/src/mozilla-central/dom/xul/nsXULElement.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "nsCOMPtr.h" |
7 | | #include "nsDOMCID.h" |
8 | | #include "nsError.h" |
9 | | #include "nsDOMString.h" |
10 | | #include "nsAtom.h" |
11 | | #include "nsIBaseWindow.h" |
12 | | #include "nsIDOMEventListener.h" |
13 | | #include "nsIDOMXULCommandDispatcher.h" |
14 | | #include "nsIDOMXULSelectCntrlItemEl.h" |
15 | | #include "nsIDocument.h" |
16 | | #include "mozilla/ClearOnShutdown.h" |
17 | | #include "mozilla/EventListenerManager.h" |
18 | | #include "mozilla/EventStateManager.h" |
19 | | #include "mozilla/EventStates.h" |
20 | | #include "mozilla/DeclarationBlock.h" |
21 | | #include "js/CompilationAndEvaluation.h" |
22 | | #include "js/SourceBufferHolder.h" |
23 | | #include "nsFocusManager.h" |
24 | | #include "nsHTMLStyleSheet.h" |
25 | | #include "nsNameSpaceManager.h" |
26 | | #include "nsIObjectInputStream.h" |
27 | | #include "nsIObjectOutputStream.h" |
28 | | #include "nsIPresShell.h" |
29 | | #include "nsIPrincipal.h" |
30 | | #include "nsIScriptContext.h" |
31 | | #include "nsIScriptError.h" |
32 | | #include "nsIScriptSecurityManager.h" |
33 | | #include "nsIServiceManager.h" |
34 | | #include "nsIURL.h" |
35 | | #include "nsViewManager.h" |
36 | | #include "nsIWidget.h" |
37 | | #include "nsLayoutCID.h" |
38 | | #include "nsContentCID.h" |
39 | | #include "mozilla/dom/Event.h" |
40 | | #include "nsStyleConsts.h" |
41 | | #include "nsString.h" |
42 | | #include "nsXULControllers.h" |
43 | | #include "nsIBoxObject.h" |
44 | | #include "nsPIBoxObject.h" |
45 | | #include "XULDocument.h" |
46 | | #include "nsXULPopupListener.h" |
47 | | #include "nsContentUtils.h" |
48 | | #include "nsContentList.h" |
49 | | #include "mozilla/InternalMutationEvent.h" |
50 | | #include "mozilla/MouseEvents.h" |
51 | | #include "nsPIDOMWindow.h" |
52 | | #include "nsJSPrincipals.h" |
53 | | #include "nsDOMAttributeMap.h" |
54 | | #include "nsGkAtoms.h" |
55 | | #include "nsNodeUtils.h" |
56 | | #include "nsFrameLoader.h" |
57 | | #include "mozilla/Logging.h" |
58 | | #include "nsIControllers.h" |
59 | | #include "nsAttrValueOrString.h" |
60 | | #include "nsAttrValueInlines.h" |
61 | | #include "mozilla/Attributes.h" |
62 | | #include "nsIController.h" |
63 | | #include "nsQueryObject.h" |
64 | | #include <algorithm> |
65 | | #include "nsIDOMChromeWindow.h" |
66 | | |
67 | | #include "nsReadableUtils.h" |
68 | | #include "nsIFrame.h" |
69 | | #include "nsNodeInfoManager.h" |
70 | | #include "nsXBLBinding.h" |
71 | | #include "nsXULTooltipListener.h" |
72 | | #include "mozilla/EventDispatcher.h" |
73 | | #include "mozAutoDocUpdate.h" |
74 | | #include "nsCCUncollectableMarker.h" |
75 | | #include "nsICSSDeclaration.h" |
76 | | #include "nsLayoutUtils.h" |
77 | | #include "XULFrameElement.h" |
78 | | #include "XULMenuElement.h" |
79 | | #include "XULPopupElement.h" |
80 | | #include "XULScrollElement.h" |
81 | | |
82 | | #include "mozilla/dom/XULElementBinding.h" |
83 | | #include "mozilla/dom/BoxObject.h" |
84 | | #include "mozilla/dom/MouseEventBinding.h" |
85 | | #include "mozilla/dom/MutationEventBinding.h" |
86 | | #include "mozilla/dom/XULCommandEvent.h" |
87 | | |
88 | | using namespace mozilla; |
89 | | using namespace mozilla::dom; |
90 | | |
91 | | #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING |
92 | | uint32_t nsXULPrototypeAttribute::gNumElements; |
93 | | uint32_t nsXULPrototypeAttribute::gNumAttributes; |
94 | | uint32_t nsXULPrototypeAttribute::gNumCacheTests; |
95 | | uint32_t nsXULPrototypeAttribute::gNumCacheHits; |
96 | | uint32_t nsXULPrototypeAttribute::gNumCacheSets; |
97 | | uint32_t nsXULPrototypeAttribute::gNumCacheFills; |
98 | | #endif |
99 | | |
100 | 0 | #define NS_DISPATCH_XUL_COMMAND (1 << 0) |
101 | | |
102 | | //---------------------------------------------------------------------- |
103 | | // nsXULElement |
104 | | // |
105 | | |
106 | | nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
107 | | : nsStyledElement(std::move(aNodeInfo)), |
108 | | mBindingParent(nullptr) |
109 | 0 | { |
110 | 0 | XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements); |
111 | 0 |
|
112 | 0 | // We may be READWRITE by default; check. |
113 | 0 | if (IsReadWriteTextElement()) { |
114 | 0 | AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); |
115 | 0 | RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY); |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | | nsXULElement::~nsXULElement() |
120 | 0 | { |
121 | 0 | } |
122 | | |
123 | | void |
124 | | nsXULElement::MaybeUpdatePrivateLifetime() |
125 | 0 | { |
126 | 0 | if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::windowtype, |
127 | 0 | NS_LITERAL_STRING("navigator:browser"), |
128 | 0 | eCaseMatters)) { |
129 | 0 | return; |
130 | 0 | } |
131 | 0 | |
132 | 0 | nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow(); |
133 | 0 | nsCOMPtr<nsIDocShell> docShell = win ? win->GetDocShell() : nullptr; |
134 | 0 | if (docShell) { |
135 | 0 | docShell->SetAffectPrivateSessionLifetime(false); |
136 | 0 | } |
137 | 0 | } |
138 | | |
139 | | /* static */ |
140 | | nsXULElement* NS_NewBasicXULElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
141 | 0 | { |
142 | 0 | return new nsXULElement(std::move(aNodeInfo)); |
143 | 0 | } |
144 | | |
145 | | /* static */ |
146 | | nsXULElement* nsXULElement::Construct(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
147 | 0 | { |
148 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo; |
149 | 0 | if (nodeInfo->Equals(nsGkAtoms::menupopup) || |
150 | 0 | nodeInfo->Equals(nsGkAtoms::popup) || |
151 | 0 | nodeInfo->Equals(nsGkAtoms::panel) || |
152 | 0 | nodeInfo->Equals(nsGkAtoms::tooltip)) { |
153 | 0 | return NS_NewXULPopupElement(nodeInfo.forget()); |
154 | 0 | } |
155 | 0 | |
156 | 0 | if (nodeInfo->Equals(nsGkAtoms::iframe) || |
157 | 0 | nodeInfo->Equals(nsGkAtoms::browser) || |
158 | 0 | nodeInfo->Equals(nsGkAtoms::editor)) { |
159 | 0 | return new XULFrameElement(nodeInfo.forget()); |
160 | 0 | } |
161 | 0 | |
162 | 0 | if (nodeInfo->Equals(nsGkAtoms::menu) || |
163 | 0 | nodeInfo->Equals(nsGkAtoms::menulist)) { |
164 | 0 | return new XULMenuElement(nodeInfo.forget()); |
165 | 0 | } |
166 | 0 | |
167 | 0 | if (nodeInfo->Equals(nsGkAtoms::scrollbox)) { |
168 | 0 | return new XULScrollElement(nodeInfo.forget()); |
169 | 0 | } |
170 | 0 | |
171 | 0 | return NS_NewBasicXULElement(nodeInfo.forget()); |
172 | 0 | } |
173 | | |
174 | | /* static */ |
175 | | already_AddRefed<nsXULElement> |
176 | | nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype, |
177 | | mozilla::dom::NodeInfo *aNodeInfo, |
178 | | bool aIsScriptable, |
179 | | bool aIsRoot) |
180 | 0 | { |
181 | 0 | RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; |
182 | 0 | nsCOMPtr<Element> baseElement; |
183 | 0 | NS_NewXULElement(getter_AddRefs(baseElement), |
184 | 0 | ni.forget(), |
185 | 0 | dom::FROM_PARSER_NETWORK, |
186 | 0 | aPrototype->mIsAtom); |
187 | 0 |
|
188 | 0 | if (baseElement) { |
189 | 0 | nsXULElement* element = FromNode(baseElement); |
190 | 0 |
|
191 | 0 | if (aPrototype->mHasIdAttribute) { |
192 | 0 | element->SetHasID(); |
193 | 0 | } |
194 | 0 | if (aPrototype->mHasClassAttribute) { |
195 | 0 | element->SetMayHaveClass(); |
196 | 0 | } |
197 | 0 | if (aPrototype->mHasStyleAttribute) { |
198 | 0 | element->SetMayHaveStyle(); |
199 | 0 | } |
200 | 0 |
|
201 | 0 | element->MakeHeavyweight(aPrototype); |
202 | 0 | if (aIsScriptable) { |
203 | 0 | // Check each attribute on the prototype to see if we need to do |
204 | 0 | // any additional processing and hookup that would otherwise be |
205 | 0 | // done 'automagically' by SetAttr(). |
206 | 0 | for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) { |
207 | 0 | element->AddListenerFor(aPrototype->mAttributes[i].mName); |
208 | 0 | } |
209 | 0 | } |
210 | 0 |
|
211 | 0 | if (aIsRoot && aPrototype->mNodeInfo->Equals(nsGkAtoms::window)) { |
212 | 0 | for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) { |
213 | 0 | if (aPrototype->mAttributes[i].mName.Equals(nsGkAtoms::windowtype)) { |
214 | 0 | element->MaybeUpdatePrivateLifetime(); |
215 | 0 | } |
216 | 0 | } |
217 | 0 | } |
218 | 0 |
|
219 | 0 | return baseElement.forget().downcast<nsXULElement>(); |
220 | 0 | } |
221 | 0 |
|
222 | 0 | return nullptr; |
223 | 0 | } |
224 | | |
225 | | nsresult |
226 | | nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype, |
227 | | nsIDocument* aDocument, |
228 | | bool aIsScriptable, |
229 | | bool aIsRoot, |
230 | | Element** aResult) |
231 | 0 | { |
232 | 0 | // Create an nsXULElement from a prototype |
233 | 0 | MOZ_ASSERT(aPrototype != nullptr, "null ptr"); |
234 | 0 | if (! aPrototype) |
235 | 0 | return NS_ERROR_NULL_POINTER; |
236 | 0 | |
237 | 0 | MOZ_ASSERT(aResult != nullptr, "null ptr"); |
238 | 0 | if (! aResult) |
239 | 0 | return NS_ERROR_NULL_POINTER; |
240 | 0 | |
241 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
242 | 0 | if (aDocument) { |
243 | 0 | mozilla::dom::NodeInfo* ni = aPrototype->mNodeInfo; |
244 | 0 | nodeInfo = aDocument->NodeInfoManager()-> |
245 | 0 | GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(), ni->NamespaceID(), |
246 | 0 | ELEMENT_NODE); |
247 | 0 | } else { |
248 | 0 | nodeInfo = aPrototype->mNodeInfo; |
249 | 0 | } |
250 | 0 |
|
251 | 0 | RefPtr<nsXULElement> element = CreateFromPrototype(aPrototype, nodeInfo, |
252 | 0 | aIsScriptable, aIsRoot); |
253 | 0 | element.forget(aResult); |
254 | 0 |
|
255 | 0 | return NS_OK; |
256 | 0 | } |
257 | | |
258 | | nsresult |
259 | | NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, |
260 | | FromParser aFromParser, nsAtom* aIsAtom, |
261 | | mozilla::dom::CustomElementDefinition* aDefinition) |
262 | 0 | { |
263 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo; |
264 | 0 |
|
265 | 0 | MOZ_ASSERT(nodeInfo, "need nodeinfo for non-proto Create"); |
266 | 0 |
|
267 | 0 | NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XUL), |
268 | 0 | "Trying to create XUL elements that don't have the XUL namespace"); |
269 | 0 |
|
270 | 0 | nsIDocument* doc = nodeInfo->GetDocument(); |
271 | 0 | if (doc && !doc->AllowXULXBL()) { |
272 | 0 | return NS_ERROR_NOT_AVAILABLE; |
273 | 0 | } |
274 | 0 | |
275 | 0 | return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIsAtom, aDefinition); |
276 | 0 | } |
277 | | |
278 | | void |
279 | | NS_TrustedNewXULElement(Element** aResult, |
280 | | already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
281 | 0 | { |
282 | 0 | RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; |
283 | 0 | MOZ_ASSERT(ni, "need nodeinfo for non-proto Create"); |
284 | 0 |
|
285 | 0 | // Create an nsXULElement with the specified namespace and tag. |
286 | 0 | NS_ADDREF(*aResult = nsXULElement::Construct(ni.forget())); |
287 | 0 | } |
288 | | |
289 | | //---------------------------------------------------------------------- |
290 | | // nsISupports interface |
291 | | |
292 | | NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement) |
293 | | |
294 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement, |
295 | 0 | nsStyledElement) |
296 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingParent); |
297 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
298 | | |
299 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULElement, |
300 | 0 | nsStyledElement) |
301 | 0 | // Why aren't we unlinking the prototype? |
302 | 0 | tmp->ClearHasID(); |
303 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mBindingParent); |
304 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
305 | | |
306 | | NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement) |
307 | | NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement) |
308 | | |
309 | 0 | NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement) |
310 | 0 | NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE |
311 | 0 |
|
312 | 0 | nsCOMPtr<nsISupports> iface = |
313 | 0 | CustomElementRegistry::CallGetCustomInterface(this, aIID); |
314 | 0 | if (iface) { |
315 | 0 | iface->QueryInterface(aIID, aInstancePtr); |
316 | 0 | if (*aInstancePtr) { |
317 | 0 | return NS_OK; |
318 | 0 | } |
319 | 0 | } |
320 | 0 | |
321 | 0 | NS_INTERFACE_MAP_END_INHERITING(Element) |
322 | 0 |
|
323 | 0 | //---------------------------------------------------------------------- |
324 | 0 | // nsINode interface |
325 | 0 |
|
326 | 0 | nsresult |
327 | 0 | nsXULElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const |
328 | 0 | { |
329 | 0 | *aResult = nullptr; |
330 | 0 |
|
331 | 0 | RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; |
332 | 0 | RefPtr<nsXULElement> element = Construct(ni.forget()); |
333 | 0 |
|
334 | 0 | nsresult rv = element->mAttrs.EnsureCapacityToClone(mAttrs); |
335 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
336 | 0 |
|
337 | 0 | // Note that we're _not_ copying mControllers. |
338 | 0 |
|
339 | 0 | uint32_t count = mAttrs.AttrCount(); |
340 | 0 | rv = NS_OK; |
341 | 0 | for (uint32_t i = 0; i < count; ++i) { |
342 | 0 | const nsAttrName* originalName = mAttrs.AttrNameAt(i); |
343 | 0 | const nsAttrValue* originalValue = mAttrs.AttrAt(i); |
344 | 0 | nsAttrValue attrValue; |
345 | 0 |
|
346 | 0 | // Style rules need to be cloned. |
347 | 0 | if (originalValue->Type() == nsAttrValue::eCSSDeclaration) { |
348 | 0 | DeclarationBlock* decl = originalValue->GetCSSDeclarationValue(); |
349 | 0 | RefPtr<DeclarationBlock> declClone = decl->Clone(); |
350 | 0 |
|
351 | 0 | nsString stringValue; |
352 | 0 | originalValue->ToString(stringValue); |
353 | 0 |
|
354 | 0 | attrValue.SetTo(declClone.forget(), &stringValue); |
355 | 0 | } else { |
356 | 0 | attrValue.SetTo(*originalValue); |
357 | 0 | } |
358 | 0 |
|
359 | 0 | bool oldValueSet; |
360 | 0 | if (originalName->IsAtom()) { |
361 | 0 | rv = element->mAttrs.SetAndSwapAttr(originalName->Atom(), |
362 | 0 | attrValue, |
363 | 0 | &oldValueSet); |
364 | 0 | } else { |
365 | 0 | rv = element->mAttrs.SetAndSwapAttr(originalName->NodeInfo(), |
366 | 0 | attrValue, |
367 | 0 | &oldValueSet); |
368 | 0 | } |
369 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
370 | 0 | element->AddListenerFor(*originalName); |
371 | 0 | if (originalName->Equals(nsGkAtoms::id) && |
372 | 0 | !originalValue->IsEmptyString()) { |
373 | 0 | element->SetHasID(); |
374 | 0 | } |
375 | 0 | if (originalName->Equals(nsGkAtoms::_class)) { |
376 | 0 | element->SetMayHaveClass(); |
377 | 0 | } |
378 | 0 | if (originalName->Equals(nsGkAtoms::style)) { |
379 | 0 | element->SetMayHaveStyle(); |
380 | 0 | } |
381 | 0 | } |
382 | 0 |
|
383 | 0 | element.forget(aResult); |
384 | 0 | return rv; |
385 | 0 | } |
386 | | |
387 | | //---------------------------------------------------------------------- |
388 | | |
389 | | EventListenerManager* |
390 | | nsXULElement::GetEventListenerManagerForAttr(nsAtom* aAttrName, bool* aDefer) |
391 | 0 | { |
392 | 0 | // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() |
393 | 0 | // here, override BindToTree for those classes and munge event |
394 | 0 | // listeners there? |
395 | 0 | nsIDocument* doc = OwnerDoc(); |
396 | 0 |
|
397 | 0 | nsPIDOMWindowInner *window; |
398 | 0 | Element *root = doc->GetRootElement(); |
399 | 0 | if ((!root || root == this) && (window = doc->GetInnerWindow())) { |
400 | 0 |
|
401 | 0 | nsCOMPtr<EventTarget> piTarget = do_QueryInterface(window); |
402 | 0 |
|
403 | 0 | *aDefer = false; |
404 | 0 | return piTarget->GetOrCreateListenerManager(); |
405 | 0 | } |
406 | 0 | |
407 | 0 | return nsStyledElement::GetEventListenerManagerForAttr(aAttrName, aDefer); |
408 | 0 | } |
409 | | |
410 | | // returns true if the element is not a list |
411 | | static bool IsNonList(mozilla::dom::NodeInfo* aNodeInfo) |
412 | 0 | { |
413 | 0 | return !aNodeInfo->Equals(nsGkAtoms::tree) && |
414 | 0 | !aNodeInfo->Equals(nsGkAtoms::richlistbox); |
415 | 0 | } |
416 | | |
417 | | bool |
418 | | nsXULElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse) |
419 | 0 | { |
420 | 0 | /* |
421 | 0 | * Returns true if an element may be focused, and false otherwise. The inout |
422 | 0 | * argument aTabIndex will be set to the tab order index to be used; -1 for |
423 | 0 | * elements that should not be part of the tab order and a greater value to |
424 | 0 | * indicate its tab order. |
425 | 0 | * |
426 | 0 | * Confusingly, the supplied value for the aTabIndex argument may indicate |
427 | 0 | * whether the element may be focused as a result of the -moz-user-focus |
428 | 0 | * property, where -1 means no and 0 means yes. |
429 | 0 | * |
430 | 0 | * For controls, the element cannot be focused and is not part of the tab |
431 | 0 | * order if it is disabled. |
432 | 0 | * |
433 | 0 | * Controls (those that implement nsIDOMXULControlElement): |
434 | 0 | * *aTabIndex = -1 no tabindex Not focusable or tabbable |
435 | 0 | * *aTabIndex = -1 tabindex="-1" Not focusable or tabbable |
436 | 0 | * *aTabIndex = -1 tabindex=">=0" Focusable and tabbable |
437 | 0 | * *aTabIndex >= 0 no tabindex Focusable and tabbable |
438 | 0 | * *aTabIndex >= 0 tabindex="-1" Focusable but not tabbable |
439 | 0 | * *aTabIndex >= 0 tabindex=">=0" Focusable and tabbable |
440 | 0 | * Non-controls: |
441 | 0 | * *aTabIndex = -1 Not focusable or tabbable |
442 | 0 | * *aTabIndex >= 0 Focusable and tabbable |
443 | 0 | * |
444 | 0 | * If aTabIndex is null, then the tabindex is not computed, and |
445 | 0 | * true is returned for non-disabled controls and false otherwise. |
446 | 0 | */ |
447 | 0 |
|
448 | 0 | // elements are not focusable by default |
449 | 0 | bool shouldFocus = false; |
450 | 0 |
|
451 | | #ifdef XP_MACOSX |
452 | | // on Mac, mouse interactions only focus the element if it's a list, |
453 | | // or if it's a remote target, since the remote target must handle |
454 | | // the focus. |
455 | | if (aWithMouse && |
456 | | IsNonList(mNodeInfo) && |
457 | | !EventStateManager::IsRemoteTarget(this)) |
458 | | { |
459 | | return false; |
460 | | } |
461 | | #endif |
462 | |
|
463 | 0 | nsCOMPtr<nsIDOMXULControlElement> xulControl = do_QueryObject(this); |
464 | 0 | if (xulControl) { |
465 | 0 | // a disabled element cannot be focused and is not part of the tab order |
466 | 0 | bool disabled; |
467 | 0 | xulControl->GetDisabled(&disabled); |
468 | 0 | if (disabled) { |
469 | 0 | if (aTabIndex) |
470 | 0 | *aTabIndex = -1; |
471 | 0 | return false; |
472 | 0 | } |
473 | 0 | shouldFocus = true; |
474 | 0 | } |
475 | 0 |
|
476 | 0 | if (aTabIndex) { |
477 | 0 | if (xulControl) { |
478 | 0 | if (HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { |
479 | 0 | // if either the aTabIndex argument or a specified tabindex is non-negative, |
480 | 0 | // the element becomes focusable. |
481 | 0 | int32_t tabIndex = 0; |
482 | 0 | xulControl->GetTabIndex(&tabIndex); |
483 | 0 | shouldFocus = *aTabIndex >= 0 || tabIndex >= 0; |
484 | 0 | *aTabIndex = tabIndex; |
485 | 0 | } else { |
486 | 0 | // otherwise, if there is no tabindex attribute, just use the value of |
487 | 0 | // *aTabIndex to indicate focusability. Reset any supplied tabindex to 0. |
488 | 0 | shouldFocus = *aTabIndex >= 0; |
489 | 0 | if (shouldFocus) |
490 | 0 | *aTabIndex = 0; |
491 | 0 | } |
492 | 0 |
|
493 | 0 | if (shouldFocus && sTabFocusModelAppliesToXUL && |
494 | 0 | !(sTabFocusModel & eTabFocus_formElementsMask)) { |
495 | 0 | // By default, the tab focus model doesn't apply to xul element on any system but OS X. |
496 | 0 | // on OS X we're following it for UI elements (XUL) as sTabFocusModel is based on |
497 | 0 | // "Full Keyboard Access" system setting (see mac/nsILookAndFeel). |
498 | 0 | // both textboxes and list elements (i.e. trees and list) should always be focusable |
499 | 0 | // (textboxes are handled as html:input) |
500 | 0 | // For compatibility, we only do this for controls, otherwise elements like <browser> |
501 | 0 | // cannot take this focus. |
502 | 0 | if (IsNonList(mNodeInfo)) |
503 | 0 | *aTabIndex = -1; |
504 | 0 | } |
505 | 0 | } else { |
506 | 0 | shouldFocus = *aTabIndex >= 0; |
507 | 0 | } |
508 | 0 | } |
509 | 0 |
|
510 | 0 | return shouldFocus; |
511 | 0 | } |
512 | | |
513 | | bool |
514 | | nsXULElement::HasMenu() |
515 | 0 | { |
516 | 0 | nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame()); |
517 | 0 | return menu != nullptr; |
518 | 0 | } |
519 | | |
520 | | void |
521 | | nsXULElement::OpenMenu(bool aOpenFlag) |
522 | 0 | { |
523 | 0 | nsCOMPtr<nsIDocument> doc = GetUncomposedDoc(); |
524 | 0 | if (doc) { |
525 | 0 | doc->FlushPendingNotifications(FlushType::Frames); |
526 | 0 | } |
527 | 0 |
|
528 | 0 | nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
529 | 0 | if (pm) { |
530 | 0 | if (aOpenFlag) { |
531 | 0 | // Nothing will happen if this element isn't a menu. |
532 | 0 | pm->ShowMenu(this, false, false); |
533 | 0 | } |
534 | 0 | else { |
535 | 0 | nsMenuFrame* menu = do_QueryFrame(GetPrimaryFrame()); |
536 | 0 | if (menu) { |
537 | 0 | nsMenuPopupFrame* popupFrame = menu->GetPopup(); |
538 | 0 | if (popupFrame) { |
539 | 0 | pm->HidePopup(popupFrame->GetContent(), false, true, false, false); |
540 | 0 | } |
541 | 0 | } |
542 | 0 | } |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | | bool |
547 | | nsXULElement::PerformAccesskey(bool aKeyCausesActivation, |
548 | | bool aIsTrustedEvent) |
549 | 0 | { |
550 | 0 | RefPtr<Element> content(this); |
551 | 0 |
|
552 | 0 | if (IsXULElement(nsGkAtoms::label)) { |
553 | 0 | nsAutoString control; |
554 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::control, control); |
555 | 0 | if (control.IsEmpty()) { |
556 | 0 | return false; |
557 | 0 | } |
558 | 0 | |
559 | 0 | //XXXsmaug Should we use ShadowRoot::GetElementById in case |
560 | 0 | // content is in Shadow DOM? |
561 | 0 | nsCOMPtr<nsIDocument> document = content->GetUncomposedDoc(); |
562 | 0 | if (!document) { |
563 | 0 | return false; |
564 | 0 | } |
565 | 0 | |
566 | 0 | content = document->GetElementById(control); |
567 | 0 | if (!content) { |
568 | 0 | return false; |
569 | 0 | } |
570 | 0 | } |
571 | 0 | |
572 | 0 | nsIFrame* frame = content->GetPrimaryFrame(); |
573 | 0 | if (!frame || !frame->IsVisibleConsideringAncestors()) { |
574 | 0 | return false; |
575 | 0 | } |
576 | 0 | |
577 | 0 | bool focused = false; |
578 | 0 | nsXULElement* elm = FromNode(content); |
579 | 0 | if (elm) { |
580 | 0 | // Define behavior for each type of XUL element. |
581 | 0 | if (!content->IsXULElement(nsGkAtoms::toolbarbutton)) { |
582 | 0 | nsIFocusManager* fm = nsFocusManager::GetFocusManager(); |
583 | 0 | if (fm) { |
584 | 0 | nsCOMPtr<Element> elementToFocus; |
585 | 0 | // for radio buttons, focus the radiogroup instead |
586 | 0 | if (content->IsXULElement(nsGkAtoms::radio)) { |
587 | 0 | nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem(do_QueryInterface(content)); |
588 | 0 | if (controlItem) { |
589 | 0 | bool disabled; |
590 | 0 | controlItem->GetDisabled(&disabled); |
591 | 0 | if (!disabled) { |
592 | 0 | nsCOMPtr<nsIDOMXULSelectControlElement> selectControl; |
593 | 0 | controlItem->GetControl(getter_AddRefs(selectControl)); |
594 | 0 | elementToFocus = do_QueryInterface(selectControl); |
595 | 0 | } |
596 | 0 | } |
597 | 0 | } else { |
598 | 0 | elementToFocus = content; |
599 | 0 | } |
600 | 0 | if (elementToFocus) { |
601 | 0 | fm->SetFocus(elementToFocus, nsIFocusManager::FLAG_BYKEY); |
602 | 0 |
|
603 | 0 | // Return true if the element became focused. |
604 | 0 | nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow(); |
605 | 0 | focused = (window && window->GetFocusedElement()); |
606 | 0 | } |
607 | 0 | } |
608 | 0 | } |
609 | 0 | if (aKeyCausesActivation && |
610 | 0 | !content->IsAnyOfXULElements(nsGkAtoms::textbox, nsGkAtoms::menulist)) { |
611 | 0 | elm->ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_KEYBOARD, aIsTrustedEvent); |
612 | 0 | } |
613 | 0 | } else { |
614 | 0 | return content->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent); |
615 | 0 | } |
616 | 0 | |
617 | 0 | return focused; |
618 | 0 | } |
619 | | |
620 | | //---------------------------------------------------------------------- |
621 | | |
622 | | void |
623 | | nsXULElement::AddListenerFor(const nsAttrName& aName) |
624 | 0 | { |
625 | 0 | // If appropriate, add a popup listener and/or compile the event |
626 | 0 | // handler. Called when we change the element's document, create a |
627 | 0 | // new element, change an attribute's value, etc. |
628 | 0 | // Eventlistenener-attributes are always in the null namespace |
629 | 0 | if (aName.IsAtom()) { |
630 | 0 | nsAtom *attr = aName.Atom(); |
631 | 0 | MaybeAddPopupListener(attr); |
632 | 0 | if (nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) { |
633 | 0 | nsAutoString value; |
634 | 0 | GetAttr(kNameSpaceID_None, attr, value); |
635 | 0 | SetEventHandler(attr, value, true); |
636 | 0 | } |
637 | 0 | } |
638 | 0 | } |
639 | | |
640 | | void |
641 | | nsXULElement::MaybeAddPopupListener(nsAtom* aLocalName) |
642 | 0 | { |
643 | 0 | // If appropriate, add a popup listener. Called when we change the |
644 | 0 | // element's document, create a new element, change an attribute's |
645 | 0 | // value, etc. |
646 | 0 | if (aLocalName == nsGkAtoms::menu || |
647 | 0 | aLocalName == nsGkAtoms::contextmenu || |
648 | 0 | // XXXdwh popup and context are deprecated |
649 | 0 | aLocalName == nsGkAtoms::popup || |
650 | 0 | aLocalName == nsGkAtoms::context) { |
651 | 0 | AddPopupListener(aLocalName); |
652 | 0 | } |
653 | 0 | } |
654 | | |
655 | | //---------------------------------------------------------------------- |
656 | | // |
657 | | // nsIContent interface |
658 | | // |
659 | | void |
660 | | nsXULElement::UpdateEditableState(bool aNotify) |
661 | 0 | { |
662 | 0 | // Don't call through to Element here because the things |
663 | 0 | // it does don't work for cases when we're an editable control. |
664 | 0 | nsIContent *parent = GetParent(); |
665 | 0 |
|
666 | 0 | SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE)); |
667 | 0 | UpdateState(aNotify); |
668 | 0 | } |
669 | | |
670 | | class XULInContentErrorReporter : public Runnable |
671 | | { |
672 | | public: |
673 | | explicit XULInContentErrorReporter(nsIDocument* aDocument) |
674 | | : mozilla::Runnable("XULInContentErrorReporter") |
675 | | , mDocument(aDocument) |
676 | 0 | { |
677 | 0 | } |
678 | | |
679 | | NS_IMETHOD Run() override |
680 | 0 | { |
681 | 0 | mDocument->WarnOnceAbout(nsIDocument::eImportXULIntoContent, false); |
682 | 0 | return NS_OK; |
683 | 0 | } |
684 | | |
685 | | private: |
686 | | nsCOMPtr<nsIDocument> mDocument; |
687 | | }; |
688 | | |
689 | | static bool |
690 | | NeedTooltipSupport(const nsXULElement& aXULElement) |
691 | 0 | { |
692 | 0 | if (aXULElement.NodeInfo()->Equals(nsGkAtoms::treechildren)) { |
693 | 0 | // treechildren always get tooltip support, since cropped tree cells show |
694 | 0 | // their full text in a tooltip. |
695 | 0 | return true; |
696 | 0 | } |
697 | 0 | |
698 | 0 | return aXULElement.GetBoolAttr(nsGkAtoms::tooltip) || |
699 | 0 | aXULElement.GetBoolAttr(nsGkAtoms::tooltiptext); |
700 | 0 | } |
701 | | |
702 | | nsresult |
703 | | nsXULElement::BindToTree(nsIDocument* aDocument, |
704 | | nsIContent* aParent, |
705 | | nsIContent* aBindingParent) |
706 | 0 | { |
707 | 0 | if (!aBindingParent && |
708 | 0 | aDocument && |
709 | 0 | !aDocument->IsLoadedAsInteractiveData() && |
710 | 0 | !aDocument->AllowXULXBL() && |
711 | 0 | !aDocument->HasWarnedAbout(nsIDocument::eImportXULIntoContent)) { |
712 | 0 | nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(aDocument)); |
713 | 0 | } |
714 | 0 |
|
715 | 0 | nsresult rv = nsStyledElement::BindToTree(aDocument, aParent, aBindingParent); |
716 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
717 | 0 |
|
718 | 0 | nsIDocument* doc = GetComposedDoc(); |
719 | | #ifdef DEBUG |
720 | | if (doc && !doc->AllowXULXBL() && !doc->IsUnstyledDocument()) { |
721 | | // To save CPU cycles and memory, non-XUL documents only load the user |
722 | | // agent style sheet rules for a minimal set of XUL elements such as |
723 | | // 'scrollbar' that may be created implicitly for their content (those |
724 | | // rules being in minimal-xul.css). |
725 | | // |
726 | | // This assertion makes sure no other XUL element is used in a non-XUL |
727 | | // document. |
728 | | nsAtom* tag = NodeInfo()->NameAtom(); |
729 | | MOZ_ASSERT( |
730 | | // scrollbar parts |
731 | | tag == nsGkAtoms::scrollbar || |
732 | | tag == nsGkAtoms::scrollbarbutton || |
733 | | tag == nsGkAtoms::scrollcorner || |
734 | | tag == nsGkAtoms::slider || |
735 | | tag == nsGkAtoms::thumb || |
736 | | // other |
737 | | tag == nsGkAtoms::datetimebox || |
738 | | tag == nsGkAtoms::resizer || |
739 | | tag == nsGkAtoms::label || |
740 | | tag == nsGkAtoms::videocontrols, |
741 | | "Unexpected XUL element in non-XUL doc" |
742 | | ); |
743 | | } |
744 | | #endif |
745 | |
|
746 | 0 | if (doc && NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { |
747 | 0 | // Create our XUL key listener and hook it up. |
748 | 0 | nsXBLService::AttachGlobalKeyHandler(this); |
749 | 0 | } |
750 | 0 |
|
751 | 0 | if (doc && NeedTooltipSupport(*this)) { |
752 | 0 | AddTooltipSupport(); |
753 | 0 | } |
754 | 0 |
|
755 | 0 | return rv; |
756 | 0 | } |
757 | | |
758 | | void |
759 | | nsXULElement::UnbindFromTree(bool aDeep, bool aNullParent) |
760 | 0 | { |
761 | 0 | if (NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { |
762 | 0 | nsXBLService::DetachGlobalKeyHandler(this); |
763 | 0 | } |
764 | 0 |
|
765 | 0 | if (NeedTooltipSupport(*this)) { |
766 | 0 | RemoveTooltipSupport(); |
767 | 0 | } |
768 | 0 |
|
769 | 0 | // mControllers can own objects that are implemented |
770 | 0 | // in JavaScript (such as some implementations of |
771 | 0 | // nsIControllers. These objects prevent their global |
772 | 0 | // object's script object from being garbage collected, |
773 | 0 | // which means JS continues to hold an owning reference |
774 | 0 | // to the nsGlobalWindow, which owns the document, |
775 | 0 | // which owns this content. That's a cycle, so we break |
776 | 0 | // it here. (It might be better to break this by releasing |
777 | 0 | // mDocument in nsGlobalWindow::SetDocShell, but I'm not |
778 | 0 | // sure whether that would fix all possible cycles through |
779 | 0 | // mControllers.) |
780 | 0 | nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); |
781 | 0 | if (slots) { |
782 | 0 | slots->mControllers = nullptr; |
783 | 0 | } |
784 | 0 |
|
785 | 0 | nsStyledElement::UnbindFromTree(aDeep, aNullParent); |
786 | 0 | } |
787 | | |
788 | | void |
789 | | nsXULElement::UnregisterAccessKey(const nsAString& aOldValue) |
790 | 0 | { |
791 | 0 | // If someone changes the accesskey, unregister the old one |
792 | 0 | // |
793 | 0 | nsIDocument* doc = GetComposedDoc(); |
794 | 0 | if (doc && !aOldValue.IsEmpty()) { |
795 | 0 | nsIPresShell *shell = doc->GetShell(); |
796 | 0 |
|
797 | 0 | if (shell) { |
798 | 0 | Element* element = this; |
799 | 0 |
|
800 | 0 | // find out what type of content node this is |
801 | 0 | if (mNodeInfo->Equals(nsGkAtoms::label)) { |
802 | 0 | // For anonymous labels the unregistering must |
803 | 0 | // occur on the binding parent control. |
804 | 0 | // XXXldb: And what if the binding parent is null? |
805 | 0 | nsIContent* bindingParent = GetBindingParent(); |
806 | 0 | element = bindingParent ? bindingParent->AsElement() : nullptr; |
807 | 0 | } |
808 | 0 |
|
809 | 0 | if (element) { |
810 | 0 | shell->GetPresContext()->EventStateManager()-> |
811 | 0 | UnregisterAccessKey(element, aOldValue.First()); |
812 | 0 | } |
813 | 0 | } |
814 | 0 | } |
815 | 0 | } |
816 | | |
817 | | nsresult |
818 | | nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName, |
819 | | const nsAttrValueOrString* aValue, bool aNotify) |
820 | 0 | { |
821 | 0 | if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey && |
822 | 0 | IsInUncomposedDoc()) { |
823 | 0 | nsAutoString oldValue; |
824 | 0 | if (GetAttr(aNamespaceID, aName, oldValue)) { |
825 | 0 | UnregisterAccessKey(oldValue); |
826 | 0 | } |
827 | 0 | } else if (aNamespaceID == kNameSpaceID_None && |
828 | 0 | (aName == nsGkAtoms::command || aName == nsGkAtoms::observes) && |
829 | 0 | IsInUncomposedDoc()) { |
830 | 0 | // XXX sXBL/XBL2 issue! Owner or current document? |
831 | 0 | nsAutoString oldValue; |
832 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::observes, oldValue); |
833 | 0 | if (oldValue.IsEmpty()) { |
834 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::command, oldValue); |
835 | 0 | } |
836 | 0 |
|
837 | 0 | if (!oldValue.IsEmpty()) { |
838 | 0 | RemoveBroadcaster(oldValue); |
839 | 0 | } |
840 | 0 | } else if (aNamespaceID == kNameSpaceID_None && |
841 | 0 | aValue && |
842 | 0 | mNodeInfo->Equals(nsGkAtoms::window) && |
843 | 0 | aName == nsGkAtoms::chromemargin) { |
844 | 0 | nsAttrValue attrValue; |
845 | 0 | // Make sure the margin format is valid first |
846 | 0 | if (!attrValue.ParseIntMarginValue(aValue->String())) { |
847 | 0 | return NS_ERROR_INVALID_ARG; |
848 | 0 | } |
849 | 0 | } else if (aNamespaceID == kNameSpaceID_None && |
850 | 0 | aName == nsGkAtoms::usercontextid) { |
851 | 0 | nsAutoString oldValue; |
852 | 0 | bool hasAttribute = GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, oldValue); |
853 | 0 | if (hasAttribute && (!aValue || !aValue->String().Equals(oldValue))) { |
854 | 0 | MOZ_ASSERT(false, "Changing usercontextid is not allowed."); |
855 | 0 | return NS_ERROR_INVALID_ARG; |
856 | 0 | } |
857 | 0 | } |
858 | 0 |
|
859 | 0 | return nsStyledElement::BeforeSetAttr(aNamespaceID, aName, |
860 | 0 | aValue, aNotify); |
861 | 0 | } |
862 | | |
863 | | nsresult |
864 | | nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, |
865 | | const nsAttrValue* aValue, |
866 | | const nsAttrValue* aOldValue, |
867 | | nsIPrincipal* aSubjectPrincipal, |
868 | | bool aNotify) |
869 | 0 | { |
870 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
871 | 0 | if (aValue) { |
872 | 0 | // Add popup and event listeners. We can't call AddListenerFor since |
873 | 0 | // the attribute isn't set yet. |
874 | 0 | MaybeAddPopupListener(aName); |
875 | 0 | if (nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL)) { |
876 | 0 | if (aValue->Type() == nsAttrValue::eString) { |
877 | 0 | SetEventHandler(aName, aValue->GetStringValue(), true); |
878 | 0 | } else { |
879 | 0 | nsAutoString body; |
880 | 0 | aValue->ToString(body); |
881 | 0 | SetEventHandler(aName, body, true); |
882 | 0 | } |
883 | 0 | } |
884 | 0 |
|
885 | 0 | nsIDocument* document = GetUncomposedDoc(); |
886 | 0 |
|
887 | 0 | // Hide chrome if needed |
888 | 0 | if (mNodeInfo->Equals(nsGkAtoms::window)) { |
889 | 0 | if (aName == nsGkAtoms::hidechrome) { |
890 | 0 | HideWindowChrome( |
891 | 0 | aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters)); |
892 | 0 | } else if (aName == nsGkAtoms::chromemargin) { |
893 | 0 | SetChromeMargins(aValue); |
894 | 0 | } else if (aName == nsGkAtoms::windowtype && |
895 | 0 | document && document->GetRootElement() == this) { |
896 | 0 | MaybeUpdatePrivateLifetime(); |
897 | 0 | } |
898 | 0 | } |
899 | 0 | // title and drawintitlebar are settable on |
900 | 0 | // any root node (windows, dialogs, etc) |
901 | 0 | if (document && document->GetRootElement() == this) { |
902 | 0 | if (aName == nsGkAtoms::title) { |
903 | 0 | document->NotifyPossibleTitleChange(false); |
904 | 0 | } else if (aName == nsGkAtoms::drawintitlebar) { |
905 | 0 | SetDrawsInTitlebar( |
906 | 0 | aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters)); |
907 | 0 | } else if (aName == nsGkAtoms::drawtitle) { |
908 | 0 | SetDrawsTitle( |
909 | 0 | aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters)); |
910 | 0 | } else if (aName == nsGkAtoms::localedir) { |
911 | 0 | // if the localedir changed on the root element, reset the document direction |
912 | 0 | if (document->IsXULDocument()) { |
913 | 0 | document->AsXULDocument()->ResetDocumentDirection(); |
914 | 0 | } |
915 | 0 | } else if (aName == nsGkAtoms::lwtheme || |
916 | 0 | aName == nsGkAtoms::lwthemetextcolor) { |
917 | 0 | // if the lwtheme changed, make sure to reset the document lwtheme cache |
918 | 0 | document->ResetDocumentLWTheme(); |
919 | 0 | UpdateBrightTitlebarForeground(document); |
920 | 0 | } else if (aName == nsGkAtoms::brighttitlebarforeground) { |
921 | 0 | UpdateBrightTitlebarForeground(document); |
922 | 0 | } |
923 | 0 | } |
924 | 0 | } else { |
925 | 0 | if (mNodeInfo->Equals(nsGkAtoms::window)) { |
926 | 0 | if (aName == nsGkAtoms::hidechrome) { |
927 | 0 | HideWindowChrome(false); |
928 | 0 | } else if (aName == nsGkAtoms::chromemargin) { |
929 | 0 | ResetChromeMargins(); |
930 | 0 | } |
931 | 0 | } |
932 | 0 |
|
933 | 0 | nsIDocument* doc = GetUncomposedDoc(); |
934 | 0 | if (doc && doc->GetRootElement() == this) { |
935 | 0 | if (aName == nsGkAtoms::localedir) { |
936 | 0 | // if the localedir changed on the root element, reset the document direction |
937 | 0 | if (doc->IsXULDocument()) { |
938 | 0 | doc->AsXULDocument()->ResetDocumentDirection(); |
939 | 0 | } |
940 | 0 | } else if ((aName == nsGkAtoms::lwtheme || |
941 | 0 | aName == nsGkAtoms::lwthemetextcolor)) { |
942 | 0 | // if the lwtheme changed, make sure to restyle appropriately |
943 | 0 | doc->ResetDocumentLWTheme(); |
944 | 0 | UpdateBrightTitlebarForeground(doc); |
945 | 0 | } else if (aName == nsGkAtoms::brighttitlebarforeground) { |
946 | 0 | UpdateBrightTitlebarForeground(doc); |
947 | 0 | } else if (aName == nsGkAtoms::drawintitlebar) { |
948 | 0 | SetDrawsInTitlebar(false); |
949 | 0 | } else if (aName == nsGkAtoms::drawtitle) { |
950 | 0 | SetDrawsTitle(false); |
951 | 0 | } |
952 | 0 | } |
953 | 0 | } |
954 | 0 |
|
955 | 0 | if (aName == nsGkAtoms::tooltip || aName == nsGkAtoms::tooltiptext) { |
956 | 0 | if (!!aValue != !!aOldValue && |
957 | 0 | IsInComposedDoc() && |
958 | 0 | !NodeInfo()->Equals(nsGkAtoms::treechildren)) { |
959 | 0 | if (aValue) { |
960 | 0 | AddTooltipSupport(); |
961 | 0 | } else { |
962 | 0 | RemoveTooltipSupport(); |
963 | 0 | } |
964 | 0 | } |
965 | 0 | } |
966 | 0 | // XXX need to check if they're changing an event handler: if |
967 | 0 | // so, then we need to unhook the old one. Or something. |
968 | 0 | } |
969 | 0 |
|
970 | 0 | return nsStyledElement::AfterSetAttr(aNamespaceID, aName, |
971 | 0 | aValue, aOldValue, aSubjectPrincipal, aNotify); |
972 | 0 | } |
973 | | |
974 | | void |
975 | | nsXULElement::AddTooltipSupport() |
976 | 0 | { |
977 | 0 | nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance(); |
978 | 0 | if (!listener) { |
979 | 0 | return; |
980 | 0 | } |
981 | 0 | |
982 | 0 | listener->AddTooltipSupport(this); |
983 | 0 | } |
984 | | |
985 | | void |
986 | | nsXULElement::RemoveTooltipSupport() |
987 | 0 | { |
988 | 0 | nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance(); |
989 | 0 | if (!listener) { |
990 | 0 | return; |
991 | 0 | } |
992 | 0 | |
993 | 0 | listener->RemoveTooltipSupport(this); |
994 | 0 | } |
995 | | |
996 | | bool |
997 | | nsXULElement::ParseAttribute(int32_t aNamespaceID, |
998 | | nsAtom* aAttribute, |
999 | | const nsAString& aValue, |
1000 | | nsIPrincipal* aMaybeScriptedPrincipal, |
1001 | | nsAttrValue& aResult) |
1002 | 0 | { |
1003 | 0 | // Parse into a nsAttrValue |
1004 | 0 | if (!nsStyledElement::ParseAttribute(aNamespaceID, aAttribute, aValue, |
1005 | 0 | aMaybeScriptedPrincipal, aResult)) { |
1006 | 0 | // Fall back to parsing as atom for short values |
1007 | 0 | aResult.ParseStringOrAtom(aValue); |
1008 | 0 | } |
1009 | 0 |
|
1010 | 0 | return true; |
1011 | 0 | } |
1012 | | |
1013 | | void |
1014 | | nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId) |
1015 | 0 | { |
1016 | 0 | nsIDocument* doc = OwnerDoc(); |
1017 | 0 | if (!doc->IsXULDocument()) { |
1018 | 0 | return; |
1019 | 0 | } |
1020 | 0 | if (Element* broadcaster = doc->GetElementById(broadcasterId)) { |
1021 | 0 | doc->AsXULDocument()->RemoveBroadcastListenerFor( |
1022 | 0 | *broadcaster, *this, NS_LITERAL_STRING("*")); |
1023 | 0 | } |
1024 | 0 | } |
1025 | | |
1026 | | void |
1027 | | nsXULElement::DestroyContent() |
1028 | 0 | { |
1029 | 0 | nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots(); |
1030 | 0 | if (slots) { |
1031 | 0 | slots->mControllers = nullptr; |
1032 | 0 | } |
1033 | 0 |
|
1034 | 0 | nsStyledElement::DestroyContent(); |
1035 | 0 | } |
1036 | | |
1037 | | #ifdef DEBUG |
1038 | | void |
1039 | | nsXULElement::List(FILE* out, int32_t aIndent) const |
1040 | | { |
1041 | | nsCString prefix("XUL"); |
1042 | | if (HasSlots()) { |
1043 | | prefix.Append('*'); |
1044 | | } |
1045 | | prefix.Append(' '); |
1046 | | |
1047 | | nsStyledElement::List(out, aIndent, prefix); |
1048 | | } |
1049 | | #endif |
1050 | | |
1051 | | bool |
1052 | | nsXULElement::IsEventStoppedFromAnonymousScrollbar(EventMessage aMessage) |
1053 | 0 | { |
1054 | 0 | return (IsRootOfNativeAnonymousSubtree() && |
1055 | 0 | IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner) && |
1056 | 0 | (aMessage == eMouseClick || aMessage == eMouseDoubleClick || |
1057 | 0 | aMessage == eXULCommand || aMessage == eContextMenu || |
1058 | 0 | aMessage == eDragStart || aMessage == eMouseAuxClick)); |
1059 | 0 | } |
1060 | | |
1061 | | nsresult |
1062 | | nsXULElement::DispatchXULCommand(const EventChainVisitor& aVisitor, |
1063 | | nsAutoString& aCommand) |
1064 | 0 | { |
1065 | 0 | // XXX sXBL/XBL2 issue! Owner or current document? |
1066 | 0 | nsCOMPtr<nsIDocument> doc = GetUncomposedDoc(); |
1067 | 0 | NS_ENSURE_STATE(doc); |
1068 | 0 | RefPtr<Element> commandElt = doc->GetElementById(aCommand); |
1069 | 0 | if (commandElt) { |
1070 | 0 | // Create a new command event to dispatch to the element |
1071 | 0 | // pointed to by the command attribute. The new event's |
1072 | 0 | // sourceEvent will be the original command event that we're |
1073 | 0 | // handling. |
1074 | 0 | RefPtr<Event> event = aVisitor.mDOMEvent; |
1075 | 0 | uint16_t inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; |
1076 | 0 | while (event) { |
1077 | 0 | NS_ENSURE_STATE(event->GetOriginalTarget() != commandElt); |
1078 | 0 | RefPtr<XULCommandEvent> commandEvent = event->AsXULCommandEvent(); |
1079 | 0 | if (commandEvent) { |
1080 | 0 | event = commandEvent->GetSourceEvent(); |
1081 | 0 | inputSource = commandEvent->InputSource(); |
1082 | 0 | } else { |
1083 | 0 | event = nullptr; |
1084 | 0 | } |
1085 | 0 | } |
1086 | 0 | WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent(); |
1087 | 0 | nsContentUtils::DispatchXULCommand( |
1088 | 0 | commandElt, |
1089 | 0 | orig->IsTrusted(), |
1090 | 0 | aVisitor.mDOMEvent, |
1091 | 0 | nullptr, |
1092 | 0 | orig->IsControl(), |
1093 | 0 | orig->IsAlt(), |
1094 | 0 | orig->IsShift(), |
1095 | 0 | orig->IsMeta(), |
1096 | 0 | inputSource); |
1097 | 0 | } else { |
1098 | 0 | NS_WARNING("A XUL element is attached to a command that doesn't exist!\n"); |
1099 | 0 | } |
1100 | 0 | return NS_OK; |
1101 | 0 | } |
1102 | | |
1103 | | void |
1104 | | nsXULElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) |
1105 | 0 | { |
1106 | 0 | aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 |
1107 | 0 | if (IsEventStoppedFromAnonymousScrollbar(aVisitor.mEvent->mMessage)) { |
1108 | 0 | // Don't propagate these events from native anonymous scrollbar. |
1109 | 0 | aVisitor.mCanHandle = true; |
1110 | 0 | aVisitor.SetParentTarget(nullptr, false); |
1111 | 0 | return; |
1112 | 0 | } |
1113 | 0 | if (aVisitor.mEvent->mMessage == eXULCommand && |
1114 | 0 | aVisitor.mEvent->mClass == eInputEventClass && |
1115 | 0 | aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this) && |
1116 | 0 | !IsXULElement(nsGkAtoms::command)) { |
1117 | 0 | // Check that we really have an xul command event. That will be handled |
1118 | 0 | // in a special way. |
1119 | 0 | // See if we have a command elt. If so, we execute on the command |
1120 | 0 | // instead of on our content element. |
1121 | 0 | nsAutoString command; |
1122 | 0 | if (aVisitor.mDOMEvent && |
1123 | 0 | aVisitor.mDOMEvent->AsXULCommandEvent() && |
1124 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) && |
1125 | 0 | !command.IsEmpty()) { |
1126 | 0 | // Stop building the event target chain for the original event. |
1127 | 0 | // We don't want it to propagate to any DOM nodes. |
1128 | 0 | aVisitor.mCanHandle = false; |
1129 | 0 | aVisitor.mAutomaticChromeDispatch = false; |
1130 | 0 | // Dispatch XUL command in PreHandleEvent to prevent it breaks event |
1131 | 0 | // target chain creation |
1132 | 0 | aVisitor.mWantsPreHandleEvent = true; |
1133 | 0 | aVisitor.mItemFlags |= NS_DISPATCH_XUL_COMMAND; |
1134 | 0 | return; |
1135 | 0 | } |
1136 | 0 | } |
1137 | 0 |
|
1138 | 0 | nsStyledElement::GetEventTargetParent(aVisitor); |
1139 | 0 | } |
1140 | | |
1141 | | nsresult |
1142 | | nsXULElement::PreHandleEvent(EventChainVisitor& aVisitor) |
1143 | 0 | { |
1144 | 0 | if (aVisitor.mItemFlags & NS_DISPATCH_XUL_COMMAND) { |
1145 | 0 | nsAutoString command; |
1146 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::command, command); |
1147 | 0 | MOZ_ASSERT(!command.IsEmpty()); |
1148 | 0 | return DispatchXULCommand(aVisitor, command); |
1149 | 0 | } |
1150 | 0 | return nsStyledElement::PreHandleEvent(aVisitor); |
1151 | 0 | } |
1152 | | |
1153 | | //---------------------------------------------------------------------- |
1154 | | // Implementation methods |
1155 | | |
1156 | | |
1157 | | nsChangeHint |
1158 | | nsXULElement::GetAttributeChangeHint(const nsAtom* aAttribute, |
1159 | | int32_t aModType) const |
1160 | 0 | { |
1161 | 0 | nsChangeHint retval(nsChangeHint(0)); |
1162 | 0 |
|
1163 | 0 | if (aAttribute == nsGkAtoms::value && |
1164 | 0 | (aModType == MutationEvent_Binding::REMOVAL || |
1165 | 0 | aModType == MutationEvent_Binding::ADDITION)) { |
1166 | 0 | if (IsAnyOfXULElements(nsGkAtoms::label, nsGkAtoms::description)) |
1167 | 0 | // Label and description dynamically morph between a normal |
1168 | 0 | // block and a cropping single-line XUL text frame. If the |
1169 | 0 | // value attribute is being added or removed, then we need to |
1170 | 0 | // return a hint of frame change. (See bugzilla bug 95475 for |
1171 | 0 | // details.) |
1172 | 0 | retval = nsChangeHint_ReconstructFrame; |
1173 | 0 | } else { |
1174 | 0 | // if left or top changes we reflow. This will happen in xul |
1175 | 0 | // containers that manage positioned children such as a stack. |
1176 | 0 | if (nsGkAtoms::left == aAttribute || nsGkAtoms::top == aAttribute || |
1177 | 0 | nsGkAtoms::right == aAttribute || nsGkAtoms::bottom == aAttribute || |
1178 | 0 | nsGkAtoms::start == aAttribute || nsGkAtoms::end == aAttribute) |
1179 | 0 | retval = NS_STYLE_HINT_REFLOW; |
1180 | 0 | } |
1181 | 0 |
|
1182 | 0 | return retval; |
1183 | 0 | } |
1184 | | |
1185 | | NS_IMETHODIMP_(bool) |
1186 | | nsXULElement::IsAttributeMapped(const nsAtom* aAttribute) const |
1187 | 0 | { |
1188 | 0 | return false; |
1189 | 0 | } |
1190 | | |
1191 | | nsIControllers* |
1192 | | nsXULElement::GetControllers(ErrorResult& rv) |
1193 | 0 | { |
1194 | 0 | if (! Controllers()) { |
1195 | 0 | nsExtendedDOMSlots* slots = ExtendedDOMSlots(); |
1196 | 0 |
|
1197 | 0 | slots->mControllers = new nsXULControllers(); |
1198 | 0 | } |
1199 | 0 |
|
1200 | 0 | return Controllers(); |
1201 | 0 | } |
1202 | | |
1203 | | already_AddRefed<BoxObject> |
1204 | | nsXULElement::GetBoxObject(ErrorResult& rv) |
1205 | 0 | { |
1206 | 0 | // XXX sXBL/XBL2 issue! Owner or current document? |
1207 | 0 | return OwnerDoc()->GetBoxObjectFor(this, rv); |
1208 | 0 | } |
1209 | | |
1210 | | void |
1211 | | nsXULElement::Click(CallerType aCallerType) |
1212 | 0 | { |
1213 | 0 | ClickWithInputSource(MouseEvent_Binding::MOZ_SOURCE_UNKNOWN, |
1214 | 0 | aCallerType == CallerType::System); |
1215 | 0 | } |
1216 | | |
1217 | | void |
1218 | | nsXULElement::ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent) |
1219 | 0 | { |
1220 | 0 | if (BoolAttrIsTrue(nsGkAtoms::disabled)) |
1221 | 0 | return; |
1222 | 0 | |
1223 | 0 | nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // Strong just in case |
1224 | 0 | if (doc) { |
1225 | 0 | RefPtr<nsPresContext> context = doc->GetPresContext(); |
1226 | 0 | if (context) { |
1227 | 0 | // strong ref to PresContext so events don't destroy it |
1228 | 0 |
|
1229 | 0 | WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown, |
1230 | 0 | nullptr, WidgetMouseEvent::eReal); |
1231 | 0 | WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp, |
1232 | 0 | nullptr, WidgetMouseEvent::eReal); |
1233 | 0 | WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr, |
1234 | 0 | WidgetMouseEvent::eReal); |
1235 | 0 | eventDown.inputSource = eventUp.inputSource = eventClick.inputSource |
1236 | 0 | = aInputSource; |
1237 | 0 |
|
1238 | 0 | // send mouse down |
1239 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
1240 | 0 | EventDispatcher::Dispatch(static_cast<nsIContent*>(this), |
1241 | 0 | context, &eventDown, nullptr, &status); |
1242 | 0 |
|
1243 | 0 | // send mouse up |
1244 | 0 | status = nsEventStatus_eIgnore; // reset status |
1245 | 0 | EventDispatcher::Dispatch(static_cast<nsIContent*>(this), |
1246 | 0 | context, &eventUp, nullptr, &status); |
1247 | 0 |
|
1248 | 0 | // send mouse click |
1249 | 0 | status = nsEventStatus_eIgnore; // reset status |
1250 | 0 | EventDispatcher::Dispatch(static_cast<nsIContent*>(this), |
1251 | 0 | context, &eventClick, nullptr, &status); |
1252 | 0 |
|
1253 | 0 | // If the click has been prevented, lets skip the command call |
1254 | 0 | // this is how a physical click works |
1255 | 0 | if (status == nsEventStatus_eConsumeNoDefault) { |
1256 | 0 | return; |
1257 | 0 | } |
1258 | 0 | } |
1259 | 0 | } |
1260 | 0 | |
1261 | 0 | // oncommand is fired when an element is clicked... |
1262 | 0 | DoCommand(); |
1263 | 0 | } |
1264 | | |
1265 | | void |
1266 | | nsXULElement::DoCommand() |
1267 | 0 | { |
1268 | 0 | nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // strong just in case |
1269 | 0 | if (doc) { |
1270 | 0 | nsContentUtils::DispatchXULCommand(this, true); |
1271 | 0 | } |
1272 | 0 | } |
1273 | | |
1274 | | bool |
1275 | | nsXULElement::IsNodeOfType(uint32_t aFlags) const |
1276 | 0 | { |
1277 | 0 | return false; |
1278 | 0 | } |
1279 | | |
1280 | | nsresult |
1281 | | nsXULElement::AddPopupListener(nsAtom* aName) |
1282 | 0 | { |
1283 | 0 | // Add a popup listener to the element |
1284 | 0 | bool isContext = (aName == nsGkAtoms::context || |
1285 | 0 | aName == nsGkAtoms::contextmenu); |
1286 | 0 | uint32_t listenerFlag = isContext ? |
1287 | 0 | XUL_ELEMENT_HAS_CONTENTMENU_LISTENER : |
1288 | 0 | XUL_ELEMENT_HAS_POPUP_LISTENER; |
1289 | 0 |
|
1290 | 0 | if (HasFlag(listenerFlag)) { |
1291 | 0 | return NS_OK; |
1292 | 0 | } |
1293 | 0 | |
1294 | 0 | nsCOMPtr<nsIDOMEventListener> listener = |
1295 | 0 | new nsXULPopupListener(this, isContext); |
1296 | 0 |
|
1297 | 0 | // Add the popup as a listener on this element. |
1298 | 0 | EventListenerManager* manager = GetOrCreateListenerManager(); |
1299 | 0 | SetFlags(listenerFlag); |
1300 | 0 |
|
1301 | 0 | if (isContext) { |
1302 | 0 | manager->AddEventListenerByType(listener, |
1303 | 0 | NS_LITERAL_STRING("contextmenu"), |
1304 | 0 | TrustedEventsAtSystemGroupBubble()); |
1305 | 0 | } else { |
1306 | 0 | manager->AddEventListenerByType(listener, |
1307 | 0 | NS_LITERAL_STRING("mousedown"), |
1308 | 0 | TrustedEventsAtSystemGroupBubble()); |
1309 | 0 | } |
1310 | 0 | return NS_OK; |
1311 | 0 | } |
1312 | | |
1313 | | EventStates |
1314 | | nsXULElement::IntrinsicState() const |
1315 | 0 | { |
1316 | 0 | EventStates state = nsStyledElement::IntrinsicState(); |
1317 | 0 |
|
1318 | 0 | if (IsReadWriteTextElement()) { |
1319 | 0 | state |= NS_EVENT_STATE_MOZ_READWRITE; |
1320 | 0 | state &= ~NS_EVENT_STATE_MOZ_READONLY; |
1321 | 0 | } |
1322 | 0 |
|
1323 | 0 | return state; |
1324 | 0 | } |
1325 | | |
1326 | | //---------------------------------------------------------------------- |
1327 | | |
1328 | | nsresult |
1329 | | nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) |
1330 | 0 | { |
1331 | 0 | if (!aPrototype) { |
1332 | 0 | return NS_OK; |
1333 | 0 | } |
1334 | 0 | |
1335 | 0 | uint32_t i; |
1336 | 0 | nsresult rv; |
1337 | 0 | for (i = 0; i < aPrototype->mNumAttributes; ++i) { |
1338 | 0 | nsXULPrototypeAttribute* protoattr = &aPrototype->mAttributes[i]; |
1339 | 0 | nsAttrValue attrValue; |
1340 | 0 |
|
1341 | 0 | // Style rules need to be cloned. |
1342 | 0 | if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) { |
1343 | 0 | DeclarationBlock* decl = protoattr->mValue.GetCSSDeclarationValue(); |
1344 | 0 | RefPtr<DeclarationBlock> declClone = decl->Clone(); |
1345 | 0 |
|
1346 | 0 | nsString stringValue; |
1347 | 0 | protoattr->mValue.ToString(stringValue); |
1348 | 0 |
|
1349 | 0 | attrValue.SetTo(declClone.forget(), &stringValue); |
1350 | 0 | } else { |
1351 | 0 | attrValue.SetTo(protoattr->mValue); |
1352 | 0 | } |
1353 | 0 |
|
1354 | 0 | bool oldValueSet; |
1355 | 0 | // XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName |
1356 | 0 | if (protoattr->mName.IsAtom()) { |
1357 | 0 | rv = mAttrs.SetAndSwapAttr(protoattr->mName.Atom(), |
1358 | 0 | attrValue, &oldValueSet); |
1359 | 0 | } else { |
1360 | 0 | rv = mAttrs.SetAndSwapAttr(protoattr->mName.NodeInfo(), |
1361 | 0 | attrValue, &oldValueSet); |
1362 | 0 | } |
1363 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1364 | 0 | } |
1365 | 0 | return NS_OK; |
1366 | 0 | } |
1367 | | |
1368 | | nsresult |
1369 | | nsXULElement::HideWindowChrome(bool aShouldHide) |
1370 | 0 | { |
1371 | 0 | nsIDocument* doc = GetUncomposedDoc(); |
1372 | 0 | if (!doc || doc->GetRootElement() != this) |
1373 | 0 | return NS_ERROR_UNEXPECTED; |
1374 | 0 | |
1375 | 0 | // only top level chrome documents can hide the window chrome |
1376 | 0 | if (!doc->IsRootDisplayDocument()) |
1377 | 0 | return NS_OK; |
1378 | 0 | |
1379 | 0 | nsPresContext* presContext = doc->GetPresContext(); |
1380 | 0 |
|
1381 | 0 | if (presContext && presContext->IsChrome()) { |
1382 | 0 | nsIFrame* frame = GetPrimaryFrame(); |
1383 | 0 |
|
1384 | 0 | if (frame) { |
1385 | 0 | nsView* view = frame->GetClosestView(); |
1386 | 0 |
|
1387 | 0 | if (view) { |
1388 | 0 | nsIWidget* w = view->GetWidget(); |
1389 | 0 | NS_ENSURE_STATE(w); |
1390 | 0 | w->HideWindowChrome(aShouldHide); |
1391 | 0 | } |
1392 | 0 | } |
1393 | 0 | } |
1394 | 0 |
|
1395 | 0 | return NS_OK; |
1396 | 0 | } |
1397 | | |
1398 | | nsIWidget* |
1399 | | nsXULElement::GetWindowWidget() |
1400 | 0 | { |
1401 | 0 | nsIDocument* doc = GetComposedDoc(); |
1402 | 0 |
|
1403 | 0 | // only top level chrome documents can set the titlebar color |
1404 | 0 | if (doc && doc->IsRootDisplayDocument()) { |
1405 | 0 | nsCOMPtr<nsISupports> container = doc->GetContainer(); |
1406 | 0 | nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); |
1407 | 0 | if (baseWindow) { |
1408 | 0 | nsCOMPtr<nsIWidget> mainWidget; |
1409 | 0 | baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); |
1410 | 0 | return mainWidget; |
1411 | 0 | } |
1412 | 0 | } |
1413 | 0 | return nullptr; |
1414 | 0 | } |
1415 | | |
1416 | | class SetDrawInTitleBarEvent : public Runnable |
1417 | | { |
1418 | | public: |
1419 | | SetDrawInTitleBarEvent(nsIWidget* aWidget, bool aState) |
1420 | | : mozilla::Runnable("SetDrawInTitleBarEvent") |
1421 | | , mWidget(aWidget) |
1422 | | , mState(aState) |
1423 | 0 | {} |
1424 | | |
1425 | 0 | NS_IMETHOD Run() override { |
1426 | 0 | NS_ASSERTION(mWidget, "You shouldn't call this runnable with a null widget!"); |
1427 | 0 |
|
1428 | 0 | mWidget->SetDrawsInTitlebar(mState); |
1429 | 0 | return NS_OK; |
1430 | 0 | } |
1431 | | |
1432 | | private: |
1433 | | nsCOMPtr<nsIWidget> mWidget; |
1434 | | bool mState; |
1435 | | }; |
1436 | | |
1437 | | void |
1438 | | nsXULElement::SetDrawsInTitlebar(bool aState) |
1439 | 0 | { |
1440 | 0 | nsIWidget* mainWidget = GetWindowWidget(); |
1441 | 0 | if (mainWidget) { |
1442 | 0 | nsContentUtils::AddScriptRunner(new SetDrawInTitleBarEvent(mainWidget, aState)); |
1443 | 0 | } |
1444 | 0 | } |
1445 | | |
1446 | | void |
1447 | | nsXULElement::SetDrawsTitle(bool aState) |
1448 | 0 | { |
1449 | 0 | nsIWidget* mainWidget = GetWindowWidget(); |
1450 | 0 | if (mainWidget) { |
1451 | 0 | // We can do this synchronously because SetDrawsTitle doesn't have any |
1452 | 0 | // synchronous effects apart from a harmless invalidation. |
1453 | 0 | mainWidget->SetDrawsTitle(aState); |
1454 | 0 | } |
1455 | 0 | } |
1456 | | |
1457 | | void |
1458 | | nsXULElement::UpdateBrightTitlebarForeground(nsIDocument* aDoc) |
1459 | 0 | { |
1460 | 0 | nsIWidget* mainWidget = GetWindowWidget(); |
1461 | 0 | if (mainWidget) { |
1462 | 0 | // We can do this synchronously because SetBrightTitlebarForeground doesn't have any |
1463 | 0 | // synchronous effects apart from a harmless invalidation. |
1464 | 0 | mainWidget->SetUseBrightTitlebarForeground( |
1465 | 0 | aDoc->GetDocumentLWTheme() == nsIDocument::Doc_Theme_Bright || |
1466 | 0 | aDoc->GetRootElement()->AttrValueIs(kNameSpaceID_None, |
1467 | 0 | nsGkAtoms::brighttitlebarforeground, |
1468 | 0 | NS_LITERAL_STRING("true"), |
1469 | 0 | eCaseMatters)); |
1470 | 0 | } |
1471 | 0 | } |
1472 | | |
1473 | | class MarginSetter : public Runnable |
1474 | | { |
1475 | | public: |
1476 | | explicit MarginSetter(nsIWidget* aWidget) |
1477 | | : mozilla::Runnable("MarginSetter") |
1478 | | , mWidget(aWidget) |
1479 | | , mMargin(-1, -1, -1, -1) |
1480 | 0 | { |
1481 | 0 | } |
1482 | | MarginSetter(nsIWidget* aWidget, const LayoutDeviceIntMargin& aMargin) |
1483 | | : mozilla::Runnable("MarginSetter") |
1484 | | , mWidget(aWidget) |
1485 | | , mMargin(aMargin) |
1486 | 0 | { |
1487 | 0 | } |
1488 | | |
1489 | | NS_IMETHOD Run() override |
1490 | 0 | { |
1491 | 0 | // SetNonClientMargins can dispatch native events, hence doing |
1492 | 0 | // it off a script runner. |
1493 | 0 | mWidget->SetNonClientMargins(mMargin); |
1494 | 0 | return NS_OK; |
1495 | 0 | } |
1496 | | |
1497 | | private: |
1498 | | nsCOMPtr<nsIWidget> mWidget; |
1499 | | LayoutDeviceIntMargin mMargin; |
1500 | | }; |
1501 | | |
1502 | | void |
1503 | | nsXULElement::SetChromeMargins(const nsAttrValue* aValue) |
1504 | 0 | { |
1505 | 0 | if (!aValue) |
1506 | 0 | return; |
1507 | 0 | |
1508 | 0 | nsIWidget* mainWidget = GetWindowWidget(); |
1509 | 0 | if (!mainWidget) |
1510 | 0 | return; |
1511 | 0 | |
1512 | 0 | // top, right, bottom, left - see nsAttrValue |
1513 | 0 | nsIntMargin margins; |
1514 | 0 | bool gotMargins = false; |
1515 | 0 |
|
1516 | 0 | if (aValue->Type() == nsAttrValue::eIntMarginValue) { |
1517 | 0 | gotMargins = aValue->GetIntMarginValue(margins); |
1518 | 0 | } else { |
1519 | 0 | nsAutoString tmp; |
1520 | 0 | aValue->ToString(tmp); |
1521 | 0 | gotMargins = nsContentUtils::ParseIntMarginValue(tmp, margins); |
1522 | 0 | } |
1523 | 0 | if (gotMargins) { |
1524 | 0 | nsContentUtils::AddScriptRunner( |
1525 | 0 | new MarginSetter( |
1526 | 0 | mainWidget, LayoutDeviceIntMargin::FromUnknownMargin(margins))); |
1527 | 0 | } |
1528 | 0 | } |
1529 | | |
1530 | | void |
1531 | | nsXULElement::ResetChromeMargins() |
1532 | 0 | { |
1533 | 0 | nsIWidget* mainWidget = GetWindowWidget(); |
1534 | 0 | if (!mainWidget) |
1535 | 0 | return; |
1536 | 0 | // See nsIWidget |
1537 | 0 | nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget)); |
1538 | 0 | } |
1539 | | |
1540 | | bool |
1541 | | nsXULElement::BoolAttrIsTrue(nsAtom* aName) const |
1542 | 0 | { |
1543 | 0 | const nsAttrValue* attr = |
1544 | 0 | GetAttrInfo(kNameSpaceID_None, aName).mValue; |
1545 | 0 |
|
1546 | 0 | return attr && attr->Type() == nsAttrValue::eAtom && |
1547 | 0 | attr->GetAtomValue() == nsGkAtoms::_true; |
1548 | 0 | } |
1549 | | |
1550 | | void |
1551 | | nsXULElement::RecompileScriptEventListeners() |
1552 | 0 | { |
1553 | 0 | int32_t i, count = mAttrs.AttrCount(); |
1554 | 0 | for (i = 0; i < count; ++i) { |
1555 | 0 | const nsAttrName *name = mAttrs.AttrNameAt(i); |
1556 | 0 |
|
1557 | 0 | // Eventlistenener-attributes are always in the null namespace |
1558 | 0 | if (!name->IsAtom()) { |
1559 | 0 | continue; |
1560 | 0 | } |
1561 | 0 | |
1562 | 0 | nsAtom *attr = name->Atom(); |
1563 | 0 | if (!nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) { |
1564 | 0 | continue; |
1565 | 0 | } |
1566 | 0 | |
1567 | 0 | nsAutoString value; |
1568 | 0 | GetAttr(kNameSpaceID_None, attr, value); |
1569 | 0 | SetEventHandler(attr, value, true); |
1570 | 0 | } |
1571 | 0 | } |
1572 | | |
1573 | | bool |
1574 | | nsXULElement::IsEventAttributeNameInternal(nsAtom *aName) |
1575 | 0 | { |
1576 | 0 | return nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL); |
1577 | 0 | } |
1578 | | |
1579 | | JSObject* |
1580 | | nsXULElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
1581 | 0 | { |
1582 | 0 | return dom::XULElement_Binding::Wrap(aCx, this, aGivenProto); |
1583 | 0 | } |
1584 | | |
1585 | | NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode) |
1586 | | |
1587 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode) |
1588 | 0 | if (tmp->mType == nsXULPrototypeNode::eType_Element) { |
1589 | 0 | static_cast<nsXULPrototypeElement*>(tmp)->Unlink(); |
1590 | 0 | } else if (tmp->mType == nsXULPrototypeNode::eType_Script) { |
1591 | 0 | static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects(); |
1592 | 0 | } |
1593 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
1594 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode) |
1595 | 0 | if (tmp->mType == nsXULPrototypeNode::eType_Element) { |
1596 | 0 | nsXULPrototypeElement *elem = |
1597 | 0 | static_cast<nsXULPrototypeElement*>(tmp); |
1598 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo"); |
1599 | 0 | cb.NoteNativeChild(elem->mNodeInfo, |
1600 | 0 | NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo)); |
1601 | 0 | uint32_t i; |
1602 | 0 | for (i = 0; i < elem->mNumAttributes; ++i) { |
1603 | 0 | const nsAttrName& name = elem->mAttributes[i].mName; |
1604 | 0 | if (!name.IsAtom()) { |
1605 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, |
1606 | 0 | "mAttributes[i].mName.NodeInfo()"); |
1607 | 0 | cb.NoteNativeChild(name.NodeInfo(), |
1608 | 0 | NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo)); |
1609 | 0 | } |
1610 | 0 | } |
1611 | 0 | ImplCycleCollectionTraverse(cb, elem->mChildren, "mChildren"); |
1612 | 0 | } |
1613 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
1614 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode) |
1615 | 0 | if (tmp->mType == nsXULPrototypeNode::eType_Script) { |
1616 | 0 | nsXULPrototypeScript *script = |
1617 | 0 | static_cast<nsXULPrototypeScript*>(tmp); |
1618 | 0 | script->Trace(aCallbacks, aClosure); |
1619 | 0 | } |
1620 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
1621 | | |
1622 | | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef) |
1623 | | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release) |
1624 | | |
1625 | | //---------------------------------------------------------------------- |
1626 | | // |
1627 | | // nsXULPrototypeAttribute |
1628 | | // |
1629 | | |
1630 | | nsXULPrototypeAttribute::~nsXULPrototypeAttribute() |
1631 | 0 | { |
1632 | 0 | MOZ_COUNT_DTOR(nsXULPrototypeAttribute); |
1633 | 0 | } |
1634 | | |
1635 | | |
1636 | | //---------------------------------------------------------------------- |
1637 | | // |
1638 | | // nsXULPrototypeElement |
1639 | | // |
1640 | | |
1641 | | nsresult |
1642 | | nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream, |
1643 | | nsXULPrototypeDocument* aProtoDoc, |
1644 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
1645 | 0 | { |
1646 | 0 | nsresult rv; |
1647 | 0 |
|
1648 | 0 | // Write basic prototype data |
1649 | 0 | rv = aStream->Write32(mType); |
1650 | 0 |
|
1651 | 0 | // Write Node Info |
1652 | 0 | int32_t index = aNodeInfos->IndexOf(mNodeInfo); |
1653 | 0 | NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index"); |
1654 | 0 | nsresult tmp = aStream->Write32(index); |
1655 | 0 | if (NS_FAILED(tmp)) { |
1656 | 0 | rv = tmp; |
1657 | 0 | } |
1658 | 0 |
|
1659 | 0 | // Write Attributes |
1660 | 0 | tmp = aStream->Write32(mNumAttributes); |
1661 | 0 | if (NS_FAILED(tmp)) { |
1662 | 0 | rv = tmp; |
1663 | 0 | } |
1664 | 0 |
|
1665 | 0 | nsAutoString attributeValue; |
1666 | 0 | uint32_t i; |
1667 | 0 | for (i = 0; i < mNumAttributes; ++i) { |
1668 | 0 | RefPtr<mozilla::dom::NodeInfo> ni; |
1669 | 0 | if (mAttributes[i].mName.IsAtom()) { |
1670 | 0 | ni = mNodeInfo->NodeInfoManager()-> |
1671 | 0 | GetNodeInfo(mAttributes[i].mName.Atom(), nullptr, |
1672 | 0 | kNameSpaceID_None, nsINode::ATTRIBUTE_NODE); |
1673 | 0 | NS_ASSERTION(ni, "the nodeinfo should already exist"); |
1674 | 0 | } else { |
1675 | 0 | ni = mAttributes[i].mName.NodeInfo(); |
1676 | 0 | } |
1677 | 0 |
|
1678 | 0 | index = aNodeInfos->IndexOf(ni); |
1679 | 0 | NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index"); |
1680 | 0 | tmp = aStream->Write32(index); |
1681 | 0 | if (NS_FAILED(tmp)) { |
1682 | 0 | rv = tmp; |
1683 | 0 | } |
1684 | 0 |
|
1685 | 0 | mAttributes[i].mValue.ToString(attributeValue); |
1686 | 0 | tmp = aStream->WriteWStringZ(attributeValue.get()); |
1687 | 0 | if (NS_FAILED(tmp)) { |
1688 | 0 | rv = tmp; |
1689 | 0 | } |
1690 | 0 | } |
1691 | 0 |
|
1692 | 0 | // Now write children |
1693 | 0 | tmp = aStream->Write32(uint32_t(mChildren.Length())); |
1694 | 0 | if (NS_FAILED(tmp)) { |
1695 | 0 | rv = tmp; |
1696 | 0 | } |
1697 | 0 | for (i = 0; i < mChildren.Length(); i++) { |
1698 | 0 | nsXULPrototypeNode* child = mChildren[i].get(); |
1699 | 0 | switch (child->mType) { |
1700 | 0 | case eType_Element: |
1701 | 0 | case eType_Text: |
1702 | 0 | case eType_PI: |
1703 | 0 | tmp = child->Serialize(aStream, aProtoDoc, aNodeInfos); |
1704 | 0 | if (NS_FAILED(tmp)) { |
1705 | 0 | rv = tmp; |
1706 | 0 | } |
1707 | 0 | break; |
1708 | 0 | case eType_Script: |
1709 | 0 | tmp = aStream->Write32(child->mType); |
1710 | 0 | if (NS_FAILED(tmp)) { |
1711 | 0 | rv = tmp; |
1712 | 0 | } |
1713 | 0 | nsXULPrototypeScript* script = static_cast<nsXULPrototypeScript*>(child); |
1714 | 0 |
|
1715 | 0 | tmp = aStream->Write8(script->mOutOfLine); |
1716 | 0 | if (NS_FAILED(tmp)) { |
1717 | 0 | rv = tmp; |
1718 | 0 | } |
1719 | 0 | if (! script->mOutOfLine) { |
1720 | 0 | tmp = script->Serialize(aStream, aProtoDoc, aNodeInfos); |
1721 | 0 | if (NS_FAILED(tmp)) { |
1722 | 0 | rv = tmp; |
1723 | 0 | } |
1724 | 0 | } else { |
1725 | 0 | tmp = aStream->WriteCompoundObject(script->mSrcURI, |
1726 | 0 | NS_GET_IID(nsIURI), |
1727 | 0 | true); |
1728 | 0 | if (NS_FAILED(tmp)) { |
1729 | 0 | rv = tmp; |
1730 | 0 | } |
1731 | 0 |
|
1732 | 0 | if (script->HasScriptObject()) { |
1733 | 0 | // This may return NS_OK without muxing script->mSrcURI's |
1734 | 0 | // data into the cache file, in the case where that |
1735 | 0 | // muxed document is already there (written by a prior |
1736 | 0 | // session, or by an earlier cache episode during this |
1737 | 0 | // session). |
1738 | 0 | tmp = script->SerializeOutOfLine(aStream, aProtoDoc); |
1739 | 0 | if (NS_FAILED(tmp)) { |
1740 | 0 | rv = tmp; |
1741 | 0 | } |
1742 | 0 | } |
1743 | 0 | } |
1744 | 0 | break; |
1745 | 0 | } |
1746 | 0 | } |
1747 | 0 |
|
1748 | 0 | return rv; |
1749 | 0 | } |
1750 | | |
1751 | | nsresult |
1752 | | nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream, |
1753 | | nsXULPrototypeDocument* aProtoDoc, |
1754 | | nsIURI* aDocumentURI, |
1755 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
1756 | 0 | { |
1757 | 0 | MOZ_ASSERT(aNodeInfos, "missing nodeinfo array"); |
1758 | 0 |
|
1759 | 0 | // Read Node Info |
1760 | 0 | uint32_t number = 0; |
1761 | 0 | nsresult rv = aStream->Read32(&number); |
1762 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1763 | 0 | mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr); |
1764 | 0 | if (!mNodeInfo) { |
1765 | 0 | return NS_ERROR_UNEXPECTED; |
1766 | 0 | } |
1767 | 0 | |
1768 | 0 | // Read Attributes |
1769 | 0 | rv = aStream->Read32(&number); |
1770 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1771 | 0 | mNumAttributes = int32_t(number); |
1772 | 0 |
|
1773 | 0 | if (mNumAttributes > 0) { |
1774 | 0 | mAttributes = new (fallible) nsXULPrototypeAttribute[mNumAttributes]; |
1775 | 0 | if (!mAttributes) { |
1776 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1777 | 0 | } |
1778 | 0 | |
1779 | 0 | nsAutoString attributeValue; |
1780 | 0 | for (uint32_t i = 0; i < mNumAttributes; ++i) { |
1781 | 0 | rv = aStream->Read32(&number); |
1782 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1783 | 0 | mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr); |
1784 | 0 | if (!ni) { |
1785 | 0 | return NS_ERROR_UNEXPECTED; |
1786 | 0 | } |
1787 | 0 | |
1788 | 0 | mAttributes[i].mName.SetTo(ni); |
1789 | 0 |
|
1790 | 0 | rv = aStream->ReadString(attributeValue); |
1791 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1792 | 0 | rv = SetAttrAt(i, attributeValue, aDocumentURI); |
1793 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1794 | 0 | } |
1795 | 0 | } |
1796 | 0 |
|
1797 | 0 | rv = aStream->Read32(&number); |
1798 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1799 | 0 | uint32_t numChildren = int32_t(number); |
1800 | 0 |
|
1801 | 0 | if (numChildren > 0) { |
1802 | 0 | if (!mChildren.SetCapacity(numChildren, fallible)) { |
1803 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1804 | 0 | } |
1805 | 0 | |
1806 | 0 | for (uint32_t i = 0; i < numChildren; i++) { |
1807 | 0 | rv = aStream->Read32(&number); |
1808 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1809 | 0 | Type childType = (Type)number; |
1810 | 0 |
|
1811 | 0 | RefPtr<nsXULPrototypeNode> child; |
1812 | 0 |
|
1813 | 0 | switch (childType) { |
1814 | 0 | case eType_Element: |
1815 | 0 | child = new nsXULPrototypeElement(); |
1816 | 0 | rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, |
1817 | 0 | aNodeInfos); |
1818 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1819 | 0 | break; |
1820 | 0 | case eType_Text: |
1821 | 0 | child = new nsXULPrototypeText(); |
1822 | 0 | rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, |
1823 | 0 | aNodeInfos); |
1824 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1825 | 0 | break; |
1826 | 0 | case eType_PI: |
1827 | 0 | child = new nsXULPrototypePI(); |
1828 | 0 | rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, |
1829 | 0 | aNodeInfos); |
1830 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1831 | 0 | break; |
1832 | 0 | case eType_Script: { |
1833 | 0 | // language version/options obtained during deserialization. |
1834 | 0 | RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(0); |
1835 | 0 |
|
1836 | 0 | rv = aStream->ReadBoolean(&script->mOutOfLine); |
1837 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1838 | 0 | if (!script->mOutOfLine) { |
1839 | 0 | rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI, |
1840 | 0 | aNodeInfos); |
1841 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1842 | 0 | } else { |
1843 | 0 | nsCOMPtr<nsISupports> supports; |
1844 | 0 | rv = aStream->ReadObject(true, getter_AddRefs(supports)); |
1845 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1846 | 0 | script->mSrcURI = do_QueryInterface(supports); |
1847 | 0 |
|
1848 | 0 | rv = script->DeserializeOutOfLine(aStream, aProtoDoc); |
1849 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) return rv; |
1850 | 0 | } |
1851 | 0 | |
1852 | 0 | child = script.forget(); |
1853 | 0 | break; |
1854 | 0 | } |
1855 | 0 | default: |
1856 | 0 | MOZ_ASSERT(false, "Unexpected child type!"); |
1857 | 0 | return NS_ERROR_UNEXPECTED; |
1858 | 0 | } |
1859 | 0 |
|
1860 | 0 | MOZ_ASSERT(child, "Don't append null to mChildren"); |
1861 | 0 | MOZ_ASSERT(child->mType == childType); |
1862 | 0 | mChildren.AppendElement(child); |
1863 | 0 |
|
1864 | 0 | // Oh dear. Something failed during the deserialization. |
1865 | 0 | // We don't know what. But likely consequences of failed |
1866 | 0 | // deserializations included calls to |AbortCaching| which |
1867 | 0 | // shuts down the cache and closes our streams. |
1868 | 0 | // If that happens, next time through this loop, we die a messy |
1869 | 0 | // death. So, let's just fail now, and propagate that failure |
1870 | 0 | // upward so that the ChromeProtocolHandler knows it can't use |
1871 | 0 | // a cached chrome channel for this. |
1872 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) |
1873 | 0 | return rv; |
1874 | 0 | } |
1875 | 0 | } |
1876 | 0 |
|
1877 | 0 | return rv; |
1878 | 0 | } |
1879 | | |
1880 | | nsresult |
1881 | | nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue, |
1882 | | nsIURI* aDocumentURI) |
1883 | 0 | { |
1884 | 0 | MOZ_ASSERT(aPos < mNumAttributes, "out-of-bounds"); |
1885 | 0 |
|
1886 | 0 | // WARNING!! |
1887 | 0 | // This code is largely duplicated in nsXULElement::SetAttr. |
1888 | 0 | // Any changes should be made to both functions. |
1889 | 0 |
|
1890 | 0 | if (!mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) { |
1891 | 0 | mAttributes[aPos].mValue.ParseStringOrAtom(aValue); |
1892 | 0 |
|
1893 | 0 | return NS_OK; |
1894 | 0 | } |
1895 | 0 | |
1896 | 0 | if (mAttributes[aPos].mName.Equals(nsGkAtoms::id) && |
1897 | 0 | !aValue.IsEmpty()) { |
1898 | 0 | mHasIdAttribute = true; |
1899 | 0 | // Store id as atom. |
1900 | 0 | // id="" means that the element has no id. Not that it has |
1901 | 0 | // emptystring as id. |
1902 | 0 | mAttributes[aPos].mValue.ParseAtom(aValue); |
1903 | 0 |
|
1904 | 0 | return NS_OK; |
1905 | 0 | } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::is)) { |
1906 | 0 | // Store is as atom. |
1907 | 0 | mAttributes[aPos].mValue.ParseAtom(aValue); |
1908 | 0 | mIsAtom = mAttributes[aPos].mValue.GetAtomValue(); |
1909 | 0 |
|
1910 | 0 | return NS_OK; |
1911 | 0 | } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::_class)) { |
1912 | 0 | mHasClassAttribute = true; |
1913 | 0 | // Compute the element's class list |
1914 | 0 | mAttributes[aPos].mValue.ParseAtomArray(aValue); |
1915 | 0 |
|
1916 | 0 | return NS_OK; |
1917 | 0 | } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) { |
1918 | 0 | mHasStyleAttribute = true; |
1919 | 0 | // Parse the element's 'style' attribute |
1920 | 0 |
|
1921 | 0 | // This is basically duplicating what nsINode::NodePrincipal() does |
1922 | 0 | nsIPrincipal* principal = |
1923 | 0 | mNodeInfo->NodeInfoManager()->DocumentPrincipal(); |
1924 | 0 | // XXX Get correct Base URI (need GetBaseURI on *prototype* element) |
1925 | 0 | // TODO: If we implement Content Security Policy for chrome documents |
1926 | 0 | // as has been discussed, the CSP should be checked here to see if |
1927 | 0 | // inline styles are allowed to be applied. |
1928 | 0 | // XXX No specific specs talk about xul and referrer policy, pass Unset |
1929 | 0 | RefPtr<URLExtraData> data = |
1930 | 0 | new URLExtraData(aDocumentURI, aDocumentURI, principal, |
1931 | 0 | mozilla::net::RP_Unset); |
1932 | 0 | RefPtr<DeclarationBlock> declaration = |
1933 | 0 | DeclarationBlock::FromCssText( |
1934 | 0 | aValue, data, eCompatibility_FullStandards, nullptr); |
1935 | 0 | if (declaration) { |
1936 | 0 | mAttributes[aPos].mValue.SetTo(declaration.forget(), &aValue); |
1937 | 0 |
|
1938 | 0 | return NS_OK; |
1939 | 0 | } |
1940 | 0 | // Don't abort if parsing failed, it could just be malformed css. |
1941 | 0 | } |
1942 | 0 | |
1943 | 0 | mAttributes[aPos].mValue.ParseStringOrAtom(aValue); |
1944 | 0 |
|
1945 | 0 | return NS_OK; |
1946 | 0 | } |
1947 | | |
1948 | | void |
1949 | | nsXULPrototypeElement::Unlink() |
1950 | 0 | { |
1951 | 0 | mNumAttributes = 0; |
1952 | 0 | delete[] mAttributes; |
1953 | 0 | mAttributes = nullptr; |
1954 | 0 | mChildren.Clear(); |
1955 | 0 | } |
1956 | | |
1957 | | void |
1958 | | nsXULPrototypeElement::TraceAllScripts(JSTracer* aTrc) |
1959 | 0 | { |
1960 | 0 | for (uint32_t i = 0; i < mChildren.Length(); ++i) { |
1961 | 0 | nsXULPrototypeNode* child = mChildren[i]; |
1962 | 0 | if (child->mType == nsXULPrototypeNode::eType_Element) { |
1963 | 0 | static_cast<nsXULPrototypeElement*>(child)->TraceAllScripts(aTrc); |
1964 | 0 | } else if (child->mType == nsXULPrototypeNode::eType_Script) { |
1965 | 0 | static_cast<nsXULPrototypeScript*>(child)->TraceScriptObject(aTrc); |
1966 | 0 | } |
1967 | 0 | } |
1968 | 0 | } |
1969 | | |
1970 | | //---------------------------------------------------------------------- |
1971 | | // |
1972 | | // nsXULPrototypeScript |
1973 | | // |
1974 | | |
1975 | | nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo) |
1976 | | : nsXULPrototypeNode(eType_Script), |
1977 | | mLineNo(aLineNo), |
1978 | | mSrcLoading(false), |
1979 | | mOutOfLine(true), |
1980 | | mSrcLoadWaiters(nullptr), |
1981 | | mScriptObject(nullptr) |
1982 | 0 | { |
1983 | 0 | } |
1984 | | |
1985 | | |
1986 | | nsXULPrototypeScript::~nsXULPrototypeScript() |
1987 | 0 | { |
1988 | 0 | UnlinkJSObjects(); |
1989 | 0 | } |
1990 | | |
1991 | | nsresult |
1992 | | nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream, |
1993 | | nsXULPrototypeDocument* aProtoDoc, |
1994 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
1995 | 0 | { |
1996 | 0 | NS_ENSURE_TRUE(aProtoDoc, NS_ERROR_UNEXPECTED); |
1997 | 0 |
|
1998 | 0 | AutoJSAPI jsapi; |
1999 | 0 | if (!jsapi.Init(xpc::CompilationScope())) { |
2000 | 0 | return NS_ERROR_UNEXPECTED; |
2001 | 0 | } |
2002 | 0 | |
2003 | 0 | NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || |
2004 | 0 | !mScriptObject, |
2005 | 0 | "script source still loading when serializing?!"); |
2006 | 0 | if (!mScriptObject) |
2007 | 0 | return NS_ERROR_FAILURE; |
2008 | 0 | |
2009 | 0 | // Write basic prototype data |
2010 | 0 | nsresult rv; |
2011 | 0 | rv = aStream->Write32(mLineNo); |
2012 | 0 | if (NS_FAILED(rv)) return rv; |
2013 | 0 | rv = aStream->Write32(0); // See bug 1418294. |
2014 | 0 | if (NS_FAILED(rv)) return rv; |
2015 | 0 | |
2016 | 0 | JSContext* cx = jsapi.cx(); |
2017 | 0 | JS::Rooted<JSScript*> script(cx, mScriptObject); |
2018 | 0 | MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx)); |
2019 | 0 | return nsContentUtils::XPConnect()->WriteScript(aStream, cx, script); |
2020 | 0 | } |
2021 | | |
2022 | | nsresult |
2023 | | nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream, |
2024 | | nsXULPrototypeDocument* aProtoDoc) |
2025 | 0 | { |
2026 | 0 | nsresult rv = NS_ERROR_NOT_IMPLEMENTED; |
2027 | 0 |
|
2028 | 0 | bool isChrome = false; |
2029 | 0 | if (NS_FAILED(mSrcURI->SchemeIs("chrome", &isChrome)) || !isChrome) |
2030 | 0 | // Don't cache scripts that don't come from chrome uris. |
2031 | 0 | return rv; |
2032 | 0 | |
2033 | 0 | nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); |
2034 | 0 | if (!cache) |
2035 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
2036 | 0 | |
2037 | 0 | NS_ASSERTION(cache->IsEnabled(), |
2038 | 0 | "writing to the cache file, but the XUL cache is off?"); |
2039 | 0 | bool exists; |
2040 | 0 | cache->HasData(mSrcURI, &exists); |
2041 | 0 |
|
2042 | 0 | /* return will be NS_OK from GetAsciiSpec. |
2043 | 0 | * that makes no sense. |
2044 | 0 | * nor does returning NS_OK from HasMuxedDocument. |
2045 | 0 | * XXX return something meaningful. |
2046 | 0 | */ |
2047 | 0 | if (exists) |
2048 | 0 | return NS_OK; |
2049 | 0 | |
2050 | 0 | nsCOMPtr<nsIObjectOutputStream> oos; |
2051 | 0 | rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos)); |
2052 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2053 | 0 |
|
2054 | 0 | nsresult tmp = Serialize(oos, aProtoDoc, nullptr); |
2055 | 0 | if (NS_FAILED(tmp)) { |
2056 | 0 | rv = tmp; |
2057 | 0 | } |
2058 | 0 | tmp = cache->FinishOutputStream(mSrcURI); |
2059 | 0 | if (NS_FAILED(tmp)) { |
2060 | 0 | rv = tmp; |
2061 | 0 | } |
2062 | 0 |
|
2063 | 0 | if (NS_FAILED(rv)) |
2064 | 0 | cache->AbortCaching(); |
2065 | 0 | return rv; |
2066 | 0 | } |
2067 | | |
2068 | | |
2069 | | nsresult |
2070 | | nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream, |
2071 | | nsXULPrototypeDocument* aProtoDoc, |
2072 | | nsIURI* aDocumentURI, |
2073 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
2074 | 0 | { |
2075 | 0 | nsresult rv; |
2076 | 0 | NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || |
2077 | 0 | !mScriptObject, |
2078 | 0 | "prototype script not well-initialized when deserializing?!"); |
2079 | 0 |
|
2080 | 0 | // Read basic prototype data |
2081 | 0 | rv = aStream->Read32(&mLineNo); |
2082 | 0 | if (NS_FAILED(rv)) return rv; |
2083 | 0 | uint32_t dummy; |
2084 | 0 | rv = aStream->Read32(&dummy); // See bug 1418294. |
2085 | 0 | if (NS_FAILED(rv)) return rv; |
2086 | 0 | |
2087 | 0 | AutoJSAPI jsapi; |
2088 | 0 | if (!jsapi.Init(xpc::CompilationScope())) { |
2089 | 0 | return NS_ERROR_UNEXPECTED; |
2090 | 0 | } |
2091 | 0 | JSContext* cx = jsapi.cx(); |
2092 | 0 |
|
2093 | 0 | JS::Rooted<JSScript*> newScriptObject(cx); |
2094 | 0 | rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx, |
2095 | 0 | newScriptObject.address()); |
2096 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2097 | 0 | Set(newScriptObject); |
2098 | 0 | return NS_OK; |
2099 | 0 | } |
2100 | | |
2101 | | |
2102 | | nsresult |
2103 | | nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput, |
2104 | | nsXULPrototypeDocument* aProtoDoc) |
2105 | 0 | { |
2106 | 0 | // Keep track of failure via rv, so we can |
2107 | 0 | // AbortCaching if things look bad. |
2108 | 0 | nsresult rv = NS_OK; |
2109 | 0 | nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); |
2110 | 0 |
|
2111 | 0 | nsCOMPtr<nsIObjectInputStream> objectInput = aInput; |
2112 | 0 | if (cache) { |
2113 | 0 | bool useXULCache = true; |
2114 | 0 | if (mSrcURI) { |
2115 | 0 | // NB: we must check the XUL script cache early, to avoid |
2116 | 0 | // multiple deserialization attempts for a given script. |
2117 | 0 | // Note that XULDocument::LoadScript |
2118 | 0 | // checks the XUL script cache too, in order to handle the |
2119 | 0 | // serialization case. |
2120 | 0 | // |
2121 | 0 | // We need do this only for <script src='strres.js'> and the |
2122 | 0 | // like, i.e., out-of-line scripts that are included by several |
2123 | 0 | // different XUL documents stored in the cache file. |
2124 | 0 | useXULCache = cache->IsEnabled(); |
2125 | 0 |
|
2126 | 0 | if (useXULCache) { |
2127 | 0 | JSScript* newScriptObject = |
2128 | 0 | cache->GetScript(mSrcURI); |
2129 | 0 | if (newScriptObject) |
2130 | 0 | Set(newScriptObject); |
2131 | 0 | } |
2132 | 0 | } |
2133 | 0 |
|
2134 | 0 | if (!mScriptObject) { |
2135 | 0 | if (mSrcURI) { |
2136 | 0 | rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput)); |
2137 | 0 | } |
2138 | 0 | // If !mSrcURI, we have an inline script. We shouldn't have |
2139 | 0 | // to do anything else in that case, I think. |
2140 | 0 |
|
2141 | 0 | // We do reflect errors into rv, but our caller may want to |
2142 | 0 | // ignore our return value, because mScriptObject will be null |
2143 | 0 | // after any error, and that suffices to cause the script to |
2144 | 0 | // be reloaded (from the src= URI, if any) and recompiled. |
2145 | 0 | // We're better off slow-loading than bailing out due to a |
2146 | 0 | // error. |
2147 | 0 | if (NS_SUCCEEDED(rv)) |
2148 | 0 | rv = Deserialize(objectInput, aProtoDoc, nullptr, nullptr); |
2149 | 0 |
|
2150 | 0 | if (NS_SUCCEEDED(rv)) { |
2151 | 0 | if (useXULCache && mSrcURI) { |
2152 | 0 | bool isChrome = false; |
2153 | 0 | mSrcURI->SchemeIs("chrome", &isChrome); |
2154 | 0 | if (isChrome) { |
2155 | 0 | JS::Rooted<JSScript*> script(RootingCx(), GetScriptObject()); |
2156 | 0 | cache->PutScript(mSrcURI, script); |
2157 | 0 | } |
2158 | 0 | } |
2159 | 0 | cache->FinishInputStream(mSrcURI); |
2160 | 0 | } else { |
2161 | 0 | // If mSrcURI is not in the cache, |
2162 | 0 | // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to |
2163 | 0 | // update the cache file to hold a serialization of |
2164 | 0 | // this script, once it has finished loading. |
2165 | 0 | if (rv != NS_ERROR_NOT_AVAILABLE) |
2166 | 0 | cache->AbortCaching(); |
2167 | 0 | } |
2168 | 0 | } |
2169 | 0 | } |
2170 | 0 | return rv; |
2171 | 0 | } |
2172 | | |
2173 | | class NotifyOffThreadScriptCompletedRunnable : public Runnable |
2174 | | { |
2175 | | // An array of all outstanding script receivers. All reference counting of |
2176 | | // these objects happens on the main thread. When we return to the main |
2177 | | // thread from script compilation we make sure our receiver is still in |
2178 | | // this array (still alive) before proceeding. This array is cleared during |
2179 | | // shutdown, potentially before all outstanding script compilations have |
2180 | | // finished. We do not need to worry about pointer replay here, because |
2181 | | // a) we should not be starting script compilation after clearing this |
2182 | | // array and b) in all other cases the receiver will still be alive. |
2183 | | static StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> sReceivers; |
2184 | | static bool sSetupClearOnShutdown; |
2185 | | |
2186 | | nsIOffThreadScriptReceiver* mReceiver; |
2187 | | JS::OffThreadToken* mToken; |
2188 | | |
2189 | | public: |
2190 | | NotifyOffThreadScriptCompletedRunnable(nsIOffThreadScriptReceiver* aReceiver, |
2191 | | JS::OffThreadToken* aToken) |
2192 | | : mozilla::Runnable("NotifyOffThreadScriptCompletedRunnable") |
2193 | | , mReceiver(aReceiver) |
2194 | | , mToken(aToken) |
2195 | 0 | { |
2196 | 0 | } |
2197 | | |
2198 | | static void NoteReceiver(nsIOffThreadScriptReceiver* aReceiver) |
2199 | 0 | { |
2200 | 0 | if (!sSetupClearOnShutdown) { |
2201 | 0 | ClearOnShutdown(&sReceivers); |
2202 | 0 | sSetupClearOnShutdown = true; |
2203 | 0 | sReceivers = new nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>(); |
2204 | 0 | } |
2205 | 0 |
|
2206 | 0 | // If we ever crash here, it's because we tried to lazy compile script |
2207 | 0 | // too late in shutdown. |
2208 | 0 | sReceivers->AppendElement(aReceiver); |
2209 | 0 | } |
2210 | | |
2211 | | NS_DECL_NSIRUNNABLE |
2212 | | }; |
2213 | | |
2214 | | StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> NotifyOffThreadScriptCompletedRunnable::sReceivers; |
2215 | | bool NotifyOffThreadScriptCompletedRunnable::sSetupClearOnShutdown = false; |
2216 | | |
2217 | | NS_IMETHODIMP |
2218 | | NotifyOffThreadScriptCompletedRunnable::Run() |
2219 | 0 | { |
2220 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2221 | 0 |
|
2222 | 0 | JS::Rooted<JSScript*> script(RootingCx()); |
2223 | 0 | { |
2224 | 0 | AutoJSAPI jsapi; |
2225 | 0 | if (!jsapi.Init(xpc::CompilationScope())) { |
2226 | 0 | // Now what? I guess we just leak... this should probably never |
2227 | 0 | // happen. |
2228 | 0 | return NS_ERROR_UNEXPECTED; |
2229 | 0 | } |
2230 | 0 | JSContext* cx = jsapi.cx(); |
2231 | 0 | script = JS::FinishOffThreadScript(cx, mToken); |
2232 | 0 | } |
2233 | 0 |
|
2234 | 0 | if (!sReceivers) { |
2235 | 0 | // We've already shut down. |
2236 | 0 | return NS_OK; |
2237 | 0 | } |
2238 | 0 | |
2239 | 0 | auto index = sReceivers->IndexOf(mReceiver); |
2240 | 0 | MOZ_RELEASE_ASSERT(index != sReceivers->NoIndex); |
2241 | 0 | nsCOMPtr<nsIOffThreadScriptReceiver> receiver = (*sReceivers)[index].forget(); |
2242 | 0 | sReceivers->RemoveElementAt(index); |
2243 | 0 |
|
2244 | 0 | return receiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE); |
2245 | 0 | } |
2246 | | |
2247 | | static void |
2248 | | OffThreadScriptReceiverCallback(JS::OffThreadToken* aToken, void* aCallbackData) |
2249 | 0 | { |
2250 | 0 | // Be careful not to adjust the refcount on the receiver, as this callback |
2251 | 0 | // may be invoked off the main thread. |
2252 | 0 | nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData); |
2253 | 0 | RefPtr<NotifyOffThreadScriptCompletedRunnable> notify = |
2254 | 0 | new NotifyOffThreadScriptCompletedRunnable(aReceiver, aToken); |
2255 | 0 | NS_DispatchToMainThread(notify); |
2256 | 0 | } |
2257 | | |
2258 | | nsresult |
2259 | | nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf, |
2260 | | nsIURI* aURI, uint32_t aLineNo, |
2261 | | nsIDocument* aDocument, |
2262 | | nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */) |
2263 | 0 | { |
2264 | 0 | // We'll compile the script in the compilation scope. |
2265 | 0 | AutoJSAPI jsapi; |
2266 | 0 | if (!jsapi.Init(xpc::CompilationScope())) { |
2267 | 0 | return NS_ERROR_UNEXPECTED; |
2268 | 0 | } |
2269 | 0 | JSContext* cx = jsapi.cx(); |
2270 | 0 |
|
2271 | 0 | nsresult rv; |
2272 | 0 | nsAutoCString urlspec; |
2273 | 0 | nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec, &rv); |
2274 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2275 | 0 | return rv; |
2276 | 0 | } |
2277 | 0 | |
2278 | 0 | // Ok, compile it to create a prototype script object! |
2279 | 0 | JS::CompileOptions options(cx); |
2280 | 0 | options.setIntroductionType("scriptElement") |
2281 | 0 | .setFileAndLine(urlspec.get(), aLineNo); |
2282 | 0 | // If the script was inline, tell the JS parser to save source for |
2283 | 0 | // Function.prototype.toSource(). If it's out of line, we retrieve the |
2284 | 0 | // source from the files on demand. |
2285 | 0 | options.setSourceIsLazy(mOutOfLine); |
2286 | 0 | JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx)); |
2287 | 0 | if (scope) { |
2288 | 0 | JS::ExposeObjectToActiveJS(scope); |
2289 | 0 | } |
2290 | 0 |
|
2291 | 0 | if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) { |
2292 | 0 | if (!JS::CompileOffThread(cx, options, |
2293 | 0 | aSrcBuf, |
2294 | 0 | OffThreadScriptReceiverCallback, |
2295 | 0 | static_cast<void*>(aOffThreadReceiver))) { |
2296 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
2297 | 0 | } |
2298 | 0 | NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver); |
2299 | 0 | } else { |
2300 | 0 | JS::Rooted<JSScript*> script(cx); |
2301 | 0 | if (!JS::Compile(cx, options, aSrcBuf, &script)) |
2302 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
2303 | 0 | Set(script); |
2304 | 0 | } |
2305 | 0 | return NS_OK; |
2306 | 0 | } |
2307 | | |
2308 | | nsresult |
2309 | | nsXULPrototypeScript::Compile(const char16_t* aText, |
2310 | | int32_t aTextLength, |
2311 | | nsIURI* aURI, |
2312 | | uint32_t aLineNo, |
2313 | | nsIDocument* aDocument, |
2314 | | nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */) |
2315 | 0 | { |
2316 | 0 | JS::SourceBufferHolder srcBuf(aText, aTextLength, |
2317 | 0 | JS::SourceBufferHolder::NoOwnership); |
2318 | 0 | return Compile(srcBuf, aURI, aLineNo, aDocument, aOffThreadReceiver); |
2319 | 0 | } |
2320 | | |
2321 | | void |
2322 | | nsXULPrototypeScript::UnlinkJSObjects() |
2323 | 0 | { |
2324 | 0 | if (mScriptObject) { |
2325 | 0 | mScriptObject = nullptr; |
2326 | 0 | mozilla::DropJSObjects(this); |
2327 | 0 | } |
2328 | 0 | } |
2329 | | |
2330 | | void |
2331 | | nsXULPrototypeScript::Set(JSScript* aObject) |
2332 | 0 | { |
2333 | 0 | MOZ_ASSERT(!mScriptObject, "Leaking script object."); |
2334 | 0 | if (!aObject) { |
2335 | 0 | mScriptObject = nullptr; |
2336 | 0 | return; |
2337 | 0 | } |
2338 | 0 | |
2339 | 0 | mScriptObject = aObject; |
2340 | 0 | mozilla::HoldJSObjects(this); |
2341 | 0 | } |
2342 | | |
2343 | | //---------------------------------------------------------------------- |
2344 | | // |
2345 | | // nsXULPrototypeText |
2346 | | // |
2347 | | |
2348 | | nsresult |
2349 | | nsXULPrototypeText::Serialize(nsIObjectOutputStream* aStream, |
2350 | | nsXULPrototypeDocument* aProtoDoc, |
2351 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
2352 | 0 | { |
2353 | 0 | nsresult rv; |
2354 | 0 |
|
2355 | 0 | // Write basic prototype data |
2356 | 0 | rv = aStream->Write32(mType); |
2357 | 0 |
|
2358 | 0 | nsresult tmp = aStream->WriteWStringZ(mValue.get()); |
2359 | 0 | if (NS_FAILED(tmp)) { |
2360 | 0 | rv = tmp; |
2361 | 0 | } |
2362 | 0 |
|
2363 | 0 | return rv; |
2364 | 0 | } |
2365 | | |
2366 | | nsresult |
2367 | | nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream, |
2368 | | nsXULPrototypeDocument* aProtoDoc, |
2369 | | nsIURI* aDocumentURI, |
2370 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
2371 | 0 | { |
2372 | 0 | nsresult rv = aStream->ReadString(mValue); |
2373 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2374 | 0 | return rv; |
2375 | 0 | } |
2376 | 0 | return NS_OK; |
2377 | 0 | } |
2378 | | |
2379 | | //---------------------------------------------------------------------- |
2380 | | // |
2381 | | // nsXULPrototypePI |
2382 | | // |
2383 | | |
2384 | | nsresult |
2385 | | nsXULPrototypePI::Serialize(nsIObjectOutputStream* aStream, |
2386 | | nsXULPrototypeDocument* aProtoDoc, |
2387 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
2388 | 0 | { |
2389 | 0 | nsresult rv; |
2390 | 0 |
|
2391 | 0 | // Write basic prototype data |
2392 | 0 | rv = aStream->Write32(mType); |
2393 | 0 |
|
2394 | 0 | nsresult tmp = aStream->WriteWStringZ(mTarget.get()); |
2395 | 0 | if (NS_FAILED(tmp)) { |
2396 | 0 | rv = tmp; |
2397 | 0 | } |
2398 | 0 | tmp = aStream->WriteWStringZ(mData.get()); |
2399 | 0 | if (NS_FAILED(tmp)) { |
2400 | 0 | rv = tmp; |
2401 | 0 | } |
2402 | 0 |
|
2403 | 0 | return rv; |
2404 | 0 | } |
2405 | | |
2406 | | nsresult |
2407 | | nsXULPrototypePI::Deserialize(nsIObjectInputStream* aStream, |
2408 | | nsXULPrototypeDocument* aProtoDoc, |
2409 | | nsIURI* aDocumentURI, |
2410 | | const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) |
2411 | 0 | { |
2412 | 0 | nsresult rv; |
2413 | 0 |
|
2414 | 0 | rv = aStream->ReadString(mTarget); |
2415 | 0 | if (NS_FAILED(rv)) return rv; |
2416 | 0 | rv = aStream->ReadString(mData); |
2417 | 0 | if (NS_FAILED(rv)) return rv; |
2418 | 0 | |
2419 | 0 | return rv; |
2420 | 0 | } |