Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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 "nsCOMPtr.h" |
michael@0 | 9 | #include "nsIAtom.h" |
michael@0 | 10 | #include "nsIInputStream.h" |
michael@0 | 11 | #include "nsNameSpaceManager.h" |
michael@0 | 12 | #include "nsIURI.h" |
michael@0 | 13 | #include "nsIURL.h" |
michael@0 | 14 | #include "nsIChannel.h" |
michael@0 | 15 | #include "nsXPIDLString.h" |
michael@0 | 16 | #include "nsReadableUtils.h" |
michael@0 | 17 | #include "nsNetUtil.h" |
michael@0 | 18 | #include "plstr.h" |
michael@0 | 19 | #include "nsContentCreatorFunctions.h" |
michael@0 | 20 | #include "nsIDocument.h" |
michael@0 | 21 | #include "nsIXMLContentSink.h" |
michael@0 | 22 | #include "nsContentCID.h" |
michael@0 | 23 | #include "mozilla/dom/XMLDocument.h" |
michael@0 | 24 | #include "nsXBLService.h" |
michael@0 | 25 | #include "nsXBLBinding.h" |
michael@0 | 26 | #include "nsXBLPrototypeBinding.h" |
michael@0 | 27 | #include "nsXBLContentSink.h" |
michael@0 | 28 | #include "xptinfo.h" |
michael@0 | 29 | #include "nsIInterfaceInfoManager.h" |
michael@0 | 30 | #include "nsIDocumentObserver.h" |
michael@0 | 31 | #include "nsGkAtoms.h" |
michael@0 | 32 | #include "nsXBLProtoImpl.h" |
michael@0 | 33 | #include "nsCRT.h" |
michael@0 | 34 | #include "nsContentUtils.h" |
michael@0 | 35 | #include "nsTextFragment.h" |
michael@0 | 36 | #include "nsTextNode.h" |
michael@0 | 37 | #include "nsIInterfaceInfo.h" |
michael@0 | 38 | #include "nsIScriptError.h" |
michael@0 | 39 | |
michael@0 | 40 | #include "nsIStyleRuleProcessor.h" |
michael@0 | 41 | #include "nsXBLResourceLoader.h" |
michael@0 | 42 | #include "mozilla/dom/CDATASection.h" |
michael@0 | 43 | #include "mozilla/dom/Comment.h" |
michael@0 | 44 | #include "mozilla/dom/Element.h" |
michael@0 | 45 | |
michael@0 | 46 | #ifdef MOZ_XUL |
michael@0 | 47 | #include "nsXULElement.h" |
michael@0 | 48 | #endif |
michael@0 | 49 | |
michael@0 | 50 | using namespace mozilla; |
michael@0 | 51 | using namespace mozilla::dom; |
michael@0 | 52 | |
michael@0 | 53 | // Helper Classes ===================================================================== |
michael@0 | 54 | |
michael@0 | 55 | // nsXBLAttributeEntry and helpers. This class is used to efficiently handle |
michael@0 | 56 | // attribute changes in anonymous content. |
michael@0 | 57 | |
michael@0 | 58 | class nsXBLAttributeEntry { |
michael@0 | 59 | public: |
michael@0 | 60 | nsXBLAttributeEntry(nsIAtom* aSrcAtom, nsIAtom* aDstAtom, |
michael@0 | 61 | int32_t aDstNameSpace, nsIContent* aContent) |
michael@0 | 62 | : mElement(aContent), |
michael@0 | 63 | mSrcAttribute(aSrcAtom), |
michael@0 | 64 | mDstAttribute(aDstAtom), |
michael@0 | 65 | mDstNameSpace(aDstNameSpace), |
michael@0 | 66 | mNext(nullptr) { } |
michael@0 | 67 | |
michael@0 | 68 | ~nsXBLAttributeEntry() { |
michael@0 | 69 | NS_CONTENT_DELETE_LIST_MEMBER(nsXBLAttributeEntry, this, mNext); |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | nsIAtom* GetSrcAttribute() { return mSrcAttribute; } |
michael@0 | 73 | nsIAtom* GetDstAttribute() { return mDstAttribute; } |
michael@0 | 74 | int32_t GetDstNameSpace() { return mDstNameSpace; } |
michael@0 | 75 | |
michael@0 | 76 | nsIContent* GetElement() { return mElement; } |
michael@0 | 77 | |
michael@0 | 78 | nsXBLAttributeEntry* GetNext() { return mNext; } |
michael@0 | 79 | void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; } |
michael@0 | 80 | |
michael@0 | 81 | protected: |
michael@0 | 82 | nsIContent* mElement; |
michael@0 | 83 | |
michael@0 | 84 | nsCOMPtr<nsIAtom> mSrcAttribute; |
michael@0 | 85 | nsCOMPtr<nsIAtom> mDstAttribute; |
michael@0 | 86 | int32_t mDstNameSpace; |
michael@0 | 87 | nsXBLAttributeEntry* mNext; |
michael@0 | 88 | }; |
michael@0 | 89 | |
michael@0 | 90 | // ============================================================================= |
michael@0 | 91 | |
michael@0 | 92 | // Implementation ///////////////////////////////////////////////////////////////// |
michael@0 | 93 | |
michael@0 | 94 | // Constructors/Destructors |
michael@0 | 95 | nsXBLPrototypeBinding::nsXBLPrototypeBinding() |
michael@0 | 96 | : mImplementation(nullptr), |
michael@0 | 97 | mBaseBinding(nullptr), |
michael@0 | 98 | mInheritStyle(true), |
michael@0 | 99 | mCheckedBaseProto(false), |
michael@0 | 100 | mKeyHandlersRegistered(false), |
michael@0 | 101 | mChromeOnlyContent(false), |
michael@0 | 102 | mResources(nullptr), |
michael@0 | 103 | mBaseNameSpaceID(kNameSpaceID_None) |
michael@0 | 104 | { |
michael@0 | 105 | MOZ_COUNT_CTOR(nsXBLPrototypeBinding); |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | nsresult |
michael@0 | 109 | nsXBLPrototypeBinding::Init(const nsACString& aID, |
michael@0 | 110 | nsXBLDocumentInfo* aInfo, |
michael@0 | 111 | nsIContent* aElement, |
michael@0 | 112 | bool aFirstBinding) |
michael@0 | 113 | { |
michael@0 | 114 | nsresult rv = aInfo->DocumentURI()->Clone(getter_AddRefs(mBindingURI)); |
michael@0 | 115 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 116 | |
michael@0 | 117 | // The binding URI might be an immutable URI (e.g. for about: URIs). In that case, |
michael@0 | 118 | // we'll fail in SetRef below, but that doesn't matter much for now. |
michael@0 | 119 | if (aFirstBinding) { |
michael@0 | 120 | rv = mBindingURI->Clone(getter_AddRefs(mAlternateBindingURI)); |
michael@0 | 121 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 122 | } |
michael@0 | 123 | mBindingURI->SetRef(aID); |
michael@0 | 124 | |
michael@0 | 125 | mXBLDocInfoWeak = aInfo; |
michael@0 | 126 | |
michael@0 | 127 | // aElement will be null when reading from the cache, but the element will |
michael@0 | 128 | // still be set later. |
michael@0 | 129 | if (aElement) { |
michael@0 | 130 | SetBindingElement(aElement); |
michael@0 | 131 | } |
michael@0 | 132 | return NS_OK; |
michael@0 | 133 | } |
michael@0 | 134 | |
michael@0 | 135 | bool nsXBLPrototypeBinding::CompareBindingURI(nsIURI* aURI) const |
michael@0 | 136 | { |
michael@0 | 137 | bool equal = false; |
michael@0 | 138 | mBindingURI->Equals(aURI, &equal); |
michael@0 | 139 | if (!equal && mAlternateBindingURI) { |
michael@0 | 140 | mAlternateBindingURI->Equals(aURI, &equal); |
michael@0 | 141 | } |
michael@0 | 142 | return equal; |
michael@0 | 143 | } |
michael@0 | 144 | |
michael@0 | 145 | void |
michael@0 | 146 | nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const |
michael@0 | 147 | { |
michael@0 | 148 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding"); |
michael@0 | 149 | cb.NoteXPCOMChild(mBinding); |
michael@0 | 150 | if (mResources) { |
michael@0 | 151 | mResources->Traverse(cb); |
michael@0 | 152 | } |
michael@0 | 153 | ImplCycleCollectionTraverse(cb, mInterfaceTable, "proto mInterfaceTable"); |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | void |
michael@0 | 157 | nsXBLPrototypeBinding::UnlinkJSObjects() |
michael@0 | 158 | { |
michael@0 | 159 | if (mImplementation) |
michael@0 | 160 | mImplementation->UnlinkJSObjects(); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | void |
michael@0 | 164 | nsXBLPrototypeBinding::Trace(const TraceCallbacks& aCallbacks, void *aClosure) const |
michael@0 | 165 | { |
michael@0 | 166 | if (mImplementation) |
michael@0 | 167 | mImplementation->Trace(aCallbacks, aClosure); |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | void |
michael@0 | 171 | nsXBLPrototypeBinding::Initialize() |
michael@0 | 172 | { |
michael@0 | 173 | nsIContent* content = GetImmediateChild(nsGkAtoms::content); |
michael@0 | 174 | if (content) { |
michael@0 | 175 | ConstructAttributeTable(content); |
michael@0 | 176 | } |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void) |
michael@0 | 180 | { |
michael@0 | 181 | delete mImplementation; |
michael@0 | 182 | MOZ_COUNT_DTOR(nsXBLPrototypeBinding); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | void |
michael@0 | 186 | nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding) |
michael@0 | 187 | { |
michael@0 | 188 | if (mBaseBinding == aBinding) |
michael@0 | 189 | return; |
michael@0 | 190 | |
michael@0 | 191 | if (mBaseBinding) { |
michael@0 | 192 | NS_ERROR("Base XBL prototype binding is already defined!"); |
michael@0 | 193 | return; |
michael@0 | 194 | } |
michael@0 | 195 | |
michael@0 | 196 | mBaseBinding = aBinding; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | void |
michael@0 | 200 | nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement) |
michael@0 | 201 | { |
michael@0 | 202 | mBinding = aElement; |
michael@0 | 203 | if (mBinding->AttrValueIs(kNameSpaceID_None, nsGkAtoms::inheritstyle, |
michael@0 | 204 | nsGkAtoms::_false, eCaseMatters)) |
michael@0 | 205 | mInheritStyle = false; |
michael@0 | 206 | |
michael@0 | 207 | mChromeOnlyContent = mBinding->AttrValueIs(kNameSpaceID_None, |
michael@0 | 208 | nsGkAtoms::chromeOnlyContent, |
michael@0 | 209 | nsGkAtoms::_true, eCaseMatters); |
michael@0 | 210 | } |
michael@0 | 211 | |
michael@0 | 212 | bool |
michael@0 | 213 | nsXBLPrototypeBinding::GetAllowScripts() const |
michael@0 | 214 | { |
michael@0 | 215 | return mXBLDocInfoWeak->GetScriptAccess(); |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | bool |
michael@0 | 219 | nsXBLPrototypeBinding::LoadResources() |
michael@0 | 220 | { |
michael@0 | 221 | if (mResources) { |
michael@0 | 222 | bool result; |
michael@0 | 223 | mResources->LoadResources(&result); |
michael@0 | 224 | return result; |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | return true; |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | nsresult |
michael@0 | 231 | nsXBLPrototypeBinding::AddResource(nsIAtom* aResourceType, const nsAString& aSrc) |
michael@0 | 232 | { |
michael@0 | 233 | if (!mResources) { |
michael@0 | 234 | mResources = new nsXBLPrototypeResources(this); |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | mResources->AddResource(aResourceType, aSrc); |
michael@0 | 238 | return NS_OK; |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | nsresult |
michael@0 | 242 | nsXBLPrototypeBinding::FlushSkinSheets() |
michael@0 | 243 | { |
michael@0 | 244 | if (mResources) |
michael@0 | 245 | return mResources->FlushSkinSheets(); |
michael@0 | 246 | return NS_OK; |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | nsresult |
michael@0 | 250 | nsXBLPrototypeBinding::BindingAttached(nsIContent* aBoundElement) |
michael@0 | 251 | { |
michael@0 | 252 | if (mImplementation && mImplementation->CompiledMembers() && |
michael@0 | 253 | mImplementation->mConstructor) |
michael@0 | 254 | return mImplementation->mConstructor->Execute(aBoundElement); |
michael@0 | 255 | return NS_OK; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | nsresult |
michael@0 | 259 | nsXBLPrototypeBinding::BindingDetached(nsIContent* aBoundElement) |
michael@0 | 260 | { |
michael@0 | 261 | if (mImplementation && mImplementation->CompiledMembers() && |
michael@0 | 262 | mImplementation->mDestructor) |
michael@0 | 263 | return mImplementation->mDestructor->Execute(aBoundElement); |
michael@0 | 264 | return NS_OK; |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | nsXBLProtoImplAnonymousMethod* |
michael@0 | 268 | nsXBLPrototypeBinding::GetConstructor() |
michael@0 | 269 | { |
michael@0 | 270 | if (mImplementation) |
michael@0 | 271 | return mImplementation->mConstructor; |
michael@0 | 272 | |
michael@0 | 273 | return nullptr; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | nsXBLProtoImplAnonymousMethod* |
michael@0 | 277 | nsXBLPrototypeBinding::GetDestructor() |
michael@0 | 278 | { |
michael@0 | 279 | if (mImplementation) |
michael@0 | 280 | return mImplementation->mDestructor; |
michael@0 | 281 | |
michael@0 | 282 | return nullptr; |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | nsresult |
michael@0 | 286 | nsXBLPrototypeBinding::SetConstructor(nsXBLProtoImplAnonymousMethod* aMethod) |
michael@0 | 287 | { |
michael@0 | 288 | if (!mImplementation) |
michael@0 | 289 | return NS_ERROR_FAILURE; |
michael@0 | 290 | mImplementation->mConstructor = aMethod; |
michael@0 | 291 | return NS_OK; |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | nsresult |
michael@0 | 295 | nsXBLPrototypeBinding::SetDestructor(nsXBLProtoImplAnonymousMethod* aMethod) |
michael@0 | 296 | { |
michael@0 | 297 | if (!mImplementation) |
michael@0 | 298 | return NS_ERROR_FAILURE; |
michael@0 | 299 | mImplementation->mDestructor = aMethod; |
michael@0 | 300 | return NS_OK; |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | nsresult |
michael@0 | 304 | nsXBLPrototypeBinding::InstallImplementation(nsXBLBinding* aBinding) |
michael@0 | 305 | { |
michael@0 | 306 | if (mImplementation) |
michael@0 | 307 | return mImplementation->InstallImplementation(this, aBinding); |
michael@0 | 308 | return NS_OK; |
michael@0 | 309 | } |
michael@0 | 310 | |
michael@0 | 311 | // XXXbz this duplicates lots of SetAttrs |
michael@0 | 312 | void |
michael@0 | 313 | nsXBLPrototypeBinding::AttributeChanged(nsIAtom* aAttribute, |
michael@0 | 314 | int32_t aNameSpaceID, |
michael@0 | 315 | bool aRemoveFlag, |
michael@0 | 316 | nsIContent* aChangedElement, |
michael@0 | 317 | nsIContent* aAnonymousContent, |
michael@0 | 318 | bool aNotify) |
michael@0 | 319 | { |
michael@0 | 320 | if (!mAttributeTable) |
michael@0 | 321 | return; |
michael@0 | 322 | |
michael@0 | 323 | InnerAttributeTable *attributesNS = mAttributeTable->Get(aNameSpaceID); |
michael@0 | 324 | if (!attributesNS) |
michael@0 | 325 | return; |
michael@0 | 326 | |
michael@0 | 327 | nsXBLAttributeEntry* xblAttr = attributesNS->Get(aAttribute); |
michael@0 | 328 | if (!xblAttr) |
michael@0 | 329 | return; |
michael@0 | 330 | |
michael@0 | 331 | // Iterate over the elements in the array. |
michael@0 | 332 | nsCOMPtr<nsIContent> content = GetImmediateChild(nsGkAtoms::content); |
michael@0 | 333 | while (xblAttr) { |
michael@0 | 334 | nsIContent* element = xblAttr->GetElement(); |
michael@0 | 335 | |
michael@0 | 336 | nsCOMPtr<nsIContent> realElement = LocateInstance(aChangedElement, content, |
michael@0 | 337 | aAnonymousContent, |
michael@0 | 338 | element); |
michael@0 | 339 | |
michael@0 | 340 | if (realElement) { |
michael@0 | 341 | // Hold a strong reference here so that the atom doesn't go away during |
michael@0 | 342 | // UnsetAttr. |
michael@0 | 343 | nsCOMPtr<nsIAtom> dstAttr = xblAttr->GetDstAttribute(); |
michael@0 | 344 | int32_t dstNs = xblAttr->GetDstNameSpace(); |
michael@0 | 345 | |
michael@0 | 346 | if (aRemoveFlag) |
michael@0 | 347 | realElement->UnsetAttr(dstNs, dstAttr, aNotify); |
michael@0 | 348 | else { |
michael@0 | 349 | bool attrPresent = true; |
michael@0 | 350 | nsAutoString value; |
michael@0 | 351 | // Check to see if the src attribute is xbl:text. If so, then we need to obtain the |
michael@0 | 352 | // children of the real element and get the text nodes' values. |
michael@0 | 353 | if (aAttribute == nsGkAtoms::text && aNameSpaceID == kNameSpaceID_XBL) { |
michael@0 | 354 | if (!nsContentUtils::GetNodeTextContent(aChangedElement, false, value)) { |
michael@0 | 355 | NS_RUNTIMEABORT("OOM"); |
michael@0 | 356 | } |
michael@0 | 357 | value.StripChar(char16_t('\n')); |
michael@0 | 358 | value.StripChar(char16_t('\r')); |
michael@0 | 359 | nsAutoString stripVal(value); |
michael@0 | 360 | stripVal.StripWhitespace(); |
michael@0 | 361 | if (stripVal.IsEmpty()) |
michael@0 | 362 | attrPresent = false; |
michael@0 | 363 | } |
michael@0 | 364 | else { |
michael@0 | 365 | attrPresent = aChangedElement->GetAttr(aNameSpaceID, aAttribute, value); |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | if (attrPresent) |
michael@0 | 369 | realElement->SetAttr(dstNs, dstAttr, value, aNotify); |
michael@0 | 370 | } |
michael@0 | 371 | |
michael@0 | 372 | // See if we're the <html> tag in XUL, and see if value is being |
michael@0 | 373 | // set or unset on us. We may also be a tag that is having |
michael@0 | 374 | // xbl:text set on us. |
michael@0 | 375 | |
michael@0 | 376 | if ((dstAttr == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) || |
michael@0 | 377 | (realElement->NodeInfo()->Equals(nsGkAtoms::html, |
michael@0 | 378 | kNameSpaceID_XUL) && |
michael@0 | 379 | dstAttr == nsGkAtoms::value)) { |
michael@0 | 380 | // Flush out all our kids. |
michael@0 | 381 | uint32_t childCount = realElement->GetChildCount(); |
michael@0 | 382 | for (uint32_t i = 0; i < childCount; i++) |
michael@0 | 383 | realElement->RemoveChildAt(0, aNotify); |
michael@0 | 384 | |
michael@0 | 385 | if (!aRemoveFlag) { |
michael@0 | 386 | // Construct a new text node and insert it. |
michael@0 | 387 | nsAutoString value; |
michael@0 | 388 | aChangedElement->GetAttr(aNameSpaceID, aAttribute, value); |
michael@0 | 389 | if (!value.IsEmpty()) { |
michael@0 | 390 | nsRefPtr<nsTextNode> textContent = |
michael@0 | 391 | new nsTextNode(realElement->NodeInfo()->NodeInfoManager()); |
michael@0 | 392 | |
michael@0 | 393 | textContent->SetText(value, true); |
michael@0 | 394 | realElement->AppendChildTo(textContent, true); |
michael@0 | 395 | } |
michael@0 | 396 | } |
michael@0 | 397 | } |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | xblAttr = xblAttr->GetNext(); |
michael@0 | 401 | } |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | void |
michael@0 | 405 | nsXBLPrototypeBinding::SetBaseTag(int32_t aNamespaceID, nsIAtom* aTag) |
michael@0 | 406 | { |
michael@0 | 407 | mBaseNameSpaceID = aNamespaceID; |
michael@0 | 408 | mBaseTag = aTag; |
michael@0 | 409 | } |
michael@0 | 410 | |
michael@0 | 411 | nsIAtom* |
michael@0 | 412 | nsXBLPrototypeBinding::GetBaseTag(int32_t* aNamespaceID) |
michael@0 | 413 | { |
michael@0 | 414 | if (mBaseTag) { |
michael@0 | 415 | *aNamespaceID = mBaseNameSpaceID; |
michael@0 | 416 | return mBaseTag; |
michael@0 | 417 | } |
michael@0 | 418 | |
michael@0 | 419 | return nullptr; |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | bool |
michael@0 | 423 | nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const |
michael@0 | 424 | { |
michael@0 | 425 | // Check our IID table. |
michael@0 | 426 | return !!mInterfaceTable.GetWeak(aIID); |
michael@0 | 427 | } |
michael@0 | 428 | |
michael@0 | 429 | // Internal helpers /////////////////////////////////////////////////////////////////////// |
michael@0 | 430 | |
michael@0 | 431 | nsIContent* |
michael@0 | 432 | nsXBLPrototypeBinding::GetImmediateChild(nsIAtom* aTag) |
michael@0 | 433 | { |
michael@0 | 434 | for (nsIContent* child = mBinding->GetFirstChild(); |
michael@0 | 435 | child; |
michael@0 | 436 | child = child->GetNextSibling()) { |
michael@0 | 437 | if (child->NodeInfo()->Equals(aTag, kNameSpaceID_XBL)) { |
michael@0 | 438 | return child; |
michael@0 | 439 | } |
michael@0 | 440 | } |
michael@0 | 441 | |
michael@0 | 442 | return nullptr; |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | nsresult |
michael@0 | 446 | nsXBLPrototypeBinding::InitClass(const nsCString& aClassName, |
michael@0 | 447 | JSContext * aContext, |
michael@0 | 448 | JS::Handle<JSObject*> aScriptObject, |
michael@0 | 449 | JS::MutableHandle<JSObject*> aClassObject, |
michael@0 | 450 | bool* aNew) |
michael@0 | 451 | { |
michael@0 | 452 | return nsXBLBinding::DoInitJSClass(aContext, aScriptObject, |
michael@0 | 453 | aClassName, this, aClassObject, aNew); |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | nsIContent* |
michael@0 | 457 | nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement, |
michael@0 | 458 | nsIContent* aTemplRoot, |
michael@0 | 459 | nsIContent* aCopyRoot, |
michael@0 | 460 | nsIContent* aTemplChild) |
michael@0 | 461 | { |
michael@0 | 462 | // XXX We will get in trouble if the binding instantiation deviates from the template |
michael@0 | 463 | // in the prototype. |
michael@0 | 464 | if (aTemplChild == aTemplRoot || !aTemplChild) |
michael@0 | 465 | return nullptr; |
michael@0 | 466 | |
michael@0 | 467 | nsIContent* templParent = aTemplChild->GetParent(); |
michael@0 | 468 | |
michael@0 | 469 | // We may be disconnected from our parent during cycle collection. |
michael@0 | 470 | if (!templParent) |
michael@0 | 471 | return nullptr; |
michael@0 | 472 | |
michael@0 | 473 | nsIContent *copyParent = |
michael@0 | 474 | templParent == aTemplRoot ? aCopyRoot : |
michael@0 | 475 | LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent); |
michael@0 | 476 | |
michael@0 | 477 | if (!copyParent) |
michael@0 | 478 | return nullptr; |
michael@0 | 479 | |
michael@0 | 480 | return copyParent->GetChildAt(templParent->IndexOf(aTemplChild)); |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | struct nsXBLAttrChangeData |
michael@0 | 484 | { |
michael@0 | 485 | nsXBLPrototypeBinding* mProto; |
michael@0 | 486 | nsIContent* mBoundElement; |
michael@0 | 487 | nsIContent* mContent; |
michael@0 | 488 | int32_t mSrcNamespace; |
michael@0 | 489 | |
michael@0 | 490 | nsXBLAttrChangeData(nsXBLPrototypeBinding* aProto, |
michael@0 | 491 | nsIContent* aElt, nsIContent* aContent) |
michael@0 | 492 | :mProto(aProto), mBoundElement(aElt), mContent(aContent) {} |
michael@0 | 493 | }; |
michael@0 | 494 | |
michael@0 | 495 | // XXXbz this duplicates lots of AttributeChanged |
michael@0 | 496 | static PLDHashOperator |
michael@0 | 497 | SetAttrs(nsISupports* aKey, nsXBLAttributeEntry* aEntry, void* aClosure) |
michael@0 | 498 | { |
michael@0 | 499 | nsXBLAttrChangeData* changeData = static_cast<nsXBLAttrChangeData*>(aClosure); |
michael@0 | 500 | |
michael@0 | 501 | nsIAtom* src = aEntry->GetSrcAttribute(); |
michael@0 | 502 | int32_t srcNs = changeData->mSrcNamespace; |
michael@0 | 503 | nsAutoString value; |
michael@0 | 504 | bool attrPresent = true; |
michael@0 | 505 | |
michael@0 | 506 | if (src == nsGkAtoms::text && srcNs == kNameSpaceID_XBL) { |
michael@0 | 507 | if (!nsContentUtils::GetNodeTextContent(changeData->mBoundElement, false, |
michael@0 | 508 | value)) { |
michael@0 | 509 | NS_RUNTIMEABORT("OOM"); |
michael@0 | 510 | } |
michael@0 | 511 | value.StripChar(char16_t('\n')); |
michael@0 | 512 | value.StripChar(char16_t('\r')); |
michael@0 | 513 | nsAutoString stripVal(value); |
michael@0 | 514 | stripVal.StripWhitespace(); |
michael@0 | 515 | |
michael@0 | 516 | if (stripVal.IsEmpty()) |
michael@0 | 517 | attrPresent = false; |
michael@0 | 518 | } |
michael@0 | 519 | else { |
michael@0 | 520 | attrPresent = changeData->mBoundElement->GetAttr(srcNs, src, value); |
michael@0 | 521 | } |
michael@0 | 522 | |
michael@0 | 523 | if (attrPresent) { |
michael@0 | 524 | nsIContent* content = |
michael@0 | 525 | changeData->mProto->GetImmediateChild(nsGkAtoms::content); |
michael@0 | 526 | |
michael@0 | 527 | nsXBLAttributeEntry* curr = aEntry; |
michael@0 | 528 | while (curr) { |
michael@0 | 529 | nsIAtom* dst = curr->GetDstAttribute(); |
michael@0 | 530 | int32_t dstNs = curr->GetDstNameSpace(); |
michael@0 | 531 | nsIContent* element = curr->GetElement(); |
michael@0 | 532 | |
michael@0 | 533 | nsIContent *realElement = |
michael@0 | 534 | changeData->mProto->LocateInstance(changeData->mBoundElement, content, |
michael@0 | 535 | changeData->mContent, element); |
michael@0 | 536 | |
michael@0 | 537 | if (realElement) { |
michael@0 | 538 | realElement->SetAttr(dstNs, dst, value, false); |
michael@0 | 539 | |
michael@0 | 540 | // XXXndeakin shouldn't this be done in lieu of SetAttr? |
michael@0 | 541 | if ((dst == nsGkAtoms::text && dstNs == kNameSpaceID_XBL) || |
michael@0 | 542 | (realElement->NodeInfo()->Equals(nsGkAtoms::html, |
michael@0 | 543 | kNameSpaceID_XUL) && |
michael@0 | 544 | dst == nsGkAtoms::value && !value.IsEmpty())) { |
michael@0 | 545 | |
michael@0 | 546 | nsRefPtr<nsTextNode> textContent = |
michael@0 | 547 | new nsTextNode(realElement->NodeInfo()->NodeInfoManager()); |
michael@0 | 548 | |
michael@0 | 549 | textContent->SetText(value, false); |
michael@0 | 550 | realElement->AppendChildTo(textContent, false); |
michael@0 | 551 | } |
michael@0 | 552 | } |
michael@0 | 553 | |
michael@0 | 554 | curr = curr->GetNext(); |
michael@0 | 555 | } |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | return PL_DHASH_NEXT; |
michael@0 | 559 | } |
michael@0 | 560 | |
michael@0 | 561 | static PLDHashOperator |
michael@0 | 562 | SetAttrsNS(const uint32_t &aNamespace, |
michael@0 | 563 | nsXBLPrototypeBinding::InnerAttributeTable* aXBLAttributes, |
michael@0 | 564 | void* aClosure) |
michael@0 | 565 | { |
michael@0 | 566 | if (aXBLAttributes && aClosure) { |
michael@0 | 567 | nsXBLAttrChangeData* changeData = static_cast<nsXBLAttrChangeData*>(aClosure); |
michael@0 | 568 | changeData->mSrcNamespace = aNamespace; |
michael@0 | 569 | aXBLAttributes->EnumerateRead(SetAttrs, aClosure); |
michael@0 | 570 | } |
michael@0 | 571 | return PL_DHASH_NEXT; |
michael@0 | 572 | } |
michael@0 | 573 | |
michael@0 | 574 | void |
michael@0 | 575 | nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent) |
michael@0 | 576 | { |
michael@0 | 577 | if (mAttributeTable) { |
michael@0 | 578 | nsXBLAttrChangeData data(this, aBoundElement, aAnonymousContent); |
michael@0 | 579 | mAttributeTable->EnumerateRead(SetAttrsNS, &data); |
michael@0 | 580 | } |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | nsIStyleRuleProcessor* |
michael@0 | 584 | nsXBLPrototypeBinding::GetRuleProcessor() |
michael@0 | 585 | { |
michael@0 | 586 | if (mResources) { |
michael@0 | 587 | return mResources->mRuleProcessor; |
michael@0 | 588 | } |
michael@0 | 589 | |
michael@0 | 590 | return nullptr; |
michael@0 | 591 | } |
michael@0 | 592 | |
michael@0 | 593 | nsXBLPrototypeResources::sheet_array_type* |
michael@0 | 594 | nsXBLPrototypeBinding::GetOrCreateStyleSheets() |
michael@0 | 595 | { |
michael@0 | 596 | if (!mResources) { |
michael@0 | 597 | mResources = new nsXBLPrototypeResources(this); |
michael@0 | 598 | } |
michael@0 | 599 | |
michael@0 | 600 | return &mResources->mStyleSheetList; |
michael@0 | 601 | } |
michael@0 | 602 | |
michael@0 | 603 | nsXBLPrototypeResources::sheet_array_type* |
michael@0 | 604 | nsXBLPrototypeBinding::GetStyleSheets() |
michael@0 | 605 | { |
michael@0 | 606 | if (mResources) { |
michael@0 | 607 | return &mResources->mStyleSheetList; |
michael@0 | 608 | } |
michael@0 | 609 | |
michael@0 | 610 | return nullptr; |
michael@0 | 611 | } |
michael@0 | 612 | |
michael@0 | 613 | void |
michael@0 | 614 | nsXBLPrototypeBinding::EnsureAttributeTable() |
michael@0 | 615 | { |
michael@0 | 616 | if (!mAttributeTable) { |
michael@0 | 617 | mAttributeTable = new nsClassHashtable<nsUint32HashKey, InnerAttributeTable>(4); |
michael@0 | 618 | } |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | void |
michael@0 | 622 | nsXBLPrototypeBinding::AddToAttributeTable(int32_t aSourceNamespaceID, nsIAtom* aSourceTag, |
michael@0 | 623 | int32_t aDestNamespaceID, nsIAtom* aDestTag, |
michael@0 | 624 | nsIContent* aContent) |
michael@0 | 625 | { |
michael@0 | 626 | InnerAttributeTable* attributesNS = mAttributeTable->Get(aSourceNamespaceID); |
michael@0 | 627 | if (!attributesNS) { |
michael@0 | 628 | attributesNS = new InnerAttributeTable(4); |
michael@0 | 629 | mAttributeTable->Put(aSourceNamespaceID, attributesNS); |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | nsXBLAttributeEntry* xblAttr = |
michael@0 | 633 | new nsXBLAttributeEntry(aSourceTag, aDestTag, aDestNamespaceID, aContent); |
michael@0 | 634 | |
michael@0 | 635 | nsXBLAttributeEntry* entry = attributesNS->Get(aSourceTag); |
michael@0 | 636 | if (!entry) { |
michael@0 | 637 | attributesNS->Put(aSourceTag, xblAttr); |
michael@0 | 638 | } else { |
michael@0 | 639 | while (entry->GetNext()) |
michael@0 | 640 | entry = entry->GetNext(); |
michael@0 | 641 | entry->SetNext(xblAttr); |
michael@0 | 642 | } |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | void |
michael@0 | 646 | nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement) |
michael@0 | 647 | { |
michael@0 | 648 | // Don't add entries for <children> elements, since those will get |
michael@0 | 649 | // removed from the DOM when we construct the insertion point table. |
michael@0 | 650 | if (!aElement->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { |
michael@0 | 651 | nsAutoString inherits; |
michael@0 | 652 | aElement->GetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, inherits); |
michael@0 | 653 | |
michael@0 | 654 | if (!inherits.IsEmpty()) { |
michael@0 | 655 | EnsureAttributeTable(); |
michael@0 | 656 | |
michael@0 | 657 | // The user specified at least one attribute. |
michael@0 | 658 | char* str = ToNewCString(inherits); |
michael@0 | 659 | char* newStr; |
michael@0 | 660 | // XXX We should use a strtok function that tokenizes PRUnichars |
michael@0 | 661 | // so that we don't have to convert from Unicode to ASCII and then back |
michael@0 | 662 | |
michael@0 | 663 | char* token = nsCRT::strtok( str, ", ", &newStr ); |
michael@0 | 664 | while( token != nullptr ) { |
michael@0 | 665 | // Build an atom out of this attribute. |
michael@0 | 666 | nsCOMPtr<nsIAtom> atom; |
michael@0 | 667 | int32_t atomNsID = kNameSpaceID_None; |
michael@0 | 668 | nsCOMPtr<nsIAtom> attribute; |
michael@0 | 669 | int32_t attributeNsID = kNameSpaceID_None; |
michael@0 | 670 | |
michael@0 | 671 | // Figure out if this token contains a :. |
michael@0 | 672 | nsAutoString attrTok; attrTok.AssignWithConversion(token); |
michael@0 | 673 | int32_t index = attrTok.Find("=", true); |
michael@0 | 674 | nsresult rv; |
michael@0 | 675 | if (index != -1) { |
michael@0 | 676 | // This attribute maps to something different. |
michael@0 | 677 | nsAutoString left, right; |
michael@0 | 678 | attrTok.Left(left, index); |
michael@0 | 679 | attrTok.Right(right, attrTok.Length()-index-1); |
michael@0 | 680 | |
michael@0 | 681 | rv = nsContentUtils::SplitQName(aElement, left, &attributeNsID, |
michael@0 | 682 | getter_AddRefs(attribute)); |
michael@0 | 683 | if (NS_FAILED(rv)) |
michael@0 | 684 | return; |
michael@0 | 685 | |
michael@0 | 686 | rv = nsContentUtils::SplitQName(aElement, right, &atomNsID, |
michael@0 | 687 | getter_AddRefs(atom)); |
michael@0 | 688 | if (NS_FAILED(rv)) |
michael@0 | 689 | return; |
michael@0 | 690 | } |
michael@0 | 691 | else { |
michael@0 | 692 | nsAutoString tok; |
michael@0 | 693 | tok.AssignWithConversion(token); |
michael@0 | 694 | rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID, |
michael@0 | 695 | getter_AddRefs(atom)); |
michael@0 | 696 | if (NS_FAILED(rv)) |
michael@0 | 697 | return; |
michael@0 | 698 | attribute = atom; |
michael@0 | 699 | attributeNsID = atomNsID; |
michael@0 | 700 | } |
michael@0 | 701 | |
michael@0 | 702 | AddToAttributeTable(atomNsID, atom, attributeNsID, attribute, aElement); |
michael@0 | 703 | |
michael@0 | 704 | // Now remove the inherits attribute from the element so that it doesn't |
michael@0 | 705 | // show up on clones of the element. It is used |
michael@0 | 706 | // by the template only, and we don't need it anymore. |
michael@0 | 707 | // XXXdwh Don't do this for XUL elements, since it faults them into heavyweight |
michael@0 | 708 | // elements. Should nuke from the prototype instead. |
michael@0 | 709 | // aElement->UnsetAttr(kNameSpaceID_XBL, nsGkAtoms::inherits, false); |
michael@0 | 710 | |
michael@0 | 711 | token = nsCRT::strtok( newStr, ", ", &newStr ); |
michael@0 | 712 | } |
michael@0 | 713 | |
michael@0 | 714 | nsMemory::Free(str); |
michael@0 | 715 | } |
michael@0 | 716 | } |
michael@0 | 717 | |
michael@0 | 718 | // Recur into our children. |
michael@0 | 719 | for (nsIContent* child = aElement->GetFirstChild(); |
michael@0 | 720 | child; |
michael@0 | 721 | child = child->GetNextSibling()) { |
michael@0 | 722 | ConstructAttributeTable(child); |
michael@0 | 723 | } |
michael@0 | 724 | } |
michael@0 | 725 | |
michael@0 | 726 | nsresult |
michael@0 | 727 | nsXBLPrototypeBinding::ConstructInterfaceTable(const nsAString& aImpls) |
michael@0 | 728 | { |
michael@0 | 729 | if (!aImpls.IsEmpty()) { |
michael@0 | 730 | // Obtain the interface info manager that can tell us the IID |
michael@0 | 731 | // for a given interface name. |
michael@0 | 732 | nsCOMPtr<nsIInterfaceInfoManager> |
michael@0 | 733 | infoManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)); |
michael@0 | 734 | if (!infoManager) |
michael@0 | 735 | return NS_ERROR_FAILURE; |
michael@0 | 736 | |
michael@0 | 737 | // The user specified at least one attribute. |
michael@0 | 738 | NS_ConvertUTF16toUTF8 utf8impl(aImpls); |
michael@0 | 739 | char* str = utf8impl.BeginWriting(); |
michael@0 | 740 | char* newStr; |
michael@0 | 741 | // XXX We should use a strtok function that tokenizes PRUnichars |
michael@0 | 742 | // so that we don't have to convert from Unicode to ASCII and then back |
michael@0 | 743 | |
michael@0 | 744 | char* token = nsCRT::strtok( str, ", ", &newStr ); |
michael@0 | 745 | while( token != nullptr ) { |
michael@0 | 746 | // get the InterfaceInfo for the name |
michael@0 | 747 | nsCOMPtr<nsIInterfaceInfo> iinfo; |
michael@0 | 748 | infoManager->GetInfoForName(token, getter_AddRefs(iinfo)); |
michael@0 | 749 | |
michael@0 | 750 | if (iinfo) { |
michael@0 | 751 | // obtain an IID. |
michael@0 | 752 | const nsIID* iid = nullptr; |
michael@0 | 753 | iinfo->GetIIDShared(&iid); |
michael@0 | 754 | |
michael@0 | 755 | if (iid) { |
michael@0 | 756 | // We found a valid iid. Add it to our table. |
michael@0 | 757 | mInterfaceTable.Put(*iid, mBinding); |
michael@0 | 758 | |
michael@0 | 759 | // this block adds the parent interfaces of each interface |
michael@0 | 760 | // defined in the xbl definition (implements="nsI...") |
michael@0 | 761 | nsCOMPtr<nsIInterfaceInfo> parentInfo; |
michael@0 | 762 | // if it has a parent, add it to the table |
michael@0 | 763 | while (NS_SUCCEEDED(iinfo->GetParent(getter_AddRefs(parentInfo))) && parentInfo) { |
michael@0 | 764 | // get the iid |
michael@0 | 765 | parentInfo->GetIIDShared(&iid); |
michael@0 | 766 | |
michael@0 | 767 | // don't add nsISupports to the table |
michael@0 | 768 | if (!iid || iid->Equals(NS_GET_IID(nsISupports))) |
michael@0 | 769 | break; |
michael@0 | 770 | |
michael@0 | 771 | // add the iid to the table |
michael@0 | 772 | mInterfaceTable.Put(*iid, mBinding); |
michael@0 | 773 | |
michael@0 | 774 | // look for the next parent |
michael@0 | 775 | iinfo = parentInfo; |
michael@0 | 776 | } |
michael@0 | 777 | } |
michael@0 | 778 | } |
michael@0 | 779 | |
michael@0 | 780 | token = nsCRT::strtok( newStr, ", ", &newStr ); |
michael@0 | 781 | } |
michael@0 | 782 | } |
michael@0 | 783 | |
michael@0 | 784 | return NS_OK; |
michael@0 | 785 | } |
michael@0 | 786 | |
michael@0 | 787 | nsresult |
michael@0 | 788 | nsXBLPrototypeBinding::AddResourceListener(nsIContent* aBoundElement) |
michael@0 | 789 | { |
michael@0 | 790 | if (!mResources) |
michael@0 | 791 | return NS_ERROR_FAILURE; // Makes no sense to add a listener when the binding |
michael@0 | 792 | // has no resources. |
michael@0 | 793 | |
michael@0 | 794 | mResources->AddResourceListener(aBoundElement); |
michael@0 | 795 | return NS_OK; |
michael@0 | 796 | } |
michael@0 | 797 | |
michael@0 | 798 | void |
michael@0 | 799 | nsXBLPrototypeBinding::CreateKeyHandlers() |
michael@0 | 800 | { |
michael@0 | 801 | nsXBLPrototypeHandler* curr = mPrototypeHandler; |
michael@0 | 802 | while (curr) { |
michael@0 | 803 | nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName(); |
michael@0 | 804 | if (eventAtom == nsGkAtoms::keyup || |
michael@0 | 805 | eventAtom == nsGkAtoms::keydown || |
michael@0 | 806 | eventAtom == nsGkAtoms::keypress) { |
michael@0 | 807 | uint8_t phase = curr->GetPhase(); |
michael@0 | 808 | uint8_t type = curr->GetType(); |
michael@0 | 809 | |
michael@0 | 810 | int32_t count = mKeyHandlers.Count(); |
michael@0 | 811 | int32_t i; |
michael@0 | 812 | nsXBLKeyEventHandler* handler = nullptr; |
michael@0 | 813 | for (i = 0; i < count; ++i) { |
michael@0 | 814 | handler = mKeyHandlers[i]; |
michael@0 | 815 | if (handler->Matches(eventAtom, phase, type)) |
michael@0 | 816 | break; |
michael@0 | 817 | } |
michael@0 | 818 | |
michael@0 | 819 | if (i == count) { |
michael@0 | 820 | nsRefPtr<nsXBLKeyEventHandler> newHandler; |
michael@0 | 821 | NS_NewXBLKeyEventHandler(eventAtom, phase, type, |
michael@0 | 822 | getter_AddRefs(newHandler)); |
michael@0 | 823 | if (newHandler) |
michael@0 | 824 | mKeyHandlers.AppendObject(newHandler); |
michael@0 | 825 | handler = newHandler; |
michael@0 | 826 | } |
michael@0 | 827 | |
michael@0 | 828 | if (handler) |
michael@0 | 829 | handler->AddProtoHandler(curr); |
michael@0 | 830 | } |
michael@0 | 831 | |
michael@0 | 832 | curr = curr->GetNextHandler(); |
michael@0 | 833 | } |
michael@0 | 834 | } |
michael@0 | 835 | |
michael@0 | 836 | class XBLPrototypeSetupCleanup |
michael@0 | 837 | { |
michael@0 | 838 | public: |
michael@0 | 839 | XBLPrototypeSetupCleanup(nsXBLDocumentInfo* aDocInfo, const nsACString& aID) |
michael@0 | 840 | : mDocInfo(aDocInfo), mID(aID) {} |
michael@0 | 841 | |
michael@0 | 842 | ~XBLPrototypeSetupCleanup() |
michael@0 | 843 | { |
michael@0 | 844 | if (mDocInfo) { |
michael@0 | 845 | mDocInfo->RemovePrototypeBinding(mID); |
michael@0 | 846 | } |
michael@0 | 847 | } |
michael@0 | 848 | |
michael@0 | 849 | void Disconnect() |
michael@0 | 850 | { |
michael@0 | 851 | mDocInfo = nullptr; |
michael@0 | 852 | } |
michael@0 | 853 | |
michael@0 | 854 | nsXBLDocumentInfo* mDocInfo; |
michael@0 | 855 | nsAutoCString mID; |
michael@0 | 856 | }; |
michael@0 | 857 | |
michael@0 | 858 | nsresult |
michael@0 | 859 | nsXBLPrototypeBinding::Read(nsIObjectInputStream* aStream, |
michael@0 | 860 | nsXBLDocumentInfo* aDocInfo, |
michael@0 | 861 | nsIDocument* aDocument, |
michael@0 | 862 | uint8_t aFlags) |
michael@0 | 863 | { |
michael@0 | 864 | mInheritStyle = (aFlags & XBLBinding_Serialize_InheritStyle) ? true : false; |
michael@0 | 865 | mChromeOnlyContent = |
michael@0 | 866 | (aFlags & XBLBinding_Serialize_ChromeOnlyContent) ? true : false; |
michael@0 | 867 | |
michael@0 | 868 | // nsXBLContentSink::ConstructBinding doesn't create a binding with an empty |
michael@0 | 869 | // id, so we don't here either. |
michael@0 | 870 | nsAutoCString id; |
michael@0 | 871 | nsresult rv = aStream->ReadCString(id); |
michael@0 | 872 | |
michael@0 | 873 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 874 | NS_ENSURE_TRUE(!id.IsEmpty(), NS_ERROR_FAILURE); |
michael@0 | 875 | |
michael@0 | 876 | nsAutoCString baseBindingURI; |
michael@0 | 877 | rv = aStream->ReadCString(baseBindingURI); |
michael@0 | 878 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 879 | mCheckedBaseProto = true; |
michael@0 | 880 | |
michael@0 | 881 | if (!baseBindingURI.IsEmpty()) { |
michael@0 | 882 | rv = NS_NewURI(getter_AddRefs(mBaseBindingURI), baseBindingURI); |
michael@0 | 883 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 884 | } |
michael@0 | 885 | |
michael@0 | 886 | rv = ReadNamespace(aStream, mBaseNameSpaceID); |
michael@0 | 887 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 888 | |
michael@0 | 889 | nsAutoString baseTag; |
michael@0 | 890 | rv = aStream->ReadString(baseTag); |
michael@0 | 891 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 892 | if (!baseTag.IsEmpty()) { |
michael@0 | 893 | mBaseTag = do_GetAtom(baseTag); |
michael@0 | 894 | } |
michael@0 | 895 | |
michael@0 | 896 | aDocument->CreateElem(NS_LITERAL_STRING("binding"), nullptr, kNameSpaceID_XBL, |
michael@0 | 897 | getter_AddRefs(mBinding)); |
michael@0 | 898 | |
michael@0 | 899 | nsCOMPtr<nsIContent> child; |
michael@0 | 900 | rv = ReadContentNode(aStream, aDocument, aDocument->NodeInfoManager(), getter_AddRefs(child)); |
michael@0 | 901 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 902 | |
michael@0 | 903 | Element* rootElement = aDocument->GetRootElement(); |
michael@0 | 904 | if (rootElement) |
michael@0 | 905 | rootElement->AppendChildTo(mBinding, false); |
michael@0 | 906 | |
michael@0 | 907 | if (child) { |
michael@0 | 908 | mBinding->AppendChildTo(child, false); |
michael@0 | 909 | } |
michael@0 | 910 | |
michael@0 | 911 | uint32_t interfaceCount; |
michael@0 | 912 | rv = aStream->Read32(&interfaceCount); |
michael@0 | 913 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 914 | |
michael@0 | 915 | for (; interfaceCount > 0; interfaceCount--) { |
michael@0 | 916 | nsIID iid; |
michael@0 | 917 | aStream->ReadID(&iid); |
michael@0 | 918 | mInterfaceTable.Put(iid, mBinding); |
michael@0 | 919 | } |
michael@0 | 920 | |
michael@0 | 921 | AutoSafeJSContext cx; |
michael@0 | 922 | JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope()); |
michael@0 | 923 | NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED); |
michael@0 | 924 | JSAutoCompartment ac(cx, compilationGlobal); |
michael@0 | 925 | |
michael@0 | 926 | bool isFirstBinding = aFlags & XBLBinding_Serialize_IsFirstBinding; |
michael@0 | 927 | rv = Init(id, aDocInfo, nullptr, isFirstBinding); |
michael@0 | 928 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 929 | |
michael@0 | 930 | // We need to set the prototype binding before reading the nsXBLProtoImpl, |
michael@0 | 931 | // as it may be retrieved within. |
michael@0 | 932 | rv = aDocInfo->SetPrototypeBinding(id, this); |
michael@0 | 933 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 934 | |
michael@0 | 935 | XBLPrototypeSetupCleanup cleanup(aDocInfo, id); |
michael@0 | 936 | |
michael@0 | 937 | nsAutoCString className; |
michael@0 | 938 | rv = aStream->ReadCString(className); |
michael@0 | 939 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 940 | |
michael@0 | 941 | if (!className.IsEmpty()) { |
michael@0 | 942 | nsXBLProtoImpl* impl; // NS_NewXBLProtoImpl will set mImplementation for us |
michael@0 | 943 | NS_NewXBLProtoImpl(this, NS_ConvertUTF8toUTF16(className).get(), &impl); |
michael@0 | 944 | |
michael@0 | 945 | // This needs to happen after SetPrototypeBinding as calls are made to |
michael@0 | 946 | // retrieve the mapped bindings from within here. However, if an error |
michael@0 | 947 | // occurs, the mapping should be removed again so that we don't keep an |
michael@0 | 948 | // invalid binding around. |
michael@0 | 949 | rv = mImplementation->Read(aStream, this); |
michael@0 | 950 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 951 | } |
michael@0 | 952 | |
michael@0 | 953 | // Next read in the handlers. |
michael@0 | 954 | nsXBLPrototypeHandler* previousHandler = nullptr; |
michael@0 | 955 | |
michael@0 | 956 | do { |
michael@0 | 957 | XBLBindingSerializeDetails type; |
michael@0 | 958 | rv = aStream->Read8(&type); |
michael@0 | 959 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 960 | |
michael@0 | 961 | if (type == XBLBinding_Serialize_NoMoreItems) |
michael@0 | 962 | break; |
michael@0 | 963 | |
michael@0 | 964 | NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Handler, |
michael@0 | 965 | "invalid handler type"); |
michael@0 | 966 | |
michael@0 | 967 | nsXBLPrototypeHandler* handler = new nsXBLPrototypeHandler(this); |
michael@0 | 968 | rv = handler->Read(aStream); |
michael@0 | 969 | if (NS_FAILED(rv)) { |
michael@0 | 970 | delete handler; |
michael@0 | 971 | return rv; |
michael@0 | 972 | } |
michael@0 | 973 | |
michael@0 | 974 | if (previousHandler) { |
michael@0 | 975 | previousHandler->SetNextHandler(handler); |
michael@0 | 976 | } |
michael@0 | 977 | else { |
michael@0 | 978 | SetPrototypeHandlers(handler); |
michael@0 | 979 | } |
michael@0 | 980 | previousHandler = handler; |
michael@0 | 981 | } while (1); |
michael@0 | 982 | |
michael@0 | 983 | if (mBinding) { |
michael@0 | 984 | while (true) { |
michael@0 | 985 | XBLBindingSerializeDetails type; |
michael@0 | 986 | rv = aStream->Read8(&type); |
michael@0 | 987 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 988 | |
michael@0 | 989 | if (type != XBLBinding_Serialize_Attribute) { |
michael@0 | 990 | break; |
michael@0 | 991 | } |
michael@0 | 992 | |
michael@0 | 993 | int32_t attrNamespace; |
michael@0 | 994 | rv = ReadNamespace(aStream, attrNamespace); |
michael@0 | 995 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 996 | |
michael@0 | 997 | nsAutoString attrPrefix, attrName, attrValue; |
michael@0 | 998 | rv = aStream->ReadString(attrPrefix); |
michael@0 | 999 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1000 | |
michael@0 | 1001 | rv = aStream->ReadString(attrName); |
michael@0 | 1002 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1003 | |
michael@0 | 1004 | rv = aStream->ReadString(attrValue); |
michael@0 | 1005 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1006 | |
michael@0 | 1007 | nsCOMPtr<nsIAtom> atomPrefix = do_GetAtom(attrPrefix); |
michael@0 | 1008 | nsCOMPtr<nsIAtom> atomName = do_GetAtom(attrName); |
michael@0 | 1009 | mBinding->SetAttr(attrNamespace, atomName, atomPrefix, attrValue, false); |
michael@0 | 1010 | } |
michael@0 | 1011 | } |
michael@0 | 1012 | |
michael@0 | 1013 | // Finally, read in the resources. |
michael@0 | 1014 | while (true) { |
michael@0 | 1015 | XBLBindingSerializeDetails type; |
michael@0 | 1016 | rv = aStream->Read8(&type); |
michael@0 | 1017 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1018 | |
michael@0 | 1019 | if (type == XBLBinding_Serialize_NoMoreItems) |
michael@0 | 1020 | break; |
michael@0 | 1021 | |
michael@0 | 1022 | NS_ASSERTION((type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Stylesheet || |
michael@0 | 1023 | (type & XBLBinding_Serialize_Mask) == XBLBinding_Serialize_Image, "invalid resource type"); |
michael@0 | 1024 | |
michael@0 | 1025 | nsAutoString src; |
michael@0 | 1026 | rv = aStream->ReadString(src); |
michael@0 | 1027 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1028 | |
michael@0 | 1029 | AddResource(type == XBLBinding_Serialize_Stylesheet ? nsGkAtoms::stylesheet : |
michael@0 | 1030 | nsGkAtoms::image, src); |
michael@0 | 1031 | } |
michael@0 | 1032 | |
michael@0 | 1033 | if (isFirstBinding) { |
michael@0 | 1034 | aDocInfo->SetFirstPrototypeBinding(this); |
michael@0 | 1035 | } |
michael@0 | 1036 | |
michael@0 | 1037 | cleanup.Disconnect(); |
michael@0 | 1038 | return NS_OK; |
michael@0 | 1039 | } |
michael@0 | 1040 | |
michael@0 | 1041 | // static |
michael@0 | 1042 | nsresult |
michael@0 | 1043 | nsXBLPrototypeBinding::ReadNewBinding(nsIObjectInputStream* aStream, |
michael@0 | 1044 | nsXBLDocumentInfo* aDocInfo, |
michael@0 | 1045 | nsIDocument* aDocument, |
michael@0 | 1046 | uint8_t aFlags) |
michael@0 | 1047 | { |
michael@0 | 1048 | // If the Read() succeeds, |binding| will end up being owned by aDocInfo's |
michael@0 | 1049 | // binding table. Otherwise, we must manually delete it. |
michael@0 | 1050 | nsXBLPrototypeBinding* binding = new nsXBLPrototypeBinding(); |
michael@0 | 1051 | nsresult rv = binding->Read(aStream, aDocInfo, aDocument, aFlags); |
michael@0 | 1052 | if (NS_FAILED(rv)) { |
michael@0 | 1053 | delete binding; |
michael@0 | 1054 | } |
michael@0 | 1055 | return rv; |
michael@0 | 1056 | } |
michael@0 | 1057 | |
michael@0 | 1058 | static PLDHashOperator |
michael@0 | 1059 | WriteInterfaceID(const nsIID& aKey, nsIContent* aData, void* aClosure) |
michael@0 | 1060 | { |
michael@0 | 1061 | // We can just write out the ids. The cache will be invalidated when a |
michael@0 | 1062 | // different build is used, so we don't need to worry about ids changing. |
michael@0 | 1063 | static_cast<nsIObjectOutputStream *>(aClosure)->WriteID(aKey); |
michael@0 | 1064 | return PL_DHASH_NEXT; |
michael@0 | 1065 | } |
michael@0 | 1066 | |
michael@0 | 1067 | nsresult |
michael@0 | 1068 | nsXBLPrototypeBinding::Write(nsIObjectOutputStream* aStream) |
michael@0 | 1069 | { |
michael@0 | 1070 | // This writes out the binding. Note that mCheckedBaseProto, |
michael@0 | 1071 | // mKeyHandlersRegistered and mKeyHandlers are not serialized as they are |
michael@0 | 1072 | // computed on demand. |
michael@0 | 1073 | |
michael@0 | 1074 | AutoSafeJSContext cx; |
michael@0 | 1075 | JS::Rooted<JSObject*> compilationGlobal(cx, xpc::GetCompilationScope()); |
michael@0 | 1076 | NS_ENSURE_TRUE(compilationGlobal, NS_ERROR_UNEXPECTED); |
michael@0 | 1077 | JSAutoCompartment ac(cx, compilationGlobal); |
michael@0 | 1078 | |
michael@0 | 1079 | uint8_t flags = mInheritStyle ? XBLBinding_Serialize_InheritStyle : 0; |
michael@0 | 1080 | |
michael@0 | 1081 | // mAlternateBindingURI is only set on the first binding. |
michael@0 | 1082 | if (mAlternateBindingURI) { |
michael@0 | 1083 | flags |= XBLBinding_Serialize_IsFirstBinding; |
michael@0 | 1084 | } |
michael@0 | 1085 | |
michael@0 | 1086 | if (mChromeOnlyContent) { |
michael@0 | 1087 | flags |= XBLBinding_Serialize_ChromeOnlyContent; |
michael@0 | 1088 | } |
michael@0 | 1089 | |
michael@0 | 1090 | nsresult rv = aStream->Write8(flags); |
michael@0 | 1091 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1092 | |
michael@0 | 1093 | nsAutoCString id; |
michael@0 | 1094 | mBindingURI->GetRef(id); |
michael@0 | 1095 | rv = aStream->WriteStringZ(id.get()); |
michael@0 | 1096 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1097 | |
michael@0 | 1098 | // write out the extends and display attribute values |
michael@0 | 1099 | nsAutoCString extends; |
michael@0 | 1100 | ResolveBaseBinding(); |
michael@0 | 1101 | if (mBaseBindingURI) |
michael@0 | 1102 | mBaseBindingURI->GetSpec(extends); |
michael@0 | 1103 | |
michael@0 | 1104 | rv = aStream->WriteStringZ(extends.get()); |
michael@0 | 1105 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1106 | |
michael@0 | 1107 | rv = WriteNamespace(aStream, mBaseNameSpaceID); |
michael@0 | 1108 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1109 | |
michael@0 | 1110 | nsAutoString baseTag; |
michael@0 | 1111 | if (mBaseTag) { |
michael@0 | 1112 | mBaseTag->ToString(baseTag); |
michael@0 | 1113 | } |
michael@0 | 1114 | rv = aStream->WriteWStringZ(baseTag.get()); |
michael@0 | 1115 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1116 | |
michael@0 | 1117 | nsIContent* content = GetImmediateChild(nsGkAtoms::content); |
michael@0 | 1118 | if (content) { |
michael@0 | 1119 | rv = WriteContentNode(aStream, content); |
michael@0 | 1120 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1121 | } |
michael@0 | 1122 | else { |
michael@0 | 1123 | // Write a marker to indicate that there is no content. |
michael@0 | 1124 | rv = aStream->Write8(XBLBinding_Serialize_NoContent); |
michael@0 | 1125 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1126 | } |
michael@0 | 1127 | |
michael@0 | 1128 | // Enumerate and write out the implemented interfaces. |
michael@0 | 1129 | rv = aStream->Write32(mInterfaceTable.Count()); |
michael@0 | 1130 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1131 | |
michael@0 | 1132 | mInterfaceTable.EnumerateRead(WriteInterfaceID, aStream); |
michael@0 | 1133 | |
michael@0 | 1134 | // Write out the implementation details. |
michael@0 | 1135 | if (mImplementation) { |
michael@0 | 1136 | rv = mImplementation->Write(aStream, this); |
michael@0 | 1137 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1138 | } |
michael@0 | 1139 | else { |
michael@0 | 1140 | // Write out an empty classname. This indicates that the binding does not |
michael@0 | 1141 | // define an implementation. |
michael@0 | 1142 | rv = aStream->WriteWStringZ(EmptyString().get()); |
michael@0 | 1143 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1144 | } |
michael@0 | 1145 | |
michael@0 | 1146 | // Write out the handlers. |
michael@0 | 1147 | nsXBLPrototypeHandler* handler = mPrototypeHandler; |
michael@0 | 1148 | while (handler) { |
michael@0 | 1149 | rv = handler->Write(aStream); |
michael@0 | 1150 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1151 | |
michael@0 | 1152 | handler = handler->GetNextHandler(); |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | aStream->Write8(XBLBinding_Serialize_NoMoreItems); |
michael@0 | 1156 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1157 | |
michael@0 | 1158 | if (mBinding) { |
michael@0 | 1159 | uint32_t attributes = mBinding->GetAttrCount(); |
michael@0 | 1160 | nsAutoString attrValue; |
michael@0 | 1161 | for (uint32_t i = 0; i < attributes; ++i) { |
michael@0 | 1162 | const nsAttrName* attr = mBinding->GetAttrNameAt(i); |
michael@0 | 1163 | nsDependentAtomString attrName = attr->LocalName(); |
michael@0 | 1164 | mBinding->GetAttr(attr->NamespaceID(), attr->LocalName(), attrValue); |
michael@0 | 1165 | rv = aStream->Write8(XBLBinding_Serialize_Attribute); |
michael@0 | 1166 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1167 | |
michael@0 | 1168 | rv = WriteNamespace(aStream, attr->NamespaceID()); |
michael@0 | 1169 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1170 | |
michael@0 | 1171 | nsIAtom* prefix = attr->GetPrefix(); |
michael@0 | 1172 | nsAutoString prefixString; |
michael@0 | 1173 | if (prefix) { |
michael@0 | 1174 | prefix->ToString(prefixString); |
michael@0 | 1175 | } |
michael@0 | 1176 | |
michael@0 | 1177 | rv = aStream->WriteWStringZ(prefixString.get()); |
michael@0 | 1178 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1179 | |
michael@0 | 1180 | rv = aStream->WriteWStringZ(attrName.get()); |
michael@0 | 1181 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1182 | |
michael@0 | 1183 | rv = aStream->WriteWStringZ(attrValue.get()); |
michael@0 | 1184 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1185 | } |
michael@0 | 1186 | } |
michael@0 | 1187 | |
michael@0 | 1188 | aStream->Write8(XBLBinding_Serialize_NoMoreItems); |
michael@0 | 1189 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1190 | |
michael@0 | 1191 | // Write out the resources |
michael@0 | 1192 | if (mResources) { |
michael@0 | 1193 | rv = mResources->Write(aStream); |
michael@0 | 1194 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | // Write out an end mark at the end. |
michael@0 | 1198 | return aStream->Write8(XBLBinding_Serialize_NoMoreItems); |
michael@0 | 1199 | } |
michael@0 | 1200 | |
michael@0 | 1201 | nsresult |
michael@0 | 1202 | nsXBLPrototypeBinding::ReadContentNode(nsIObjectInputStream* aStream, |
michael@0 | 1203 | nsIDocument* aDocument, |
michael@0 | 1204 | nsNodeInfoManager* aNim, |
michael@0 | 1205 | nsIContent** aContent) |
michael@0 | 1206 | { |
michael@0 | 1207 | *aContent = nullptr; |
michael@0 | 1208 | |
michael@0 | 1209 | int32_t namespaceID; |
michael@0 | 1210 | nsresult rv = ReadNamespace(aStream, namespaceID); |
michael@0 | 1211 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1212 | |
michael@0 | 1213 | // There is no content to read so just return. |
michael@0 | 1214 | if (namespaceID == XBLBinding_Serialize_NoContent) |
michael@0 | 1215 | return NS_OK; |
michael@0 | 1216 | |
michael@0 | 1217 | nsCOMPtr<nsIContent> content; |
michael@0 | 1218 | |
michael@0 | 1219 | // If this is a text type, just read the string and return. |
michael@0 | 1220 | if (namespaceID == XBLBinding_Serialize_TextNode || |
michael@0 | 1221 | namespaceID == XBLBinding_Serialize_CDATANode || |
michael@0 | 1222 | namespaceID == XBLBinding_Serialize_CommentNode) { |
michael@0 | 1223 | switch (namespaceID) { |
michael@0 | 1224 | case XBLBinding_Serialize_TextNode: |
michael@0 | 1225 | content = new nsTextNode(aNim); |
michael@0 | 1226 | break; |
michael@0 | 1227 | case XBLBinding_Serialize_CDATANode: |
michael@0 | 1228 | content = new CDATASection(aNim); |
michael@0 | 1229 | break; |
michael@0 | 1230 | case XBLBinding_Serialize_CommentNode: |
michael@0 | 1231 | content = new Comment(aNim); |
michael@0 | 1232 | break; |
michael@0 | 1233 | default: |
michael@0 | 1234 | break; |
michael@0 | 1235 | } |
michael@0 | 1236 | |
michael@0 | 1237 | nsAutoString text; |
michael@0 | 1238 | rv = aStream->ReadString(text); |
michael@0 | 1239 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1240 | |
michael@0 | 1241 | content->SetText(text, false); |
michael@0 | 1242 | content.swap(*aContent); |
michael@0 | 1243 | return NS_OK; |
michael@0 | 1244 | } |
michael@0 | 1245 | |
michael@0 | 1246 | // Otherwise, it's an element, so read its tag, attributes and children. |
michael@0 | 1247 | nsAutoString prefix, tag; |
michael@0 | 1248 | rv = aStream->ReadString(prefix); |
michael@0 | 1249 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1250 | |
michael@0 | 1251 | nsCOMPtr<nsIAtom> prefixAtom; |
michael@0 | 1252 | if (!prefix.IsEmpty()) |
michael@0 | 1253 | prefixAtom = do_GetAtom(prefix); |
michael@0 | 1254 | |
michael@0 | 1255 | rv = aStream->ReadString(tag); |
michael@0 | 1256 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1257 | |
michael@0 | 1258 | nsCOMPtr<nsIAtom> tagAtom = do_GetAtom(tag); |
michael@0 | 1259 | nsCOMPtr<nsINodeInfo> nodeInfo = |
michael@0 | 1260 | aNim->GetNodeInfo(tagAtom, prefixAtom, namespaceID, nsIDOMNode::ELEMENT_NODE); |
michael@0 | 1261 | |
michael@0 | 1262 | uint32_t attrCount; |
michael@0 | 1263 | rv = aStream->Read32(&attrCount); |
michael@0 | 1264 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1265 | |
michael@0 | 1266 | // Create XUL prototype elements, or regular elements for other namespaces. |
michael@0 | 1267 | // This needs to match the code in nsXBLContentSink::CreateElement. |
michael@0 | 1268 | #ifdef MOZ_XUL |
michael@0 | 1269 | if (namespaceID == kNameSpaceID_XUL) { |
michael@0 | 1270 | nsIURI* documentURI = aDocument->GetDocumentURI(); |
michael@0 | 1271 | |
michael@0 | 1272 | nsRefPtr<nsXULPrototypeElement> prototype = new nsXULPrototypeElement(); |
michael@0 | 1273 | NS_ENSURE_TRUE(prototype, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 1274 | |
michael@0 | 1275 | prototype->mNodeInfo = nodeInfo; |
michael@0 | 1276 | |
michael@0 | 1277 | nsXULPrototypeAttribute* attrs = nullptr; |
michael@0 | 1278 | if (attrCount > 0) { |
michael@0 | 1279 | attrs = new nsXULPrototypeAttribute[attrCount]; |
michael@0 | 1280 | } |
michael@0 | 1281 | |
michael@0 | 1282 | prototype->mAttributes = attrs; |
michael@0 | 1283 | prototype->mNumAttributes = attrCount; |
michael@0 | 1284 | |
michael@0 | 1285 | for (uint32_t i = 0; i < attrCount; i++) { |
michael@0 | 1286 | rv = ReadNamespace(aStream, namespaceID); |
michael@0 | 1287 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1288 | |
michael@0 | 1289 | nsAutoString prefix, name, val; |
michael@0 | 1290 | rv = aStream->ReadString(prefix); |
michael@0 | 1291 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1292 | rv = aStream->ReadString(name); |
michael@0 | 1293 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1294 | rv = aStream->ReadString(val); |
michael@0 | 1295 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1296 | |
michael@0 | 1297 | nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name); |
michael@0 | 1298 | if (namespaceID == kNameSpaceID_None) { |
michael@0 | 1299 | attrs[i].mName.SetTo(nameAtom); |
michael@0 | 1300 | } |
michael@0 | 1301 | else { |
michael@0 | 1302 | nsCOMPtr<nsIAtom> prefixAtom; |
michael@0 | 1303 | if (!prefix.IsEmpty()) |
michael@0 | 1304 | prefixAtom = do_GetAtom(prefix); |
michael@0 | 1305 | |
michael@0 | 1306 | nsCOMPtr<nsINodeInfo> ni = |
michael@0 | 1307 | aNim->GetNodeInfo(nameAtom, prefixAtom, |
michael@0 | 1308 | namespaceID, nsIDOMNode::ATTRIBUTE_NODE); |
michael@0 | 1309 | attrs[i].mName.SetTo(ni); |
michael@0 | 1310 | } |
michael@0 | 1311 | |
michael@0 | 1312 | rv = prototype->SetAttrAt(i, val, documentURI); |
michael@0 | 1313 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1314 | } |
michael@0 | 1315 | |
michael@0 | 1316 | nsCOMPtr<Element> result; |
michael@0 | 1317 | nsresult rv = |
michael@0 | 1318 | nsXULElement::Create(prototype, aDocument, false, false, getter_AddRefs(result)); |
michael@0 | 1319 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1320 | content = result; |
michael@0 | 1321 | } |
michael@0 | 1322 | else { |
michael@0 | 1323 | #endif |
michael@0 | 1324 | nsCOMPtr<Element> element; |
michael@0 | 1325 | NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER); |
michael@0 | 1326 | content = element; |
michael@0 | 1327 | |
michael@0 | 1328 | for (uint32_t i = 0; i < attrCount; i++) { |
michael@0 | 1329 | rv = ReadNamespace(aStream, namespaceID); |
michael@0 | 1330 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1331 | |
michael@0 | 1332 | nsAutoString prefix, name, val; |
michael@0 | 1333 | rv = aStream->ReadString(prefix); |
michael@0 | 1334 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1335 | rv = aStream->ReadString(name); |
michael@0 | 1336 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1337 | rv = aStream->ReadString(val); |
michael@0 | 1338 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1339 | |
michael@0 | 1340 | nsCOMPtr<nsIAtom> prefixAtom; |
michael@0 | 1341 | if (!prefix.IsEmpty()) |
michael@0 | 1342 | prefixAtom = do_GetAtom(prefix); |
michael@0 | 1343 | |
michael@0 | 1344 | nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name); |
michael@0 | 1345 | content->SetAttr(namespaceID, nameAtom, prefixAtom, val, false); |
michael@0 | 1346 | } |
michael@0 | 1347 | |
michael@0 | 1348 | #ifdef MOZ_XUL |
michael@0 | 1349 | } |
michael@0 | 1350 | #endif |
michael@0 | 1351 | |
michael@0 | 1352 | // Now read the attribute forwarding entries (xbl:inherits) |
michael@0 | 1353 | |
michael@0 | 1354 | int32_t srcNamespaceID, destNamespaceID; |
michael@0 | 1355 | rv = ReadNamespace(aStream, srcNamespaceID); |
michael@0 | 1356 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1357 | |
michael@0 | 1358 | while (srcNamespaceID != XBLBinding_Serialize_NoMoreAttributes) { |
michael@0 | 1359 | nsAutoString srcAttribute, destAttribute; |
michael@0 | 1360 | rv = aStream->ReadString(srcAttribute); |
michael@0 | 1361 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1362 | rv = ReadNamespace(aStream, destNamespaceID); |
michael@0 | 1363 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1364 | rv = aStream->ReadString(destAttribute); |
michael@0 | 1365 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1366 | |
michael@0 | 1367 | nsCOMPtr<nsIAtom> srcAtom = do_GetAtom(srcAttribute); |
michael@0 | 1368 | nsCOMPtr<nsIAtom> destAtom = do_GetAtom(destAttribute); |
michael@0 | 1369 | |
michael@0 | 1370 | EnsureAttributeTable(); |
michael@0 | 1371 | AddToAttributeTable(srcNamespaceID, srcAtom, destNamespaceID, destAtom, content); |
michael@0 | 1372 | |
michael@0 | 1373 | rv = ReadNamespace(aStream, srcNamespaceID); |
michael@0 | 1374 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1375 | } |
michael@0 | 1376 | |
michael@0 | 1377 | // Finally, read in the child nodes. |
michael@0 | 1378 | uint32_t childCount; |
michael@0 | 1379 | rv = aStream->Read32(&childCount); |
michael@0 | 1380 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1381 | |
michael@0 | 1382 | for (uint32_t i = 0; i < childCount; i++) { |
michael@0 | 1383 | nsCOMPtr<nsIContent> child; |
michael@0 | 1384 | ReadContentNode(aStream, aDocument, aNim, getter_AddRefs(child)); |
michael@0 | 1385 | |
michael@0 | 1386 | // Child may be null if this was a comment for example and can just be ignored. |
michael@0 | 1387 | if (child) { |
michael@0 | 1388 | content->AppendChildTo(child, false); |
michael@0 | 1389 | } |
michael@0 | 1390 | } |
michael@0 | 1391 | |
michael@0 | 1392 | content.swap(*aContent); |
michael@0 | 1393 | return NS_OK; |
michael@0 | 1394 | } |
michael@0 | 1395 | |
michael@0 | 1396 | // This structure holds information about a forwarded attribute that needs to be |
michael@0 | 1397 | // written out. This is used because we need several fields passed within the |
michael@0 | 1398 | // enumeration closure. |
michael@0 | 1399 | struct WriteAttributeData |
michael@0 | 1400 | { |
michael@0 | 1401 | nsXBLPrototypeBinding* binding; |
michael@0 | 1402 | nsIObjectOutputStream* stream; |
michael@0 | 1403 | nsIContent* content; |
michael@0 | 1404 | int32_t srcNamespace; |
michael@0 | 1405 | |
michael@0 | 1406 | WriteAttributeData(nsXBLPrototypeBinding* aBinding, |
michael@0 | 1407 | nsIObjectOutputStream* aStream, |
michael@0 | 1408 | nsIContent* aContent) |
michael@0 | 1409 | : binding(aBinding), stream(aStream), content(aContent) |
michael@0 | 1410 | { } |
michael@0 | 1411 | }; |
michael@0 | 1412 | |
michael@0 | 1413 | static PLDHashOperator |
michael@0 | 1414 | WriteAttribute(nsISupports* aKey, nsXBLAttributeEntry* aEntry, void* aClosure) |
michael@0 | 1415 | { |
michael@0 | 1416 | WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure); |
michael@0 | 1417 | nsIObjectOutputStream* stream = data->stream; |
michael@0 | 1418 | const int32_t srcNamespace = data->srcNamespace; |
michael@0 | 1419 | |
michael@0 | 1420 | do { |
michael@0 | 1421 | if (aEntry->GetElement() == data->content) { |
michael@0 | 1422 | data->binding->WriteNamespace(stream, srcNamespace); |
michael@0 | 1423 | stream->WriteWStringZ(nsDependentAtomString(aEntry->GetSrcAttribute()).get()); |
michael@0 | 1424 | data->binding->WriteNamespace(stream, aEntry->GetDstNameSpace()); |
michael@0 | 1425 | stream->WriteWStringZ(nsDependentAtomString(aEntry->GetDstAttribute()).get()); |
michael@0 | 1426 | } |
michael@0 | 1427 | |
michael@0 | 1428 | aEntry = aEntry->GetNext(); |
michael@0 | 1429 | } while (aEntry); |
michael@0 | 1430 | |
michael@0 | 1431 | return PL_DHASH_NEXT; |
michael@0 | 1432 | } |
michael@0 | 1433 | |
michael@0 | 1434 | // WriteAttributeNS is the callback to enumerate over the attribute |
michael@0 | 1435 | // forwarding entries. Since these are stored in a hash of hashes, |
michael@0 | 1436 | // we need to iterate over the inner hashes, calling WriteAttribute |
michael@0 | 1437 | // to do the actual work. |
michael@0 | 1438 | static PLDHashOperator |
michael@0 | 1439 | WriteAttributeNS(const uint32_t &aNamespace, |
michael@0 | 1440 | nsXBLPrototypeBinding::InnerAttributeTable* aXBLAttributes, |
michael@0 | 1441 | void* aClosure) |
michael@0 | 1442 | { |
michael@0 | 1443 | WriteAttributeData* data = static_cast<WriteAttributeData *>(aClosure); |
michael@0 | 1444 | data->srcNamespace = aNamespace; |
michael@0 | 1445 | aXBLAttributes->EnumerateRead(WriteAttribute, data); |
michael@0 | 1446 | |
michael@0 | 1447 | return PL_DHASH_NEXT; |
michael@0 | 1448 | } |
michael@0 | 1449 | |
michael@0 | 1450 | nsresult |
michael@0 | 1451 | nsXBLPrototypeBinding::WriteContentNode(nsIObjectOutputStream* aStream, |
michael@0 | 1452 | nsIContent* aNode) |
michael@0 | 1453 | { |
michael@0 | 1454 | nsresult rv; |
michael@0 | 1455 | |
michael@0 | 1456 | if (!aNode->IsElement()) { |
michael@0 | 1457 | // Text is writen out as a single byte for the type, followed by the text. |
michael@0 | 1458 | uint8_t type = XBLBinding_Serialize_NoContent; |
michael@0 | 1459 | switch (aNode->NodeType()) { |
michael@0 | 1460 | case nsIDOMNode::TEXT_NODE: |
michael@0 | 1461 | type = XBLBinding_Serialize_TextNode; |
michael@0 | 1462 | break; |
michael@0 | 1463 | case nsIDOMNode::CDATA_SECTION_NODE: |
michael@0 | 1464 | type = XBLBinding_Serialize_CDATANode; |
michael@0 | 1465 | break; |
michael@0 | 1466 | case nsIDOMNode::COMMENT_NODE: |
michael@0 | 1467 | type = XBLBinding_Serialize_CommentNode; |
michael@0 | 1468 | break; |
michael@0 | 1469 | default: |
michael@0 | 1470 | break; |
michael@0 | 1471 | } |
michael@0 | 1472 | |
michael@0 | 1473 | rv = aStream->Write8(type); |
michael@0 | 1474 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1475 | |
michael@0 | 1476 | nsAutoString content; |
michael@0 | 1477 | aNode->GetText()->AppendTo(content); |
michael@0 | 1478 | return aStream->WriteWStringZ(content.get()); |
michael@0 | 1479 | } |
michael@0 | 1480 | |
michael@0 | 1481 | // Otherwise, this is an element. |
michael@0 | 1482 | |
michael@0 | 1483 | // Write the namespace id followed by the tag name |
michael@0 | 1484 | rv = WriteNamespace(aStream, aNode->GetNameSpaceID()); |
michael@0 | 1485 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1486 | |
michael@0 | 1487 | nsAutoString prefixStr; |
michael@0 | 1488 | aNode->NodeInfo()->GetPrefix(prefixStr); |
michael@0 | 1489 | rv = aStream->WriteWStringZ(prefixStr.get()); |
michael@0 | 1490 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1491 | |
michael@0 | 1492 | rv = aStream->WriteWStringZ(nsDependentAtomString(aNode->Tag()).get()); |
michael@0 | 1493 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1494 | |
michael@0 | 1495 | // Write attributes |
michael@0 | 1496 | uint32_t count = aNode->GetAttrCount(); |
michael@0 | 1497 | rv = aStream->Write32(count); |
michael@0 | 1498 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1499 | |
michael@0 | 1500 | uint32_t i; |
michael@0 | 1501 | for (i = 0; i < count; i++) { |
michael@0 | 1502 | // Write out the namespace id, the namespace prefix, the local tag name, |
michael@0 | 1503 | // and the value, in that order. |
michael@0 | 1504 | |
michael@0 | 1505 | const nsAttrName* attr = aNode->GetAttrNameAt(i); |
michael@0 | 1506 | |
michael@0 | 1507 | // XXXndeakin don't write out xbl:inherits? |
michael@0 | 1508 | int32_t namespaceID = attr->NamespaceID(); |
michael@0 | 1509 | rv = WriteNamespace(aStream, namespaceID); |
michael@0 | 1510 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1511 | |
michael@0 | 1512 | nsAutoString prefixStr; |
michael@0 | 1513 | nsIAtom* prefix = attr->GetPrefix(); |
michael@0 | 1514 | if (prefix) |
michael@0 | 1515 | prefix->ToString(prefixStr); |
michael@0 | 1516 | rv = aStream->WriteWStringZ(prefixStr.get()); |
michael@0 | 1517 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1518 | |
michael@0 | 1519 | rv = aStream->WriteWStringZ(nsDependentAtomString(attr->LocalName()).get()); |
michael@0 | 1520 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1521 | |
michael@0 | 1522 | nsAutoString val; |
michael@0 | 1523 | aNode->GetAttr(attr->NamespaceID(), attr->LocalName(), val); |
michael@0 | 1524 | rv = aStream->WriteWStringZ(val.get()); |
michael@0 | 1525 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1526 | } |
michael@0 | 1527 | |
michael@0 | 1528 | // Write out the attribute fowarding information |
michael@0 | 1529 | if (mAttributeTable) { |
michael@0 | 1530 | WriteAttributeData data(this, aStream, aNode); |
michael@0 | 1531 | mAttributeTable->EnumerateRead(WriteAttributeNS, &data); |
michael@0 | 1532 | } |
michael@0 | 1533 | rv = aStream->Write8(XBLBinding_Serialize_NoMoreAttributes); |
michael@0 | 1534 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1535 | |
michael@0 | 1536 | // Finally, write out the child nodes. |
michael@0 | 1537 | count = aNode->GetChildCount(); |
michael@0 | 1538 | rv = aStream->Write32(count); |
michael@0 | 1539 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1540 | |
michael@0 | 1541 | for (i = 0; i < count; i++) { |
michael@0 | 1542 | rv = WriteContentNode(aStream, aNode->GetChildAt(i)); |
michael@0 | 1543 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1544 | } |
michael@0 | 1545 | |
michael@0 | 1546 | return NS_OK; |
michael@0 | 1547 | } |
michael@0 | 1548 | |
michael@0 | 1549 | nsresult |
michael@0 | 1550 | nsXBLPrototypeBinding::ReadNamespace(nsIObjectInputStream* aStream, |
michael@0 | 1551 | int32_t& aNameSpaceID) |
michael@0 | 1552 | { |
michael@0 | 1553 | uint8_t namespaceID; |
michael@0 | 1554 | nsresult rv = aStream->Read8(&namespaceID); |
michael@0 | 1555 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1556 | |
michael@0 | 1557 | if (namespaceID == XBLBinding_Serialize_CustomNamespace) { |
michael@0 | 1558 | nsAutoString namesp; |
michael@0 | 1559 | rv = aStream->ReadString(namesp); |
michael@0 | 1560 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1561 | |
michael@0 | 1562 | nsContentUtils::NameSpaceManager()->RegisterNameSpace(namesp, aNameSpaceID); |
michael@0 | 1563 | } |
michael@0 | 1564 | else { |
michael@0 | 1565 | aNameSpaceID = namespaceID; |
michael@0 | 1566 | } |
michael@0 | 1567 | |
michael@0 | 1568 | return NS_OK; |
michael@0 | 1569 | } |
michael@0 | 1570 | |
michael@0 | 1571 | nsresult |
michael@0 | 1572 | nsXBLPrototypeBinding::WriteNamespace(nsIObjectOutputStream* aStream, |
michael@0 | 1573 | int32_t aNameSpaceID) |
michael@0 | 1574 | { |
michael@0 | 1575 | // Namespaces are stored as a single byte id for well-known namespaces. |
michael@0 | 1576 | // This saves time and space as other namespaces aren't very common in |
michael@0 | 1577 | // XBL. If another namespace is used however, the namespace id will be |
michael@0 | 1578 | // XBLBinding_Serialize_CustomNamespace and the string namespace written |
michael@0 | 1579 | // out directly afterwards. |
michael@0 | 1580 | nsresult rv; |
michael@0 | 1581 | |
michael@0 | 1582 | if (aNameSpaceID <= kNameSpaceID_LastBuiltin) { |
michael@0 | 1583 | rv = aStream->Write8((int8_t)aNameSpaceID); |
michael@0 | 1584 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1585 | } |
michael@0 | 1586 | else { |
michael@0 | 1587 | rv = aStream->Write8(XBLBinding_Serialize_CustomNamespace); |
michael@0 | 1588 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 1589 | |
michael@0 | 1590 | nsAutoString namesp; |
michael@0 | 1591 | nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, namesp); |
michael@0 | 1592 | aStream->WriteWStringZ(namesp.get()); |
michael@0 | 1593 | } |
michael@0 | 1594 | |
michael@0 | 1595 | return NS_OK; |
michael@0 | 1596 | } |
michael@0 | 1597 | |
michael@0 | 1598 | |
michael@0 | 1599 | bool CheckTagNameWhiteList(int32_t aNameSpaceID, nsIAtom *aTagName) |
michael@0 | 1600 | { |
michael@0 | 1601 | static nsIContent::AttrValuesArray kValidXULTagNames[] = { |
michael@0 | 1602 | &nsGkAtoms::autorepeatbutton, &nsGkAtoms::box, &nsGkAtoms::browser, |
michael@0 | 1603 | &nsGkAtoms::button, &nsGkAtoms::hbox, &nsGkAtoms::image, &nsGkAtoms::menu, |
michael@0 | 1604 | &nsGkAtoms::menubar, &nsGkAtoms::menuitem, &nsGkAtoms::menupopup, |
michael@0 | 1605 | &nsGkAtoms::row, &nsGkAtoms::slider, &nsGkAtoms::spacer, |
michael@0 | 1606 | &nsGkAtoms::splitter, &nsGkAtoms::text, &nsGkAtoms::tree, nullptr}; |
michael@0 | 1607 | |
michael@0 | 1608 | uint32_t i; |
michael@0 | 1609 | if (aNameSpaceID == kNameSpaceID_XUL) { |
michael@0 | 1610 | for (i = 0; kValidXULTagNames[i]; ++i) { |
michael@0 | 1611 | if (aTagName == *(kValidXULTagNames[i])) { |
michael@0 | 1612 | return true; |
michael@0 | 1613 | } |
michael@0 | 1614 | } |
michael@0 | 1615 | } |
michael@0 | 1616 | else if (aNameSpaceID == kNameSpaceID_SVG && |
michael@0 | 1617 | aTagName == nsGkAtoms::generic_) { |
michael@0 | 1618 | return true; |
michael@0 | 1619 | } |
michael@0 | 1620 | |
michael@0 | 1621 | return false; |
michael@0 | 1622 | } |
michael@0 | 1623 | |
michael@0 | 1624 | nsresult |
michael@0 | 1625 | nsXBLPrototypeBinding::ResolveBaseBinding() |
michael@0 | 1626 | { |
michael@0 | 1627 | if (mCheckedBaseProto) |
michael@0 | 1628 | return NS_OK; |
michael@0 | 1629 | mCheckedBaseProto = true; |
michael@0 | 1630 | |
michael@0 | 1631 | nsCOMPtr<nsIDocument> doc = mXBLDocInfoWeak->GetDocument(); |
michael@0 | 1632 | |
michael@0 | 1633 | // Check for the presence of 'extends' and 'display' attributes |
michael@0 | 1634 | nsAutoString display, extends; |
michael@0 | 1635 | mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::extends, extends); |
michael@0 | 1636 | if (extends.IsEmpty()) |
michael@0 | 1637 | return NS_OK; |
michael@0 | 1638 | |
michael@0 | 1639 | mBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::display, display); |
michael@0 | 1640 | bool hasDisplay = !display.IsEmpty(); |
michael@0 | 1641 | |
michael@0 | 1642 | nsAutoString value(extends); |
michael@0 | 1643 | |
michael@0 | 1644 | // Now slice 'em up to see what we've got. |
michael@0 | 1645 | nsAutoString prefix; |
michael@0 | 1646 | int32_t offset; |
michael@0 | 1647 | if (hasDisplay) { |
michael@0 | 1648 | offset = display.FindChar(':'); |
michael@0 | 1649 | if (-1 != offset) { |
michael@0 | 1650 | display.Left(prefix, offset); |
michael@0 | 1651 | display.Cut(0, offset+1); |
michael@0 | 1652 | } |
michael@0 | 1653 | } |
michael@0 | 1654 | else { |
michael@0 | 1655 | offset = extends.FindChar(':'); |
michael@0 | 1656 | if (-1 != offset) { |
michael@0 | 1657 | extends.Left(prefix, offset); |
michael@0 | 1658 | extends.Cut(0, offset+1); |
michael@0 | 1659 | display = extends; |
michael@0 | 1660 | } |
michael@0 | 1661 | } |
michael@0 | 1662 | |
michael@0 | 1663 | nsAutoString nameSpace; |
michael@0 | 1664 | |
michael@0 | 1665 | if (!prefix.IsEmpty()) { |
michael@0 | 1666 | mBinding->LookupNamespaceURI(prefix, nameSpace); |
michael@0 | 1667 | if (!nameSpace.IsEmpty()) { |
michael@0 | 1668 | int32_t nameSpaceID = |
michael@0 | 1669 | nsContentUtils::NameSpaceManager()->GetNameSpaceID(nameSpace); |
michael@0 | 1670 | |
michael@0 | 1671 | nsCOMPtr<nsIAtom> tagName = do_GetAtom(display); |
michael@0 | 1672 | // Check the white list |
michael@0 | 1673 | if (!CheckTagNameWhiteList(nameSpaceID, tagName)) { |
michael@0 | 1674 | const char16_t* params[] = { display.get() }; |
michael@0 | 1675 | nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, |
michael@0 | 1676 | NS_LITERAL_CSTRING("XBL"), nullptr, |
michael@0 | 1677 | nsContentUtils::eXBL_PROPERTIES, |
michael@0 | 1678 | "InvalidExtendsBinding", |
michael@0 | 1679 | params, ArrayLength(params), |
michael@0 | 1680 | doc->GetDocumentURI()); |
michael@0 | 1681 | NS_ASSERTION(!nsXBLService::IsChromeOrResourceURI(doc->GetDocumentURI()), |
michael@0 | 1682 | "Invalid extends value"); |
michael@0 | 1683 | return NS_ERROR_ILLEGAL_VALUE; |
michael@0 | 1684 | } |
michael@0 | 1685 | |
michael@0 | 1686 | SetBaseTag(nameSpaceID, tagName); |
michael@0 | 1687 | } |
michael@0 | 1688 | } |
michael@0 | 1689 | |
michael@0 | 1690 | if (hasDisplay || nameSpace.IsEmpty()) { |
michael@0 | 1691 | mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::extends, false); |
michael@0 | 1692 | mBinding->UnsetAttr(kNameSpaceID_None, nsGkAtoms::display, false); |
michael@0 | 1693 | |
michael@0 | 1694 | return NS_NewURI(getter_AddRefs(mBaseBindingURI), value, |
michael@0 | 1695 | doc->GetDocumentCharacterSet().get(), |
michael@0 | 1696 | doc->GetDocBaseURI()); |
michael@0 | 1697 | } |
michael@0 | 1698 | |
michael@0 | 1699 | return NS_OK; |
michael@0 | 1700 | } |