dom/xbl/nsXBLContentSink.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/ArrayUtils.h"
michael@0 7
michael@0 8 #include "nsXBLContentSink.h"
michael@0 9 #include "nsIDocument.h"
michael@0 10 #include "nsBindingManager.h"
michael@0 11 #include "nsIDOMNode.h"
michael@0 12 #include "nsGkAtoms.h"
michael@0 13 #include "nsNameSpaceManager.h"
michael@0 14 #include "nsIURI.h"
michael@0 15 #include "nsTextFragment.h"
michael@0 16 #ifdef MOZ_XUL
michael@0 17 #include "nsXULElement.h"
michael@0 18 #endif
michael@0 19 #include "nsXBLProtoImplProperty.h"
michael@0 20 #include "nsXBLProtoImplMethod.h"
michael@0 21 #include "nsXBLProtoImplField.h"
michael@0 22 #include "nsXBLPrototypeBinding.h"
michael@0 23 #include "nsContentUtils.h"
michael@0 24 #include "nsIConsoleService.h"
michael@0 25 #include "nsIScriptError.h"
michael@0 26 #include "nsNodeInfoManager.h"
michael@0 27 #include "nsINodeInfo.h"
michael@0 28 #include "nsIPrincipal.h"
michael@0 29 #include "mozilla/dom/Element.h"
michael@0 30
michael@0 31 using namespace mozilla;
michael@0 32 using namespace mozilla::dom;
michael@0 33
michael@0 34 nsresult
michael@0 35 NS_NewXBLContentSink(nsIXMLContentSink** aResult,
michael@0 36 nsIDocument* aDoc,
michael@0 37 nsIURI* aURI,
michael@0 38 nsISupports* aContainer)
michael@0 39 {
michael@0 40 NS_ENSURE_ARG_POINTER(aResult);
michael@0 41
michael@0 42 nsXBLContentSink* it = new nsXBLContentSink();
michael@0 43 NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
michael@0 44
michael@0 45 nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
michael@0 46 nsresult rv = it->Init(aDoc, aURI, aContainer);
michael@0 47 NS_ENSURE_SUCCESS(rv, rv);
michael@0 48
michael@0 49 return CallQueryInterface(it, aResult);
michael@0 50 }
michael@0 51
michael@0 52 nsXBLContentSink::nsXBLContentSink()
michael@0 53 : mState(eXBL_InDocument),
michael@0 54 mSecondaryState(eXBL_None),
michael@0 55 mDocInfo(nullptr),
michael@0 56 mIsChromeOrResource(false),
michael@0 57 mFoundFirstBinding(false),
michael@0 58 mBinding(nullptr),
michael@0 59 mHandler(nullptr),
michael@0 60 mImplementation(nullptr),
michael@0 61 mImplMember(nullptr),
michael@0 62 mImplField(nullptr),
michael@0 63 mProperty(nullptr),
michael@0 64 mMethod(nullptr),
michael@0 65 mField(nullptr)
michael@0 66 {
michael@0 67 mPrettyPrintXML = false;
michael@0 68 }
michael@0 69
michael@0 70 nsXBLContentSink::~nsXBLContentSink()
michael@0 71 {
michael@0 72 }
michael@0 73
michael@0 74 nsresult
michael@0 75 nsXBLContentSink::Init(nsIDocument* aDoc,
michael@0 76 nsIURI* aURI,
michael@0 77 nsISupports* aContainer)
michael@0 78 {
michael@0 79 nsresult rv;
michael@0 80 rv = nsXMLContentSink::Init(aDoc, aURI, aContainer, nullptr);
michael@0 81 return rv;
michael@0 82 }
michael@0 83
michael@0 84 void
michael@0 85 nsXBLContentSink::MaybeStartLayout(bool aIgnorePendingSheets)
michael@0 86 {
michael@0 87 return;
michael@0 88 }
michael@0 89
michael@0 90 nsresult
michael@0 91 nsXBLContentSink::FlushText(bool aReleaseTextNode)
michael@0 92 {
michael@0 93 if (mTextLength != 0) {
michael@0 94 const nsASingleFragmentString& text = Substring(mText, mText+mTextLength);
michael@0 95 if (mState == eXBL_InHandlers) {
michael@0 96 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 97 // Get the text and add it to the event handler.
michael@0 98 if (mSecondaryState == eXBL_InHandler)
michael@0 99 mHandler->AppendHandlerText(text);
michael@0 100 mTextLength = 0;
michael@0 101 return NS_OK;
michael@0 102 }
michael@0 103 else if (mState == eXBL_InImplementation) {
michael@0 104 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 105 if (mSecondaryState == eXBL_InConstructor ||
michael@0 106 mSecondaryState == eXBL_InDestructor) {
michael@0 107 // Construct a method for the constructor/destructor.
michael@0 108 nsXBLProtoImplMethod* method;
michael@0 109 if (mSecondaryState == eXBL_InConstructor)
michael@0 110 method = mBinding->GetConstructor();
michael@0 111 else
michael@0 112 method = mBinding->GetDestructor();
michael@0 113
michael@0 114 // Get the text and add it to the constructor/destructor.
michael@0 115 method->AppendBodyText(text);
michael@0 116 }
michael@0 117 else if (mSecondaryState == eXBL_InGetter ||
michael@0 118 mSecondaryState == eXBL_InSetter) {
michael@0 119 // Get the text and add it to the getter/setter
michael@0 120 if (mSecondaryState == eXBL_InGetter)
michael@0 121 mProperty->AppendGetterText(text);
michael@0 122 else
michael@0 123 mProperty->AppendSetterText(text);
michael@0 124 }
michael@0 125 else if (mSecondaryState == eXBL_InBody) {
michael@0 126 // Get the text and add it to the method
michael@0 127 if (mMethod)
michael@0 128 mMethod->AppendBodyText(text);
michael@0 129 }
michael@0 130 else if (mSecondaryState == eXBL_InField) {
michael@0 131 // Get the text and add it to the method
michael@0 132 if (mField)
michael@0 133 mField->AppendFieldText(text);
michael@0 134 }
michael@0 135 mTextLength = 0;
michael@0 136 return NS_OK;
michael@0 137 }
michael@0 138
michael@0 139 nsIContent* content = GetCurrentContent();
michael@0 140 if (content &&
michael@0 141 (content->NodeInfo()->NamespaceEquals(kNameSpaceID_XBL) ||
michael@0 142 (content->NodeInfo()->NamespaceEquals(kNameSpaceID_XUL) &&
michael@0 143 content->Tag() != nsGkAtoms::label &&
michael@0 144 content->Tag() != nsGkAtoms::description))) {
michael@0 145
michael@0 146 bool isWS = true;
michael@0 147 if (mTextLength > 0) {
michael@0 148 const char16_t* cp = mText;
michael@0 149 const char16_t* end = mText + mTextLength;
michael@0 150 while (cp < end) {
michael@0 151 char16_t ch = *cp++;
michael@0 152 if (!dom::IsSpaceCharacter(ch)) {
michael@0 153 isWS = false;
michael@0 154 break;
michael@0 155 }
michael@0 156 }
michael@0 157 }
michael@0 158
michael@0 159 if (isWS && mTextLength > 0) {
michael@0 160 mTextLength = 0;
michael@0 161 // Make sure to drop the textnode, if any
michael@0 162 return nsXMLContentSink::FlushText(aReleaseTextNode);
michael@0 163 }
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 return nsXMLContentSink::FlushText(aReleaseTextNode);
michael@0 168 }
michael@0 169
michael@0 170 NS_IMETHODIMP
michael@0 171 nsXBLContentSink::ReportError(const char16_t* aErrorText,
michael@0 172 const char16_t* aSourceText,
michael@0 173 nsIScriptError *aError,
michael@0 174 bool *_retval)
michael@0 175 {
michael@0 176 NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!");
michael@0 177
michael@0 178 // XXX FIXME This function overrides and calls on
michael@0 179 // nsXMLContentSink::ReportError, and probably should die. See bug 347826.
michael@0 180
michael@0 181 // XXX We should make sure the binding has no effect, but that it also
michael@0 182 // gets destroyed properly without leaking. Perhaps we should even
michael@0 183 // ensure that the content that was bound is displayed with no
michael@0 184 // binding.
michael@0 185
michael@0 186 #ifdef DEBUG
michael@0 187 // Report the error to stderr.
michael@0 188 fprintf(stderr,
michael@0 189 "\n%s\n%s\n\n",
michael@0 190 NS_LossyConvertUTF16toASCII(aErrorText).get(),
michael@0 191 NS_LossyConvertUTF16toASCII(aSourceText).get());
michael@0 192 #endif
michael@0 193
michael@0 194 // Most of what this does won't be too useful, but whatever...
michael@0 195 // nsXMLContentSink::ReportError will handle the console logging.
michael@0 196 return nsXMLContentSink::ReportError(aErrorText,
michael@0 197 aSourceText,
michael@0 198 aError,
michael@0 199 _retval);
michael@0 200 }
michael@0 201
michael@0 202 nsresult
michael@0 203 nsXBLContentSink::ReportUnexpectedElement(nsIAtom* aElementName,
michael@0 204 uint32_t aLineNumber)
michael@0 205 {
michael@0 206 // XXX we should really somehow stop the parse and drop the binding
michael@0 207 // instead of just letting the XML sink build the content model like
michael@0 208 // we do...
michael@0 209 mState = eXBL_Error;
michael@0 210 nsAutoString elementName;
michael@0 211 aElementName->ToString(elementName);
michael@0 212
michael@0 213 const char16_t* params[] = { elementName.get() };
michael@0 214
michael@0 215 return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
michael@0 216 NS_LITERAL_CSTRING("XBL Content Sink"),
michael@0 217 mDocument,
michael@0 218 nsContentUtils::eXBL_PROPERTIES,
michael@0 219 "UnexpectedElement",
michael@0 220 params, ArrayLength(params),
michael@0 221 nullptr,
michael@0 222 EmptyString() /* source line */,
michael@0 223 aLineNumber);
michael@0 224 }
michael@0 225
michael@0 226 void
michael@0 227 nsXBLContentSink::AddMember(nsXBLProtoImplMember* aMember)
michael@0 228 {
michael@0 229 // Add this member to our chain.
michael@0 230 if (mImplMember)
michael@0 231 mImplMember->SetNext(aMember); // Already have a chain. Just append to the end.
michael@0 232 else
michael@0 233 mImplementation->SetMemberList(aMember); // We're the first member in the chain.
michael@0 234
michael@0 235 mImplMember = aMember; // Adjust our pointer to point to the new last member in the chain.
michael@0 236 }
michael@0 237
michael@0 238 void
michael@0 239 nsXBLContentSink::AddField(nsXBLProtoImplField* aField)
michael@0 240 {
michael@0 241 // Add this field to our chain.
michael@0 242 if (mImplField)
michael@0 243 mImplField->SetNext(aField); // Already have a chain. Just append to the end.
michael@0 244 else
michael@0 245 mImplementation->SetFieldList(aField); // We're the first member in the chain.
michael@0 246
michael@0 247 mImplField = aField; // Adjust our pointer to point to the new last field in the chain.
michael@0 248 }
michael@0 249
michael@0 250 NS_IMETHODIMP
michael@0 251 nsXBLContentSink::HandleStartElement(const char16_t *aName,
michael@0 252 const char16_t **aAtts,
michael@0 253 uint32_t aAttsCount,
michael@0 254 int32_t aIndex,
michael@0 255 uint32_t aLineNumber)
michael@0 256 {
michael@0 257 nsresult rv = nsXMLContentSink::HandleStartElement(aName,aAtts,aAttsCount,aIndex,aLineNumber);
michael@0 258 if (NS_FAILED(rv))
michael@0 259 return rv;
michael@0 260
michael@0 261 if (mState == eXBL_InBinding && !mBinding) {
michael@0 262 rv = ConstructBinding(aLineNumber);
michael@0 263 if (NS_FAILED(rv))
michael@0 264 return rv;
michael@0 265
michael@0 266 // mBinding may still be null, if the binding had no id. If so,
michael@0 267 // we'll deal with that later in the sink.
michael@0 268 }
michael@0 269
michael@0 270 return rv;
michael@0 271 }
michael@0 272
michael@0 273 NS_IMETHODIMP
michael@0 274 nsXBLContentSink::HandleEndElement(const char16_t *aName)
michael@0 275 {
michael@0 276 FlushText();
michael@0 277
michael@0 278 if (mState != eXBL_InDocument) {
michael@0 279 int32_t nameSpaceID;
michael@0 280 nsCOMPtr<nsIAtom> prefix, localName;
michael@0 281 nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
michael@0 282 getter_AddRefs(localName), &nameSpaceID);
michael@0 283
michael@0 284 if (nameSpaceID == kNameSpaceID_XBL) {
michael@0 285 if (mState == eXBL_Error) {
michael@0 286 // Check whether we've opened this tag before; we may not have if
michael@0 287 // it was a real XBL tag before the error occurred.
michael@0 288 if (!GetCurrentContent()->NodeInfo()->Equals(localName,
michael@0 289 nameSpaceID)) {
michael@0 290 // OK, this tag was never opened as far as the XML sink is
michael@0 291 // concerned. Just drop the HandleEndElement
michael@0 292 return NS_OK;
michael@0 293 }
michael@0 294 }
michael@0 295 else if (mState == eXBL_InHandlers) {
michael@0 296 if (localName == nsGkAtoms::handlers) {
michael@0 297 mState = eXBL_InBinding;
michael@0 298 mHandler = nullptr;
michael@0 299 }
michael@0 300 else if (localName == nsGkAtoms::handler)
michael@0 301 mSecondaryState = eXBL_None;
michael@0 302 return NS_OK;
michael@0 303 }
michael@0 304 else if (mState == eXBL_InResources) {
michael@0 305 if (localName == nsGkAtoms::resources)
michael@0 306 mState = eXBL_InBinding;
michael@0 307 return NS_OK;
michael@0 308 }
michael@0 309 else if (mState == eXBL_InImplementation) {
michael@0 310 if (localName == nsGkAtoms::implementation)
michael@0 311 mState = eXBL_InBinding;
michael@0 312 else if (localName == nsGkAtoms::property) {
michael@0 313 mSecondaryState = eXBL_None;
michael@0 314 mProperty = nullptr;
michael@0 315 }
michael@0 316 else if (localName == nsGkAtoms::method) {
michael@0 317 mSecondaryState = eXBL_None;
michael@0 318 mMethod = nullptr;
michael@0 319 }
michael@0 320 else if (localName == nsGkAtoms::field) {
michael@0 321 mSecondaryState = eXBL_None;
michael@0 322 mField = nullptr;
michael@0 323 }
michael@0 324 else if (localName == nsGkAtoms::constructor ||
michael@0 325 localName == nsGkAtoms::destructor)
michael@0 326 mSecondaryState = eXBL_None;
michael@0 327 else if (localName == nsGkAtoms::getter ||
michael@0 328 localName == nsGkAtoms::setter)
michael@0 329 mSecondaryState = eXBL_InProperty;
michael@0 330 else if (localName == nsGkAtoms::parameter ||
michael@0 331 localName == nsGkAtoms::body)
michael@0 332 mSecondaryState = eXBL_InMethod;
michael@0 333 return NS_OK;
michael@0 334 }
michael@0 335 else if (mState == eXBL_InBindings &&
michael@0 336 localName == nsGkAtoms::bindings) {
michael@0 337 mState = eXBL_InDocument;
michael@0 338 }
michael@0 339
michael@0 340 nsresult rv = nsXMLContentSink::HandleEndElement(aName);
michael@0 341 if (NS_FAILED(rv))
michael@0 342 return rv;
michael@0 343
michael@0 344 if (mState == eXBL_InBinding && localName == nsGkAtoms::binding) {
michael@0 345 mState = eXBL_InBindings;
michael@0 346 if (mBinding) { // See comment in HandleStartElement()
michael@0 347 mBinding->Initialize();
michael@0 348 mBinding = nullptr; // Clear our current binding ref.
michael@0 349 }
michael@0 350 }
michael@0 351
michael@0 352 return NS_OK;
michael@0 353 }
michael@0 354 }
michael@0 355
michael@0 356 return nsXMLContentSink::HandleEndElement(aName);
michael@0 357 }
michael@0 358
michael@0 359 NS_IMETHODIMP
michael@0 360 nsXBLContentSink::HandleCDataSection(const char16_t *aData,
michael@0 361 uint32_t aLength)
michael@0 362 {
michael@0 363 if (mState == eXBL_InHandlers || mState == eXBL_InImplementation)
michael@0 364 return AddText(aData, aLength);
michael@0 365 return nsXMLContentSink::HandleCDataSection(aData, aLength);
michael@0 366 }
michael@0 367
michael@0 368 #define ENSURE_XBL_STATE(_cond) \
michael@0 369 PR_BEGIN_MACRO \
michael@0 370 if (!(_cond)) { ReportUnexpectedElement(aTagName, aLineNumber); return true; } \
michael@0 371 PR_END_MACRO
michael@0 372
michael@0 373 bool
michael@0 374 nsXBLContentSink::OnOpenContainer(const char16_t **aAtts,
michael@0 375 uint32_t aAttsCount,
michael@0 376 int32_t aNameSpaceID,
michael@0 377 nsIAtom* aTagName,
michael@0 378 uint32_t aLineNumber)
michael@0 379 {
michael@0 380 if (mState == eXBL_Error) {
michael@0 381 return true;
michael@0 382 }
michael@0 383
michael@0 384 if (aNameSpaceID != kNameSpaceID_XBL) {
michael@0 385 // Construct non-XBL nodes
michael@0 386 return true;
michael@0 387 }
michael@0 388
michael@0 389 bool ret = true;
michael@0 390 if (aTagName == nsGkAtoms::bindings) {
michael@0 391 ENSURE_XBL_STATE(mState == eXBL_InDocument);
michael@0 392
michael@0 393 NS_ASSERTION(mDocument, "Must have a document!");
michael@0 394 nsRefPtr<nsXBLDocumentInfo> info = new nsXBLDocumentInfo(mDocument);
michael@0 395
michael@0 396 // We keep a weak ref. We're creating a cycle between doc/binding manager/doc info.
michael@0 397 mDocInfo = info;
michael@0 398
michael@0 399 if (!mDocInfo) {
michael@0 400 mState = eXBL_Error;
michael@0 401 return true;
michael@0 402 }
michael@0 403
michael@0 404 mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
michael@0 405
michael@0 406 nsIURI *uri = mDocument->GetDocumentURI();
michael@0 407
michael@0 408 bool isChrome = false;
michael@0 409 bool isRes = false;
michael@0 410
michael@0 411 uri->SchemeIs("chrome", &isChrome);
michael@0 412 uri->SchemeIs("resource", &isRes);
michael@0 413 mIsChromeOrResource = isChrome || isRes;
michael@0 414
michael@0 415 mState = eXBL_InBindings;
michael@0 416 }
michael@0 417 else if (aTagName == nsGkAtoms::binding) {
michael@0 418 ENSURE_XBL_STATE(mState == eXBL_InBindings);
michael@0 419 mState = eXBL_InBinding;
michael@0 420 }
michael@0 421 else if (aTagName == nsGkAtoms::handlers) {
michael@0 422 ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
michael@0 423 mState = eXBL_InHandlers;
michael@0 424 ret = false;
michael@0 425 }
michael@0 426 else if (aTagName == nsGkAtoms::handler) {
michael@0 427 ENSURE_XBL_STATE(mState == eXBL_InHandlers);
michael@0 428 mSecondaryState = eXBL_InHandler;
michael@0 429 ConstructHandler(aAtts, aLineNumber);
michael@0 430 ret = false;
michael@0 431 }
michael@0 432 else if (aTagName == nsGkAtoms::resources) {
michael@0 433 ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
michael@0 434 mState = eXBL_InResources;
michael@0 435 // Note that this mState will cause us to return false, so no need
michael@0 436 // to set ret to false.
michael@0 437 }
michael@0 438 else if (aTagName == nsGkAtoms::stylesheet || aTagName == nsGkAtoms::image) {
michael@0 439 ENSURE_XBL_STATE(mState == eXBL_InResources);
michael@0 440 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 441 ConstructResource(aAtts, aTagName);
michael@0 442 }
michael@0 443 else if (aTagName == nsGkAtoms::implementation) {
michael@0 444 ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
michael@0 445 mState = eXBL_InImplementation;
michael@0 446 ConstructImplementation(aAtts);
michael@0 447 // Note that this mState will cause us to return false, so no need
michael@0 448 // to set ret to false.
michael@0 449 }
michael@0 450 else if (aTagName == nsGkAtoms::constructor) {
michael@0 451 ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
michael@0 452 mSecondaryState == eXBL_None);
michael@0 453 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 454
michael@0 455 mSecondaryState = eXBL_InConstructor;
michael@0 456 nsAutoString name;
michael@0 457 if (!mCurrentBindingID.IsEmpty()) {
michael@0 458 name.Assign(mCurrentBindingID);
michael@0 459 name.AppendLiteral("_XBL_Constructor");
michael@0 460 } else {
michael@0 461 name.AppendLiteral("XBL_Constructor");
michael@0 462 }
michael@0 463 nsXBLProtoImplAnonymousMethod* newMethod =
michael@0 464 new nsXBLProtoImplAnonymousMethod(name.get());
michael@0 465 if (newMethod) {
michael@0 466 newMethod->SetLineNumber(aLineNumber);
michael@0 467 mBinding->SetConstructor(newMethod);
michael@0 468 AddMember(newMethod);
michael@0 469 }
michael@0 470 }
michael@0 471 else if (aTagName == nsGkAtoms::destructor) {
michael@0 472 ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
michael@0 473 mSecondaryState == eXBL_None);
michael@0 474 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 475 mSecondaryState = eXBL_InDestructor;
michael@0 476 nsAutoString name;
michael@0 477 if (!mCurrentBindingID.IsEmpty()) {
michael@0 478 name.Assign(mCurrentBindingID);
michael@0 479 name.AppendLiteral("_XBL_Destructor");
michael@0 480 } else {
michael@0 481 name.AppendLiteral("XBL_Destructor");
michael@0 482 }
michael@0 483 nsXBLProtoImplAnonymousMethod* newMethod =
michael@0 484 new nsXBLProtoImplAnonymousMethod(name.get());
michael@0 485 if (newMethod) {
michael@0 486 newMethod->SetLineNumber(aLineNumber);
michael@0 487 mBinding->SetDestructor(newMethod);
michael@0 488 AddMember(newMethod);
michael@0 489 }
michael@0 490 }
michael@0 491 else if (aTagName == nsGkAtoms::field) {
michael@0 492 ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
michael@0 493 mSecondaryState == eXBL_None);
michael@0 494 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 495 mSecondaryState = eXBL_InField;
michael@0 496 ConstructField(aAtts, aLineNumber);
michael@0 497 }
michael@0 498 else if (aTagName == nsGkAtoms::property) {
michael@0 499 ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
michael@0 500 mSecondaryState == eXBL_None);
michael@0 501 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 502 mSecondaryState = eXBL_InProperty;
michael@0 503 ConstructProperty(aAtts, aLineNumber);
michael@0 504 }
michael@0 505 else if (aTagName == nsGkAtoms::getter) {
michael@0 506 ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
michael@0 507 NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
michael@0 508 mProperty->SetGetterLineNumber(aLineNumber);
michael@0 509 mSecondaryState = eXBL_InGetter;
michael@0 510 }
michael@0 511 else if (aTagName == nsGkAtoms::setter) {
michael@0 512 ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
michael@0 513 NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
michael@0 514 mProperty->SetSetterLineNumber(aLineNumber);
michael@0 515 mSecondaryState = eXBL_InSetter;
michael@0 516 }
michael@0 517 else if (aTagName == nsGkAtoms::method) {
michael@0 518 ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
michael@0 519 mSecondaryState == eXBL_None);
michael@0 520 NS_ASSERTION(mBinding, "Must have binding here");
michael@0 521 mSecondaryState = eXBL_InMethod;
michael@0 522 ConstructMethod(aAtts);
michael@0 523 }
michael@0 524 else if (aTagName == nsGkAtoms::parameter) {
michael@0 525 ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
michael@0 526 NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
michael@0 527 ConstructParameter(aAtts);
michael@0 528 }
michael@0 529 else if (aTagName == nsGkAtoms::body) {
michael@0 530 ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
michael@0 531 NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
michael@0 532 // stash away the line number
michael@0 533 mMethod->SetLineNumber(aLineNumber);
michael@0 534 mSecondaryState = eXBL_InBody;
michael@0 535 }
michael@0 536
michael@0 537 return ret && mState != eXBL_InResources && mState != eXBL_InImplementation;
michael@0 538 }
michael@0 539
michael@0 540 #undef ENSURE_XBL_STATE
michael@0 541
michael@0 542 nsresult
michael@0 543 nsXBLContentSink::ConstructBinding(uint32_t aLineNumber)
michael@0 544 {
michael@0 545 nsCOMPtr<nsIContent> binding = GetCurrentContent();
michael@0 546 binding->GetAttr(kNameSpaceID_None, nsGkAtoms::id, mCurrentBindingID);
michael@0 547 NS_ConvertUTF16toUTF8 cid(mCurrentBindingID);
michael@0 548
michael@0 549 nsresult rv = NS_OK;
michael@0 550
michael@0 551 // Don't create a binding with no id. nsXBLPrototypeBinding::Read also
michael@0 552 // performs this check.
michael@0 553 if (!cid.IsEmpty()) {
michael@0 554 mBinding = new nsXBLPrototypeBinding();
michael@0 555 if (!mBinding)
michael@0 556 return NS_ERROR_OUT_OF_MEMORY;
michael@0 557
michael@0 558 rv = mBinding->Init(cid, mDocInfo, binding, !mFoundFirstBinding);
michael@0 559 if (NS_SUCCEEDED(rv) &&
michael@0 560 NS_SUCCEEDED(mDocInfo->SetPrototypeBinding(cid, mBinding))) {
michael@0 561 if (!mFoundFirstBinding) {
michael@0 562 mFoundFirstBinding = true;
michael@0 563 mDocInfo->SetFirstPrototypeBinding(mBinding);
michael@0 564 }
michael@0 565 binding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::id, false);
michael@0 566 } else {
michael@0 567 delete mBinding;
michael@0 568 mBinding = nullptr;
michael@0 569 }
michael@0 570 } else {
michael@0 571 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
michael@0 572 NS_LITERAL_CSTRING("XBL Content Sink"), nullptr,
michael@0 573 nsContentUtils::eXBL_PROPERTIES,
michael@0 574 "MissingIdAttr", nullptr, 0,
michael@0 575 mDocumentURI,
michael@0 576 EmptyString(),
michael@0 577 aLineNumber);
michael@0 578 }
michael@0 579
michael@0 580 return rv;
michael@0 581 }
michael@0 582
michael@0 583 static bool
michael@0 584 FindValue(const char16_t **aAtts, nsIAtom *aAtom, const char16_t **aResult)
michael@0 585 {
michael@0 586 nsCOMPtr<nsIAtom> prefix, localName;
michael@0 587 for (; *aAtts; aAtts += 2) {
michael@0 588 int32_t nameSpaceID;
michael@0 589 nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
michael@0 590 getter_AddRefs(localName), &nameSpaceID);
michael@0 591
michael@0 592 // Is this attribute one of the ones we care about?
michael@0 593 if (nameSpaceID == kNameSpaceID_None && localName == aAtom) {
michael@0 594 *aResult = aAtts[1];
michael@0 595
michael@0 596 return true;
michael@0 597 }
michael@0 598 }
michael@0 599
michael@0 600 return false;
michael@0 601 }
michael@0 602
michael@0 603 void
michael@0 604 nsXBLContentSink::ConstructHandler(const char16_t **aAtts, uint32_t aLineNumber)
michael@0 605 {
michael@0 606 const char16_t* event = nullptr;
michael@0 607 const char16_t* modifiers = nullptr;
michael@0 608 const char16_t* button = nullptr;
michael@0 609 const char16_t* clickcount = nullptr;
michael@0 610 const char16_t* keycode = nullptr;
michael@0 611 const char16_t* charcode = nullptr;
michael@0 612 const char16_t* phase = nullptr;
michael@0 613 const char16_t* command = nullptr;
michael@0 614 const char16_t* action = nullptr;
michael@0 615 const char16_t* group = nullptr;
michael@0 616 const char16_t* preventdefault = nullptr;
michael@0 617 const char16_t* allowuntrusted = nullptr;
michael@0 618
michael@0 619 nsCOMPtr<nsIAtom> prefix, localName;
michael@0 620 for (; *aAtts; aAtts += 2) {
michael@0 621 int32_t nameSpaceID;
michael@0 622 nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
michael@0 623 getter_AddRefs(localName), &nameSpaceID);
michael@0 624
michael@0 625 if (nameSpaceID != kNameSpaceID_None) {
michael@0 626 continue;
michael@0 627 }
michael@0 628
michael@0 629 // Is this attribute one of the ones we care about?
michael@0 630 if (localName == nsGkAtoms::event)
michael@0 631 event = aAtts[1];
michael@0 632 else if (localName == nsGkAtoms::modifiers)
michael@0 633 modifiers = aAtts[1];
michael@0 634 else if (localName == nsGkAtoms::button)
michael@0 635 button = aAtts[1];
michael@0 636 else if (localName == nsGkAtoms::clickcount)
michael@0 637 clickcount = aAtts[1];
michael@0 638 else if (localName == nsGkAtoms::keycode)
michael@0 639 keycode = aAtts[1];
michael@0 640 else if (localName == nsGkAtoms::key || localName == nsGkAtoms::charcode)
michael@0 641 charcode = aAtts[1];
michael@0 642 else if (localName == nsGkAtoms::phase)
michael@0 643 phase = aAtts[1];
michael@0 644 else if (localName == nsGkAtoms::command)
michael@0 645 command = aAtts[1];
michael@0 646 else if (localName == nsGkAtoms::action)
michael@0 647 action = aAtts[1];
michael@0 648 else if (localName == nsGkAtoms::group)
michael@0 649 group = aAtts[1];
michael@0 650 else if (localName == nsGkAtoms::preventdefault)
michael@0 651 preventdefault = aAtts[1];
michael@0 652 else if (localName == nsGkAtoms::allowuntrusted)
michael@0 653 allowuntrusted = aAtts[1];
michael@0 654 }
michael@0 655
michael@0 656 if (command && !mIsChromeOrResource) {
michael@0 657 // Make sure the XBL doc is chrome or resource if we have a command
michael@0 658 // shorthand syntax.
michael@0 659 mState = eXBL_Error;
michael@0 660 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
michael@0 661 NS_LITERAL_CSTRING("XBL Content Sink"),
michael@0 662 mDocument,
michael@0 663 nsContentUtils::eXBL_PROPERTIES,
michael@0 664 "CommandNotInChrome", nullptr, 0,
michael@0 665 nullptr,
michael@0 666 EmptyString() /* source line */,
michael@0 667 aLineNumber);
michael@0 668 return; // Don't even make this handler.
michael@0 669 }
michael@0 670
michael@0 671 // All of our pointers are now filled in. Construct our handler with all of
michael@0 672 // these parameters.
michael@0 673 nsXBLPrototypeHandler* newHandler;
michael@0 674 newHandler = new nsXBLPrototypeHandler(event, phase, action, command,
michael@0 675 keycode, charcode, modifiers, button,
michael@0 676 clickcount, group, preventdefault,
michael@0 677 allowuntrusted, mBinding, aLineNumber);
michael@0 678
michael@0 679 if (newHandler) {
michael@0 680 // Add this handler to our chain of handlers.
michael@0 681 if (mHandler) {
michael@0 682 // Already have a chain. Just append to the end.
michael@0 683 mHandler->SetNextHandler(newHandler);
michael@0 684 }
michael@0 685 else {
michael@0 686 // We're the first handler in the chain.
michael@0 687 mBinding->SetPrototypeHandlers(newHandler);
michael@0 688 }
michael@0 689 // Adjust our mHandler pointer to point to the new last handler in the
michael@0 690 // chain.
michael@0 691 mHandler = newHandler;
michael@0 692 } else {
michael@0 693 mState = eXBL_Error;
michael@0 694 }
michael@0 695 }
michael@0 696
michael@0 697 void
michael@0 698 nsXBLContentSink::ConstructResource(const char16_t **aAtts,
michael@0 699 nsIAtom* aResourceType)
michael@0 700 {
michael@0 701 if (!mBinding)
michael@0 702 return;
michael@0 703
michael@0 704 const char16_t* src = nullptr;
michael@0 705 if (FindValue(aAtts, nsGkAtoms::src, &src)) {
michael@0 706 mBinding->AddResource(aResourceType, nsDependentString(src));
michael@0 707 }
michael@0 708 }
michael@0 709
michael@0 710 void
michael@0 711 nsXBLContentSink::ConstructImplementation(const char16_t **aAtts)
michael@0 712 {
michael@0 713 mImplementation = nullptr;
michael@0 714 mImplMember = nullptr;
michael@0 715 mImplField = nullptr;
michael@0 716
michael@0 717 if (!mBinding)
michael@0 718 return;
michael@0 719
michael@0 720 const char16_t* name = nullptr;
michael@0 721
michael@0 722 nsCOMPtr<nsIAtom> prefix, localName;
michael@0 723 for (; *aAtts; aAtts += 2) {
michael@0 724 int32_t nameSpaceID;
michael@0 725 nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
michael@0 726 getter_AddRefs(localName), &nameSpaceID);
michael@0 727
michael@0 728 if (nameSpaceID != kNameSpaceID_None) {
michael@0 729 continue;
michael@0 730 }
michael@0 731
michael@0 732 // Is this attribute one of the ones we care about?
michael@0 733 if (localName == nsGkAtoms::name) {
michael@0 734 name = aAtts[1];
michael@0 735 }
michael@0 736 else if (localName == nsGkAtoms::implements) {
michael@0 737 // Only allow implementation of interfaces via XBL if the principal of
michael@0 738 // our XBL document is the system principal.
michael@0 739 if (nsContentUtils::IsSystemPrincipal(mDocument->NodePrincipal())) {
michael@0 740 mBinding->ConstructInterfaceTable(nsDependentString(aAtts[1]));
michael@0 741 }
michael@0 742 }
michael@0 743 }
michael@0 744
michael@0 745 NS_NewXBLProtoImpl(mBinding, name, &mImplementation);
michael@0 746 }
michael@0 747
michael@0 748 void
michael@0 749 nsXBLContentSink::ConstructField(const char16_t **aAtts, uint32_t aLineNumber)
michael@0 750 {
michael@0 751 const char16_t* name = nullptr;
michael@0 752 const char16_t* readonly = nullptr;
michael@0 753
michael@0 754 nsCOMPtr<nsIAtom> prefix, localName;
michael@0 755 for (; *aAtts; aAtts += 2) {
michael@0 756 int32_t nameSpaceID;
michael@0 757 nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
michael@0 758 getter_AddRefs(localName), &nameSpaceID);
michael@0 759
michael@0 760 if (nameSpaceID != kNameSpaceID_None) {
michael@0 761 continue;
michael@0 762 }
michael@0 763
michael@0 764 // Is this attribute one of the ones we care about?
michael@0 765 if (localName == nsGkAtoms::name) {
michael@0 766 name = aAtts[1];
michael@0 767 }
michael@0 768 else if (localName == nsGkAtoms::readonly) {
michael@0 769 readonly = aAtts[1];
michael@0 770 }
michael@0 771 }
michael@0 772
michael@0 773 if (name) {
michael@0 774 // All of our pointers are now filled in. Construct our field with all of
michael@0 775 // these parameters.
michael@0 776 mField = new nsXBLProtoImplField(name, readonly);
michael@0 777 if (mField) {
michael@0 778 mField->SetLineNumber(aLineNumber);
michael@0 779 AddField(mField);
michael@0 780 }
michael@0 781 }
michael@0 782 }
michael@0 783
michael@0 784 void
michael@0 785 nsXBLContentSink::ConstructProperty(const char16_t **aAtts, uint32_t aLineNumber)
michael@0 786 {
michael@0 787 const char16_t* name = nullptr;
michael@0 788 const char16_t* readonly = nullptr;
michael@0 789 const char16_t* onget = nullptr;
michael@0 790 const char16_t* onset = nullptr;
michael@0 791 bool exposeToUntrustedContent = false;
michael@0 792
michael@0 793 nsCOMPtr<nsIAtom> prefix, localName;
michael@0 794 for (; *aAtts; aAtts += 2) {
michael@0 795 int32_t nameSpaceID;
michael@0 796 nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
michael@0 797 getter_AddRefs(localName), &nameSpaceID);
michael@0 798
michael@0 799 if (nameSpaceID != kNameSpaceID_None) {
michael@0 800 continue;
michael@0 801 }
michael@0 802
michael@0 803 // Is this attribute one of the ones we care about?
michael@0 804 if (localName == nsGkAtoms::name) {
michael@0 805 name = aAtts[1];
michael@0 806 }
michael@0 807 else if (localName == nsGkAtoms::readonly) {
michael@0 808 readonly = aAtts[1];
michael@0 809 }
michael@0 810 else if (localName == nsGkAtoms::onget) {
michael@0 811 onget = aAtts[1];
michael@0 812 }
michael@0 813 else if (localName == nsGkAtoms::onset) {
michael@0 814 onset = aAtts[1];
michael@0 815 }
michael@0 816 else if (localName == nsGkAtoms::exposeToUntrustedContent &&
michael@0 817 nsDependentString(aAtts[1]).EqualsLiteral("true"))
michael@0 818 {
michael@0 819 exposeToUntrustedContent = true;
michael@0 820 }
michael@0 821 }
michael@0 822
michael@0 823 if (name) {
michael@0 824 // All of our pointers are now filled in. Construct our property with all of
michael@0 825 // these parameters.
michael@0 826 mProperty = new nsXBLProtoImplProperty(name, onget, onset, readonly, aLineNumber);
michael@0 827 if (exposeToUntrustedContent) {
michael@0 828 mProperty->SetExposeToUntrustedContent(true);
michael@0 829 }
michael@0 830 AddMember(mProperty);
michael@0 831 }
michael@0 832 }
michael@0 833
michael@0 834 void
michael@0 835 nsXBLContentSink::ConstructMethod(const char16_t **aAtts)
michael@0 836 {
michael@0 837 mMethod = nullptr;
michael@0 838
michael@0 839 const char16_t* name = nullptr;
michael@0 840 const char16_t* expose = nullptr;
michael@0 841 if (FindValue(aAtts, nsGkAtoms::name, &name)) {
michael@0 842 mMethod = new nsXBLProtoImplMethod(name);
michael@0 843 if (FindValue(aAtts, nsGkAtoms::exposeToUntrustedContent, &expose) &&
michael@0 844 nsDependentString(expose).EqualsLiteral("true"))
michael@0 845 {
michael@0 846 mMethod->SetExposeToUntrustedContent(true);
michael@0 847 }
michael@0 848 }
michael@0 849
michael@0 850 if (mMethod) {
michael@0 851 AddMember(mMethod);
michael@0 852 }
michael@0 853 }
michael@0 854
michael@0 855 void
michael@0 856 nsXBLContentSink::ConstructParameter(const char16_t **aAtts)
michael@0 857 {
michael@0 858 if (!mMethod)
michael@0 859 return;
michael@0 860
michael@0 861 const char16_t* name = nullptr;
michael@0 862 if (FindValue(aAtts, nsGkAtoms::name, &name)) {
michael@0 863 mMethod->AddParameter(nsDependentString(name));
michael@0 864 }
michael@0 865 }
michael@0 866
michael@0 867 nsresult
michael@0 868 nsXBLContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount,
michael@0 869 nsINodeInfo* aNodeInfo, uint32_t aLineNumber,
michael@0 870 nsIContent** aResult, bool* aAppendContent,
michael@0 871 FromParser aFromParser)
michael@0 872 {
michael@0 873 #ifdef MOZ_XUL
michael@0 874 if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
michael@0 875 #endif
michael@0 876 return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
michael@0 877 aLineNumber, aResult,
michael@0 878 aAppendContent, aFromParser);
michael@0 879 #ifdef MOZ_XUL
michael@0 880 }
michael@0 881
michael@0 882 // Note that this needs to match the code in nsXBLPrototypeBinding::ReadContentNode.
michael@0 883
michael@0 884 *aAppendContent = true;
michael@0 885 nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement();
michael@0 886 if (!prototype)
michael@0 887 return NS_ERROR_OUT_OF_MEMORY;
michael@0 888
michael@0 889 prototype->mNodeInfo = aNodeInfo;
michael@0 890
michael@0 891 AddAttributesToXULPrototype(aAtts, aAttsCount, prototype);
michael@0 892
michael@0 893 Element* result;
michael@0 894 nsresult rv = nsXULElement::Create(prototype, mDocument, false, false, &result);
michael@0 895 *aResult = result;
michael@0 896 return rv;
michael@0 897 #endif
michael@0 898 }
michael@0 899
michael@0 900 nsresult
michael@0 901 nsXBLContentSink::AddAttributes(const char16_t** aAtts,
michael@0 902 nsIContent* aContent)
michael@0 903 {
michael@0 904 if (aContent->IsXUL())
michael@0 905 return NS_OK; // Nothing to do, since the proto already has the attrs.
michael@0 906
michael@0 907 return nsXMLContentSink::AddAttributes(aAtts, aContent);
michael@0 908 }
michael@0 909
michael@0 910 #ifdef MOZ_XUL
michael@0 911 nsresult
michael@0 912 nsXBLContentSink::AddAttributesToXULPrototype(const char16_t **aAtts,
michael@0 913 uint32_t aAttsCount,
michael@0 914 nsXULPrototypeElement* aElement)
michael@0 915 {
michael@0 916 // Add tag attributes to the element
michael@0 917 nsresult rv;
michael@0 918
michael@0 919 // Create storage for the attributes
michael@0 920 nsXULPrototypeAttribute* attrs = nullptr;
michael@0 921 if (aAttsCount > 0) {
michael@0 922 attrs = new nsXULPrototypeAttribute[aAttsCount];
michael@0 923 if (!attrs)
michael@0 924 return NS_ERROR_OUT_OF_MEMORY;
michael@0 925 }
michael@0 926
michael@0 927 aElement->mAttributes = attrs;
michael@0 928 aElement->mNumAttributes = aAttsCount;
michael@0 929
michael@0 930 // Copy the attributes into the prototype
michael@0 931 nsCOMPtr<nsIAtom> prefix, localName;
michael@0 932
michael@0 933 uint32_t i;
michael@0 934 for (i = 0; i < aAttsCount; ++i) {
michael@0 935 int32_t nameSpaceID;
michael@0 936 nsContentUtils::SplitExpatName(aAtts[i * 2], getter_AddRefs(prefix),
michael@0 937 getter_AddRefs(localName), &nameSpaceID);
michael@0 938
michael@0 939 if (nameSpaceID == kNameSpaceID_None) {
michael@0 940 attrs[i].mName.SetTo(localName);
michael@0 941 }
michael@0 942 else {
michael@0 943 nsCOMPtr<nsINodeInfo> ni;
michael@0 944 ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
michael@0 945 nsIDOMNode::ATTRIBUTE_NODE);
michael@0 946 attrs[i].mName.SetTo(ni);
michael@0 947 }
michael@0 948
michael@0 949 rv = aElement->SetAttrAt(i, nsDependentString(aAtts[i * 2 + 1]),
michael@0 950 mDocumentURI);
michael@0 951 NS_ENSURE_SUCCESS(rv, rv);
michael@0 952 }
michael@0 953
michael@0 954 return NS_OK;
michael@0 955 }
michael@0 956 #endif

mercurial