mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
e2f82674b8
Backed out changeset d68dcf2ef372 (bug 1165515) Backed out changeset 7c3b45a47811 (bug 1165515) Backed out changeset b668b617bef2 (bug 1165515) Backed out changeset d0916e1283a2 (bug 1165515) Backed out changeset ac4dc7489942 (bug 1165515) Backed out changeset e9632ce8bc65 (bug 1165515) Backed out changeset c16d215cc7e4 (bug 1165515) Backed out changeset e4d474f3c51a (bug 1165515) Backed out changeset d87680bf9f7c (bug 1165515) Backed out changeset b3c0a45ba99e (bug 1165515) Backed out changeset 9370fa197674 (bug 1165515) Backed out changeset 50970d668ca1 (bug 1165515) Backed out changeset ffa4eb6d24b9 (bug 1165515) Backed out changeset 5fcf1203cc1d (bug 1165515)
1043 lines
29 KiB
C++
1043 lines
29 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* vim: set ts=8 sts=4 et sw=4 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* An implementation for a Gecko-style content sink that knows how
|
|
* to build a content model (the "prototype" document) from XUL.
|
|
*
|
|
* For more information on XUL,
|
|
* see http://developer.mozilla.org/en/docs/XUL
|
|
*/
|
|
|
|
#include "nsXULContentSink.h"
|
|
|
|
#include "jsfriendapi.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsForwardReference.h"
|
|
#include "nsHTMLStyleSheet.h"
|
|
#include "nsIContentSink.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMEventListener.h"
|
|
#include "nsIDOMHTMLFormElement.h"
|
|
#include "nsIDOMXULDocument.h"
|
|
#include "nsIFormControl.h"
|
|
#include "mozilla/dom/NodeInfo.h"
|
|
#include "nsIScriptContext.h"
|
|
#include "nsIScriptGlobalObject.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIURL.h"
|
|
#include "nsNameSpaceManager.h"
|
|
#include "nsParserBase.h"
|
|
#include "nsViewManager.h"
|
|
#include "nsIXULDocument.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsLayoutCID.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsRDFCID.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsXULElement.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "prmem.h"
|
|
#include "nsCRT.h"
|
|
|
|
#include "nsXULPrototypeDocument.h" // XXXbe temporary
|
|
#include "mozilla/css/Loader.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsAttrName.h"
|
|
#include "nsXMLContentSink.h"
|
|
#include "nsIConsoleService.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsContentTypeParser.h"
|
|
|
|
static PRLogModuleInfo* gContentSinkLog;
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
XULContentSinkImpl::ContextStack::ContextStack()
|
|
: mTop(nullptr), mDepth(0)
|
|
{
|
|
}
|
|
|
|
XULContentSinkImpl::ContextStack::~ContextStack()
|
|
{
|
|
while (mTop) {
|
|
Entry* doomed = mTop;
|
|
mTop = mTop->mNext;
|
|
delete doomed;
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::ContextStack::Push(nsXULPrototypeNode* aNode, State aState)
|
|
{
|
|
Entry* entry = new Entry;
|
|
entry->mNode = aNode;
|
|
entry->mState = aState;
|
|
entry->mNext = mTop;
|
|
|
|
mTop = entry;
|
|
|
|
++mDepth;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::ContextStack::Pop(State* aState)
|
|
{
|
|
if (mDepth == 0)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
Entry* entry = mTop;
|
|
mTop = mTop->mNext;
|
|
--mDepth;
|
|
|
|
*aState = entry->mState;
|
|
delete entry;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
XULContentSinkImpl::ContextStack::GetTopNode(nsRefPtr<nsXULPrototypeNode>& aNode)
|
|
{
|
|
if (mDepth == 0)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
aNode = mTop->mNode;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
XULContentSinkImpl::ContextStack::GetTopChildren(nsPrototypeArray** aChildren)
|
|
{
|
|
if (mDepth == 0)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
*aChildren = &(mTop->mChildren);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
XULContentSinkImpl::ContextStack::Clear()
|
|
{
|
|
Entry *cur = mTop;
|
|
while (cur) {
|
|
// Release the root element (and its descendants).
|
|
Entry *next = cur->mNext;
|
|
delete cur;
|
|
cur = next;
|
|
}
|
|
|
|
mTop = nullptr;
|
|
mDepth = 0;
|
|
}
|
|
|
|
void
|
|
XULContentSinkImpl::ContextStack::Traverse(nsCycleCollectionTraversalCallback& aCb)
|
|
{
|
|
nsCycleCollectionTraversalCallback& cb = aCb;
|
|
for (ContextStack::Entry* tmp = mTop; tmp; tmp = tmp->mNext) {
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren)
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
XULContentSinkImpl::XULContentSinkImpl()
|
|
: mText(nullptr),
|
|
mTextLength(0),
|
|
mTextSize(0),
|
|
mConstrainSize(true),
|
|
mState(eInProlog)
|
|
{
|
|
|
|
if (! gContentSinkLog)
|
|
gContentSinkLog = PR_NewLogModule("nsXULContentSink");
|
|
}
|
|
|
|
|
|
XULContentSinkImpl::~XULContentSinkImpl()
|
|
{
|
|
// The context stack _should_ be empty, unless something has gone wrong.
|
|
NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?");
|
|
mContextStack.Clear();
|
|
|
|
free(mText);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsISupports interface
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(XULContentSinkImpl)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XULContentSinkImpl)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
|
|
tmp->mContextStack.Clear();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototype)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XULContentSinkImpl)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
|
|
tmp->mContextStack.Traverse(cb);
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototype)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULContentSinkImpl)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLContentSink)
|
|
NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)
|
|
NS_INTERFACE_MAP_ENTRY(nsIExpatSink)
|
|
NS_INTERFACE_MAP_ENTRY(nsIContentSink)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(XULContentSinkImpl)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(XULContentSinkImpl)
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIContentSink interface
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::WillBuildModel(nsDTDMode aDTDMode)
|
|
{
|
|
#if FIXME
|
|
if (! mParentContentSink) {
|
|
// If we're _not_ an overlay, then notify the document that
|
|
// the load is beginning.
|
|
mDocument->BeginLoad();
|
|
}
|
|
#endif
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::DidBuildModel(bool aTerminated)
|
|
{
|
|
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
|
|
if (doc) {
|
|
doc->EndLoad();
|
|
mDocument = nullptr;
|
|
}
|
|
|
|
// Drop our reference to the parser to get rid of a circular
|
|
// reference.
|
|
mParser = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::WillInterrupt(void)
|
|
{
|
|
// XXX Notify the docshell, if necessary
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::WillResume(void)
|
|
{
|
|
// XXX Notify the docshell, if necessary
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::SetParser(nsParserBase* aParser)
|
|
{
|
|
mParser = aParser;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::SetDocumentCharset(nsACString& aCharset)
|
|
{
|
|
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
|
|
if (doc) {
|
|
doc->SetDocumentCharacterSet(aCharset);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsISupports *
|
|
XULContentSinkImpl::GetTarget()
|
|
{
|
|
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
|
|
return doc;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
XULContentSinkImpl::Init(nsIDocument* aDocument,
|
|
nsXULPrototypeDocument* aPrototype)
|
|
{
|
|
NS_PRECONDITION(aDocument != nullptr, "null ptr");
|
|
if (! aDocument)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult rv;
|
|
|
|
mDocument = do_GetWeakReference(aDocument);
|
|
mPrototype = aPrototype;
|
|
|
|
mDocumentURL = mPrototype->GetURI();
|
|
|
|
// XXX this presumes HTTP header info is already set in document
|
|
// XXX if it isn't we need to set it here...
|
|
// XXXbz not like GetHeaderData on the proto doc _does_ anything....
|
|
nsAutoString preferredStyle;
|
|
rv = mPrototype->GetHeaderData(nsGkAtoms::headerDefaultStyle,
|
|
preferredStyle);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!preferredStyle.IsEmpty()) {
|
|
aDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle,
|
|
preferredStyle);
|
|
}
|
|
|
|
// Set the right preferred style on the document's CSSLoader.
|
|
aDocument->CSSLoader()->SetPreferredSheet(preferredStyle);
|
|
|
|
mNodeInfoManager = aPrototype->GetNodeInfoManager();
|
|
if (! mNodeInfoManager)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
mState = eInProlog;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// Text buffering
|
|
//
|
|
|
|
bool
|
|
XULContentSinkImpl::IsDataInBuffer(char16_t* buffer, int32_t length)
|
|
{
|
|
for (int32_t i = 0; i < length; ++i) {
|
|
if (buffer[i] == ' ' ||
|
|
buffer[i] == '\t' ||
|
|
buffer[i] == '\n' ||
|
|
buffer[i] == '\r')
|
|
continue;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
nsresult
|
|
XULContentSinkImpl::FlushText(bool aCreateTextNode)
|
|
{
|
|
nsresult rv;
|
|
|
|
do {
|
|
// Don't do anything if there's no text to create a node from, or
|
|
// if they've told us not to create a text node
|
|
if (! mTextLength)
|
|
break;
|
|
|
|
if (! aCreateTextNode)
|
|
break;
|
|
|
|
nsRefPtr<nsXULPrototypeNode> node;
|
|
rv = mContextStack.GetTopNode(node);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
bool stripWhitespace = false;
|
|
if (node->mType == nsXULPrototypeNode::eType_Element) {
|
|
mozilla::dom::NodeInfo *nodeInfo =
|
|
static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo;
|
|
|
|
if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL))
|
|
stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) &&
|
|
!nodeInfo->Equals(nsGkAtoms::description);
|
|
}
|
|
|
|
// Don't bother if there's nothing but whitespace.
|
|
if (stripWhitespace && ! IsDataInBuffer(mText, mTextLength))
|
|
break;
|
|
|
|
// Don't bother if we're not in XUL document body
|
|
if (mState != eInDocumentElement || mContextStack.Depth() == 0)
|
|
break;
|
|
|
|
nsXULPrototypeText* text = new nsXULPrototypeText();
|
|
text->mValue.Assign(mText, mTextLength);
|
|
if (stripWhitespace)
|
|
text->mValue.Trim(" \t\n\r");
|
|
|
|
// hook it up
|
|
nsPrototypeArray* children = nullptr;
|
|
rv = mContextStack.GetTopChildren(&children);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// transfer ownership of 'text' to the children array
|
|
children->AppendElement(text);
|
|
} while (0);
|
|
|
|
// Reset our text buffer
|
|
mTextLength = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
XULContentSinkImpl::NormalizeAttributeString(const char16_t *aExpatName,
|
|
nsAttrName &aName)
|
|
{
|
|
int32_t nameSpaceID;
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix),
|
|
getter_AddRefs(localName), &nameSpaceID);
|
|
|
|
if (nameSpaceID == kNameSpaceID_None) {
|
|
aName.SetTo(localName);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsRefPtr<mozilla::dom::NodeInfo> ni;
|
|
ni = mNodeInfoManager->GetNodeInfo(localName, prefix,
|
|
nameSpaceID,
|
|
nsIDOMNode::ATTRIBUTE_NODE);
|
|
aName.SetTo(ni);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::CreateElement(mozilla::dom::NodeInfo *aNodeInfo,
|
|
nsXULPrototypeElement** aResult)
|
|
{
|
|
nsXULPrototypeElement* element = new nsXULPrototypeElement();
|
|
element->mNodeInfo = aNodeInfo;
|
|
|
|
*aResult = element;
|
|
return NS_OK;
|
|
}
|
|
|
|
/**** BEGIN NEW APIs ****/
|
|
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleStartElement(const char16_t *aName,
|
|
const char16_t **aAtts,
|
|
uint32_t aAttsCount,
|
|
uint32_t aLineNumber)
|
|
{
|
|
// XXX Hopefully the parser will flag this before we get here. If
|
|
// we're in the epilog, there should be no new elements
|
|
NS_PRECONDITION(mState != eInEpilog, "tag in XUL doc epilog");
|
|
NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount");
|
|
// Adjust aAttsCount so it's the actual number of attributes
|
|
aAttsCount /= 2;
|
|
|
|
if (mState == eInEpilog)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
if (mState != eInScript) {
|
|
FlushText();
|
|
}
|
|
|
|
int32_t nameSpaceID;
|
|
nsCOMPtr<nsIAtom> prefix, localName;
|
|
nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
|
|
getter_AddRefs(localName), &nameSpaceID);
|
|
|
|
nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
|
|
nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
|
|
nsIDOMNode::ELEMENT_NODE);
|
|
|
|
nsresult rv = NS_OK;
|
|
switch (mState) {
|
|
case eInProlog:
|
|
// We're the root document element
|
|
rv = OpenRoot(aAtts, aAttsCount, nodeInfo);
|
|
break;
|
|
|
|
case eInDocumentElement:
|
|
rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo);
|
|
break;
|
|
|
|
case eInEpilog:
|
|
case eInScript:
|
|
MOZ_LOG(gContentSinkLog, PR_LOG_WARNING,
|
|
("xul: warning: unexpected tags in epilog at line %d",
|
|
aLineNumber));
|
|
rv = NS_ERROR_UNEXPECTED; // XXX
|
|
break;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleEndElement(const char16_t *aName)
|
|
{
|
|
// Never EVER return anything but NS_OK or
|
|
// NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow
|
|
// the parser's little mind all over the planet.
|
|
nsresult rv;
|
|
|
|
nsRefPtr<nsXULPrototypeNode> node;
|
|
rv = mContextStack.GetTopNode(node);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
switch (node->mType) {
|
|
case nsXULPrototypeNode::eType_Element: {
|
|
// Flush any text _now_, so that we'll get text nodes created
|
|
// before popping the stack.
|
|
FlushText();
|
|
|
|
// Pop the context stack and do prototype hookup.
|
|
nsPrototypeArray* children = nullptr;
|
|
rv = mContextStack.GetTopChildren(&children);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsXULPrototypeElement* element =
|
|
static_cast<nsXULPrototypeElement*>(node.get());
|
|
|
|
int32_t count = children->Length();
|
|
if (count) {
|
|
element->mChildren.SetCapacity(count);
|
|
|
|
for (int32_t i = 0; i < count; ++i)
|
|
element->mChildren.AppendElement(children->ElementAt(i));
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case nsXULPrototypeNode::eType_Script: {
|
|
nsXULPrototypeScript* script =
|
|
static_cast<nsXULPrototypeScript*>(node.get());
|
|
|
|
// If given a src= attribute, we must ignore script tag content.
|
|
if (!script->mSrcURI && !script->GetScriptObject()) {
|
|
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
|
|
|
|
script->mOutOfLine = false;
|
|
if (doc)
|
|
script->Compile(mText, mTextLength, mDocumentURL,
|
|
script->mLineNo, doc);
|
|
}
|
|
|
|
FlushText(false);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
NS_ERROR("didn't expect that");
|
|
break;
|
|
}
|
|
|
|
rv = mContextStack.Pop(&mState);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted");
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (mContextStack.Depth() == 0) {
|
|
// The root element should -always- be an element, because
|
|
// it'll have been created via XULContentSinkImpl::OpenRoot().
|
|
NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element, "root is not an element");
|
|
if (node->mType != nsXULPrototypeNode::eType_Element)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
// Now that we're done parsing, set the prototype document's
|
|
// root element. This transfers ownership of the prototype
|
|
// element tree to the prototype document.
|
|
nsXULPrototypeElement* element =
|
|
static_cast<nsXULPrototypeElement*>(node.get());
|
|
|
|
mPrototype->SetRootElement(element);
|
|
mState = eInEpilog;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleComment(const char16_t *aName)
|
|
{
|
|
FlushText();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleCDataSection(const char16_t *aData, uint32_t aLength)
|
|
{
|
|
FlushText();
|
|
return AddText(aData, aLength);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset,
|
|
const nsAString & aName,
|
|
const nsAString & aSystemId,
|
|
const nsAString & aPublicId,
|
|
nsISupports* aCatalogData)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleCharacterData(const char16_t *aData,
|
|
uint32_t aLength)
|
|
{
|
|
if (aData && mState != eInProlog && mState != eInEpilog) {
|
|
return AddText(aData, aLength);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget,
|
|
const char16_t *aData)
|
|
{
|
|
FlushText();
|
|
|
|
const nsDependentString target(aTarget);
|
|
const nsDependentString data(aData);
|
|
|
|
// Note: the created nsXULPrototypePI has mRefCnt == 1
|
|
nsRefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
|
|
pi->mTarget = target;
|
|
pi->mData = data;
|
|
|
|
if (mState == eInProlog) {
|
|
// Note: passing in already addrefed pi
|
|
return mPrototype->AddProcessingInstruction(pi);
|
|
}
|
|
|
|
nsresult rv;
|
|
nsPrototypeArray* children = nullptr;
|
|
rv = mContextStack.GetTopChildren(&children);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
if (!children->AppendElement(pi)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion,
|
|
const char16_t *aEncoding,
|
|
int32_t aStandalone)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
XULContentSinkImpl::ReportError(const char16_t* aErrorText,
|
|
const char16_t* aSourceText,
|
|
nsIScriptError *aError,
|
|
bool *_retval)
|
|
{
|
|
NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
|
|
|
|
// The expat driver should report the error.
|
|
*_retval = true;
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
// make sure to empty the context stack so that
|
|
// <parsererror> could become the root element.
|
|
mContextStack.Clear();
|
|
|
|
mState = eInProlog;
|
|
|
|
// Clear any buffered-up text we have. It's enough to set the length to 0.
|
|
// The buffer itself is allocated when we're created and deleted in our
|
|
// destructor, so don't mess with it.
|
|
mTextLength = 0;
|
|
|
|
nsCOMPtr<nsIXULDocument> doc = do_QueryReferent(mDocument);
|
|
if (doc && !doc->OnDocumentParserError()) {
|
|
// The overlay was broken. Don't add a messy element to the master doc.
|
|
return NS_OK;
|
|
}
|
|
|
|
const char16_t* noAtts[] = { 0, 0 };
|
|
|
|
NS_NAMED_LITERAL_STRING(errorNs,
|
|
"http://www.mozilla.org/newlayout/xml/parsererror.xml");
|
|
|
|
nsAutoString parsererror(errorNs);
|
|
parsererror.Append((char16_t)0xFFFF);
|
|
parsererror.AppendLiteral("parsererror");
|
|
|
|
rv = HandleStartElement(parsererror.get(), noAtts, 0, 0);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsAutoString sourcetext(errorNs);
|
|
sourcetext.Append((char16_t)0xFFFF);
|
|
sourcetext.AppendLiteral("sourcetext");
|
|
|
|
rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = HandleEndElement(sourcetext.get());
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = HandleEndElement(parsererror.get());
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::OpenRoot(const char16_t** aAttributes,
|
|
const uint32_t aAttrLen,
|
|
mozilla::dom::NodeInfo *aNodeInfo)
|
|
{
|
|
NS_ASSERTION(mState == eInProlog, "how'd we get here?");
|
|
if (mState != eInProlog)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
nsresult rv;
|
|
|
|
if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
|
|
aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
|
|
MOZ_LOG(gContentSinkLog, PR_LOG_ERROR,
|
|
("xul: script tag not allowed as root content element"));
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
// Create the element
|
|
nsXULPrototypeElement* element;
|
|
rv = CreateElement(aNodeInfo, &element);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
if (PR_LOG_TEST(gContentSinkLog, PR_LOG_ERROR)) {
|
|
nsAutoString anodeC;
|
|
aNodeInfo->GetName(anodeC);
|
|
MOZ_LOG(gContentSinkLog, PR_LOG_ERROR,
|
|
("xul: unable to create element '%s' at line %d",
|
|
NS_ConvertUTF16toUTF8(anodeC).get(),
|
|
-1)); // XXX pass in line number
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Push the element onto the context stack, so that child
|
|
// containers will hook up to us as their parent.
|
|
rv = mContextStack.Push(element, mState);
|
|
if (NS_FAILED(rv)) {
|
|
element->Release();
|
|
return rv;
|
|
}
|
|
|
|
// Add the attributes
|
|
rv = AddAttributes(aAttributes, aAttrLen, element);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
mState = eInDocumentElement;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::OpenTag(const char16_t** aAttributes,
|
|
const uint32_t aAttrLen,
|
|
const uint32_t aLineNumber,
|
|
mozilla::dom::NodeInfo *aNodeInfo)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Create the element
|
|
nsXULPrototypeElement* element;
|
|
rv = CreateElement(aNodeInfo, &element);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
if (PR_LOG_TEST(gContentSinkLog, PR_LOG_ERROR)) {
|
|
nsAutoString anodeC;
|
|
aNodeInfo->GetName(anodeC);
|
|
MOZ_LOG(gContentSinkLog, PR_LOG_ERROR,
|
|
("xul: unable to create element '%s' at line %d",
|
|
NS_ConvertUTF16toUTF8(anodeC).get(),
|
|
aLineNumber));
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Link this element to its parent.
|
|
nsPrototypeArray* children = nullptr;
|
|
rv = mContextStack.GetTopChildren(&children);
|
|
if (NS_FAILED(rv)) {
|
|
delete element;
|
|
return rv;
|
|
}
|
|
|
|
// Add the attributes
|
|
rv = AddAttributes(aAttributes, aAttrLen, element);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
children->AppendElement(element);
|
|
|
|
if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
|
|
aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
|
|
// Do scripty things now
|
|
rv = OpenScript(aAttributes, aLineNumber);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
NS_ASSERTION(mState == eInScript || mState == eInDocumentElement,
|
|
"Unexpected state");
|
|
if (mState == eInScript) {
|
|
// OpenScript has pushed the nsPrototypeScriptElement onto the
|
|
// stack, so we're done.
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// Push the element onto the context stack, so that child
|
|
// containers will hook up to us as their parent.
|
|
rv = mContextStack.Push(element, mState);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
mState = eInDocumentElement;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::OpenScript(const char16_t** aAttributes,
|
|
const uint32_t aLineNumber)
|
|
{
|
|
bool isJavaScript = true;
|
|
uint32_t version = JSVERSION_LATEST;
|
|
nsresult rv;
|
|
|
|
// Look for SRC attribute and look for a LANGUAGE attribute
|
|
nsAutoString src;
|
|
while (*aAttributes) {
|
|
const nsDependentString key(aAttributes[0]);
|
|
if (key.EqualsLiteral("src")) {
|
|
src.Assign(aAttributes[1]);
|
|
} else if (key.EqualsLiteral("type")) {
|
|
nsDependentString str(aAttributes[1]);
|
|
nsContentTypeParser parser(str);
|
|
nsAutoString mimeType;
|
|
rv = parser.GetType(mimeType);
|
|
if (NS_FAILED(rv)) {
|
|
if (rv == NS_ERROR_INVALID_ARG) {
|
|
// Fail immediately rather than checking if later things
|
|
// are okay.
|
|
return NS_OK;
|
|
}
|
|
// We do want the warning here
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (nsContentUtils::IsJavascriptMIMEType(mimeType)) {
|
|
isJavaScript = true;
|
|
version = JSVERSION_LATEST;
|
|
|
|
// Get the version string, and ensure that JavaScript supports it.
|
|
nsAutoString versionName;
|
|
rv = parser.GetParameter("version", versionName);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
version = nsContentUtils::ParseJavascriptVersion(versionName);
|
|
} else if (rv != NS_ERROR_INVALID_ARG) {
|
|
return rv;
|
|
}
|
|
} else {
|
|
isJavaScript = false;
|
|
}
|
|
} else if (key.EqualsLiteral("language")) {
|
|
// Language is deprecated, and the impl in nsScriptLoader ignores the
|
|
// various version strings anyway. So we make no attempt to support
|
|
// languages other than JS for language=
|
|
nsAutoString lang(aAttributes[1]);
|
|
if (nsContentUtils::IsJavaScriptLanguage(lang)) {
|
|
isJavaScript = true;
|
|
version = JSVERSION_DEFAULT;
|
|
}
|
|
}
|
|
aAttributes += 2;
|
|
}
|
|
|
|
// Don't process scripts that aren't JavaScript.
|
|
if (!isJavaScript) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument));
|
|
nsCOMPtr<nsIScriptGlobalObject> globalObject;
|
|
if (doc)
|
|
globalObject = do_QueryInterface(doc->GetWindow());
|
|
nsRefPtr<nsXULPrototypeScript> script =
|
|
new nsXULPrototypeScript(aLineNumber, version);
|
|
|
|
// If there is a SRC attribute...
|
|
if (! src.IsEmpty()) {
|
|
// Use the SRC attribute value to load the URL
|
|
rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nullptr, mDocumentURL);
|
|
|
|
// Check if this document is allowed to load a script from this source
|
|
// NOTE: if we ever allow scripts added via the DOM to run, we need to
|
|
// add a CheckLoadURI call for that as well.
|
|
if (NS_SUCCEEDED(rv)) {
|
|
if (!mSecMan)
|
|
mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument, &rv);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = mSecMan->
|
|
CheckLoadURIWithPrincipal(doc->NodePrincipal(),
|
|
script->mSrcURI,
|
|
nsIScriptSecurityManager::ALLOW_CHROME);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// Attempt to deserialize an out-of-line script from the FastLoad
|
|
// file right away. Otherwise we'll end up reloading the script and
|
|
// corrupting the FastLoad file trying to serialize it, in the case
|
|
// where it's already there.
|
|
script->DeserializeOutOfLine(nullptr, mPrototype);
|
|
}
|
|
|
|
nsPrototypeArray* children = nullptr;
|
|
rv = mContextStack.GetTopChildren(&children);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
children->AppendElement(script);
|
|
|
|
mConstrainSize = false;
|
|
|
|
mContextStack.Push(script, mState);
|
|
mState = eInScript;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::AddAttributes(const char16_t** aAttributes,
|
|
const uint32_t aAttrLen,
|
|
nsXULPrototypeElement* aElement)
|
|
{
|
|
// Add tag attributes to the element
|
|
nsresult rv;
|
|
|
|
// Create storage for the attributes
|
|
nsXULPrototypeAttribute* attrs = nullptr;
|
|
if (aAttrLen > 0) {
|
|
attrs = new nsXULPrototypeAttribute[aAttrLen];
|
|
}
|
|
|
|
aElement->mAttributes = attrs;
|
|
aElement->mNumAttributes = aAttrLen;
|
|
|
|
// Copy the attributes into the prototype
|
|
uint32_t i;
|
|
for (i = 0; i < aAttrLen; ++i) {
|
|
rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]),
|
|
mDocumentURL);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (PR_LOG_TEST(gContentSinkLog, PR_LOG_DEBUG)) {
|
|
nsAutoString extraWhiteSpace;
|
|
int32_t cnt = mContextStack.Depth();
|
|
while (--cnt >= 0)
|
|
extraWhiteSpace.AppendLiteral(" ");
|
|
nsAutoString qnameC,valueC;
|
|
qnameC.Assign(aAttributes[0]);
|
|
valueC.Assign(aAttributes[1]);
|
|
MOZ_LOG(gContentSinkLog, PR_LOG_DEBUG,
|
|
("xul: %.5d. %s %s=%s",
|
|
-1, // XXX pass in line number
|
|
NS_ConvertUTF16toUTF8(extraWhiteSpace).get(),
|
|
NS_ConvertUTF16toUTF8(qnameC).get(),
|
|
NS_ConvertUTF16toUTF8(valueC).get()));
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
XULContentSinkImpl::AddText(const char16_t* aText,
|
|
int32_t aLength)
|
|
{
|
|
// Create buffer when we first need it
|
|
if (0 == mTextSize) {
|
|
mText = (char16_t *) malloc(sizeof(char16_t) * 4096);
|
|
if (nullptr == mText) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
mTextSize = 4096;
|
|
}
|
|
|
|
// Copy data from string into our buffer; flush buffer when it fills up
|
|
int32_t offset = 0;
|
|
while (0 != aLength) {
|
|
int32_t amount = mTextSize - mTextLength;
|
|
if (amount > aLength) {
|
|
amount = aLength;
|
|
}
|
|
if (0 == amount) {
|
|
if (mConstrainSize) {
|
|
nsresult rv = FlushText();
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
} else {
|
|
mTextSize += aLength;
|
|
mText = (char16_t *) realloc(mText, sizeof(char16_t) * mTextSize);
|
|
if (nullptr == mText) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
memcpy(&mText[mTextLength],aText + offset, sizeof(char16_t) * amount);
|
|
|
|
mTextLength += amount;
|
|
offset += amount;
|
|
aLength -= amount;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|