Limit user-agent interactions based on the touch-action property on iOS
[WebKit-https.git] / Source / WebCore / style / StyleTreeResolver.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Peter Kelly (pmk@post.com)
5  *           (C) 2001 Dirk Mueller (mueller@kde.org)
6  *           (C) 2007 David Smith (catfish.man@gmail.com)
7  * Copyright (C) 2004-2010, 2012-2016 Apple Inc. All rights reserved.
8  *           (C) 2007 Eric Seidel (eric@webkit.org)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "StyleTreeResolver.h"
28
29 #include "CSSAnimationController.h"
30 #include "CSSFontSelector.h"
31 #include "ComposedTreeAncestorIterator.h"
32 #include "ComposedTreeIterator.h"
33 #include "DocumentTimeline.h"
34 #include "ElementIterator.h"
35 #include "Frame.h"
36 #include "HTMLBodyElement.h"
37 #include "HTMLMeterElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLProgressElement.h"
40 #include "HTMLSlotElement.h"
41 #include "LoaderStrategy.h"
42 #include "NodeRenderStyle.h"
43 #include "Page.h"
44 #include "PlatformStrategies.h"
45 #include "RenderElement.h"
46 #include "RenderView.h"
47 #include "RuntimeEnabledFeatures.h"
48 #include "Settings.h"
49 #include "ShadowRoot.h"
50 #include "StyleFontSizeFunctions.h"
51 #include "StyleResolver.h"
52 #include "StyleScope.h"
53 #include "Text.h"
54
55 namespace WebCore {
56
57 namespace Style {
58
59 TreeResolver::TreeResolver(Document& document)
60     : m_document(document)
61 {
62 }
63
64 TreeResolver::~TreeResolver() = default;
65
66 TreeResolver::Scope::Scope(Document& document)
67     : styleResolver(document.styleScope().resolver())
68     , sharingResolver(document, styleResolver.ruleSets(), selectorFilter)
69 {
70     document.setIsResolvingTreeStyle(true);
71 }
72
73 TreeResolver::Scope::Scope(ShadowRoot& shadowRoot, Scope& enclosingScope)
74     : styleResolver(shadowRoot.styleScope().resolver())
75     , sharingResolver(shadowRoot.documentScope(), styleResolver.ruleSets(), selectorFilter)
76     , shadowRoot(&shadowRoot)
77     , enclosingScope(&enclosingScope)
78 {
79     styleResolver.setOverrideDocumentElementStyle(enclosingScope.styleResolver.overrideDocumentElementStyle());
80 }
81
82 TreeResolver::Scope::~Scope()
83 {
84     if (!shadowRoot)
85         styleResolver.document().setIsResolvingTreeStyle(false);
86
87     styleResolver.setOverrideDocumentElementStyle(nullptr);
88 }
89
90 TreeResolver::Parent::Parent(Document& document)
91     : element(nullptr)
92     , style(*document.renderStyle())
93 {
94 }
95
96 TreeResolver::Parent::Parent(Element& element, const RenderStyle& style, Change change, DescendantsToResolve descendantsToResolve)
97     : element(&element)
98     , style(style)
99     , change(change)
100     , descendantsToResolve(descendantsToResolve)
101 {
102 }
103
104 void TreeResolver::pushScope(ShadowRoot& shadowRoot)
105 {
106     m_scopeStack.append(adoptRef(*new Scope(shadowRoot, scope())));
107 }
108
109 void TreeResolver::pushEnclosingScope()
110 {
111     ASSERT(scope().enclosingScope);
112     m_scopeStack.append(*scope().enclosingScope);
113 }
114
115 void TreeResolver::popScope()
116 {
117     return m_scopeStack.removeLast();
118 }
119
120 std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle)
121 {
122     if (element.hasCustomStyleResolveCallbacks()) {
123         RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr;
124         if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) {
125             if (customStyle->relations)
126                 commitRelations(WTFMove(customStyle->relations), *m_update);
127
128             return WTFMove(customStyle->renderStyle);
129         }
130     }
131
132     if (auto style = scope().sharingResolver.resolve(element, *m_update))
133         return style;
134
135     auto elementStyle = scope().styleResolver.styleForElement(element, &inheritedStyle, parentBoxStyle(), RuleMatchingBehavior::MatchAllRules, &scope().selectorFilter);
136
137     if (elementStyle.relations)
138         commitRelations(WTFMove(elementStyle.relations), *m_update);
139
140     return WTFMove(elementStyle.renderStyle);
141 }
142
143 static void resetStyleForNonRenderedDescendants(Element& current)
144 {
145     for (auto& child : childrenOfType<Element>(current)) {
146         if (child.needsStyleRecalc()) {
147             child.resetComputedStyle();
148             child.resetStyleRelations();
149             child.setHasValidStyle();
150         }
151
152         if (child.childNeedsStyleRecalc())
153             resetStyleForNonRenderedDescendants(child);
154     }
155     current.clearChildNeedsStyleRecalc();
156 }
157
158 static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle)
159 {
160     if (element.renderer())
161         return true;
162     if (newStyle.display() != DisplayType::None)
163         return true;
164     if (element.rendererIsNeeded(newStyle))
165         return true;
166     return false;
167 }
168
169 static DescendantsToResolve computeDescendantsToResolve(Change change, Validity validity, DescendantsToResolve parentDescendantsToResolve)
170 {
171     if (parentDescendantsToResolve == DescendantsToResolve::All)
172         return DescendantsToResolve::All;
173     if (validity >= Validity::SubtreeInvalid)
174         return DescendantsToResolve::All;
175     switch (change) {
176     case NoChange:
177         return DescendantsToResolve::None;
178     case NoInherit:
179         return DescendantsToResolve::ChildrenWithExplicitInherit;
180     case Inherit:
181         return DescendantsToResolve::Children;
182     case Detach:
183         return DescendantsToResolve::All;
184     };
185     ASSERT_NOT_REACHED();
186     return DescendantsToResolve::None;
187 };
188
189 ElementUpdates TreeResolver::resolveElement(Element& element)
190 {
191     if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) {
192         m_document.setHasNodesWithMissingStyle();
193         return { };
194     }
195
196     auto newStyle = styleForElement(element, parent().style);
197
198     if (!affectsRenderedSubtree(element, *newStyle))
199         return { };
200
201     auto* existingStyle = element.renderOrDisplayContentsStyle();
202
203     if (m_didSeePendingStylesheet && (!existingStyle || existingStyle->isNotFinal())) {
204         newStyle->setIsNotFinal();
205         m_document.setHasNodesWithNonFinalStyle();
206     }
207
208     auto update = createAnimatedElementUpdate(WTFMove(newStyle), element, parent().change);
209     auto descendantsToResolve = computeDescendantsToResolve(update.change, element.styleValidity(), parent().descendantsToResolve);
210
211     if (&element == m_document.documentElement()) {
212         m_documentElementStyle = RenderStyle::clonePtr(*update.style);
213         scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());
214
215         if (update.change != NoChange && existingStyle && existingStyle->computedFontPixelSize() != update.style->computedFontPixelSize()) {
216             // "rem" units are relative to the document element's font size so we need to recompute everything.
217             // In practice this is rare.
218             scope().styleResolver.invalidateMatchedPropertiesCache();
219             descendantsToResolve = DescendantsToResolve::All;
220         }
221     }
222
223     // This is needed for resolving color:-webkit-text for subsequent elements.
224     // FIXME: We shouldn't mutate document when resolving style.
225     if (&element == m_document.body())
226         m_document.setTextColor(update.style->visitedDependentColor(CSSPropertyColor));
227
228     // FIXME: These elements should not change renderer based on appearance property.
229     if (element.hasTagName(HTMLNames::meterTag) || is<HTMLProgressElement>(element)) {
230         if (existingStyle && update.style->appearance() != existingStyle->appearance()) {
231             update.change = Detach;
232             descendantsToResolve = DescendantsToResolve::All;
233         }
234     }
235
236     auto beforeUpdate = resolvePseudoStyle(element, update, PseudoId::Before);
237     auto afterUpdate = resolvePseudoStyle(element, update, PseudoId::After);
238
239 #if ENABLE(POINTER_EVENTS)
240     if (RuntimeEnabledFeatures::sharedFeatures().pointerEventsEnabled())
241         m_document.updateTouchActionElements(element, *update.style.get());
242 #endif
243
244     return { WTFMove(update), descendantsToResolve, WTFMove(beforeUpdate), WTFMove(afterUpdate) };
245 }
246
247 ElementUpdate TreeResolver::resolvePseudoStyle(Element& element, const ElementUpdate& elementUpdate, PseudoId pseudoId)
248 {
249     if (elementUpdate.style->display() == DisplayType::None)
250         return { };
251     if (!elementUpdate.style->hasPseudoStyle(pseudoId))
252         return { };
253
254     auto pseudoStyle = scope().styleResolver.pseudoStyleForElement(element, { pseudoId }, *elementUpdate.style, &scope().selectorFilter);
255     if (!pseudoStyle)
256         return { };
257
258     PseudoElement* pseudoElement = pseudoId == PseudoId::Before ? element.beforePseudoElement() : element.afterPseudoElement();
259     if (!pseudoElement) {
260         auto newPseudoElement = PseudoElement::create(element, pseudoId);
261         pseudoElement = newPseudoElement.ptr();
262         if (pseudoId == PseudoId::Before)
263             element.setBeforePseudoElement(WTFMove(newPseudoElement));
264         else
265             element.setAfterPseudoElement(WTFMove(newPseudoElement));
266     }
267
268     return createAnimatedElementUpdate(WTFMove(pseudoStyle), *pseudoElement, elementUpdate.change);
269 }
270
271 const RenderStyle* TreeResolver::parentBoxStyle() const
272 {
273     // 'display: contents' doesn't generate boxes.
274     for (unsigned i = m_parentStack.size(); i; --i) {
275         auto& parent = m_parentStack[i - 1];
276         if (parent.style.display() == DisplayType::None)
277             return nullptr;
278         if (parent.style.display() != DisplayType::Contents)
279             return &parent.style;
280     }
281     ASSERT_NOT_REACHED();
282     return nullptr;
283 }
284
285 ElementUpdate TreeResolver::createAnimatedElementUpdate(std::unique_ptr<RenderStyle> newStyle, Element& element, Change parentChange)
286 {
287     auto* oldStyle = element.renderOrDisplayContentsStyle();
288
289     bool shouldRecompositeLayer = false;
290
291     // New code path for CSS Animations and CSS Transitions.
292     if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
293         // First, we need to make sure that any new CSS animation occuring on this element has a matching WebAnimation
294         // on the document timeline. Note that we get timeline() on the Document here because we need a timeline created
295         // in case no Web Animations have been created through the JS API.
296         if (element.document().pageCacheState() == Document::NotInPageCache && !element.document().renderView()->printing()) {
297             if (oldStyle && (oldStyle->hasTransitions() || newStyle->hasTransitions()))
298                 m_document.timeline().updateCSSTransitionsForElement(element, *oldStyle, *newStyle);
299
300             if ((oldStyle && oldStyle->hasAnimations()) || newStyle->hasAnimations())
301                 m_document.timeline().updateCSSAnimationsForElement(element, oldStyle, *newStyle);
302         }
303     }
304
305     if (auto timeline = m_document.existingTimeline()) {
306         // Now we can update all Web animations, which will include CSS Animations as well
307         // as animations created via the JS API.
308         auto animatedStyle = RenderStyle::clonePtr(*newStyle);
309         shouldRecompositeLayer = timeline->resolveAnimationsForElement(element, *animatedStyle);
310         newStyle = WTFMove(animatedStyle);
311     }
312
313     // Old code path for CSS Animations and CSS Transitions.
314     if (!RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
315         auto& animationController = m_document.frame()->animation();
316
317         auto animationUpdate = animationController.updateAnimations(element, *newStyle, oldStyle);
318         shouldRecompositeLayer = animationUpdate.animationChangeRequiresRecomposite;
319
320         if (animationUpdate.style)
321             newStyle = WTFMove(animationUpdate.style);
322     }
323
324     auto change = oldStyle ? determineChange(*oldStyle, *newStyle) : Detach;
325
326     auto validity = element.styleValidity();
327     if (validity >= Validity::SubtreeAndRenderersInvalid || parentChange == Detach)
328         change = Detach;
329
330     shouldRecompositeLayer |= element.styleResolutionShouldRecompositeLayer();
331
332     return { WTFMove(newStyle), change, shouldRecompositeLayer };
333 }
334
335 void TreeResolver::pushParent(Element& element, const RenderStyle& style, Change change, DescendantsToResolve descendantsToResolve)
336 {
337     scope().selectorFilter.pushParent(&element);
338
339     Parent parent(element, style, change, descendantsToResolve);
340
341     if (auto* shadowRoot = element.shadowRoot()) {
342         pushScope(*shadowRoot);
343         parent.didPushScope = true;
344     }
345     else if (is<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(element).assignedNodes()) {
346         pushEnclosingScope();
347         parent.didPushScope = true;
348     }
349
350     m_parentStack.append(WTFMove(parent));
351 }
352
353 void TreeResolver::popParent()
354 {
355     auto& parentElement = *parent().element;
356
357     parentElement.setHasValidStyle();
358     parentElement.clearChildNeedsStyleRecalc();
359
360     if (parent().didPushScope)
361         popScope();
362
363     scope().selectorFilter.popParent();
364
365     m_parentStack.removeLast();
366 }
367
368 void TreeResolver::popParentsToDepth(unsigned depth)
369 {
370     ASSERT(depth);
371     ASSERT(m_parentStack.size() >= depth);
372
373     while (m_parentStack.size() > depth)
374         popParent();
375 }
376
377 static bool shouldResolvePseudoElement(const PseudoElement* pseudoElement)
378 {
379     if (!pseudoElement)
380         return false;
381     return pseudoElement->needsStyleRecalc();
382 }
383
384 static bool shouldResolveElement(const Element& element, DescendantsToResolve parentDescendantsToResolve)
385 {
386     if (element.styleValidity() != Validity::Valid)
387         return true;
388     if (shouldResolvePseudoElement(element.beforePseudoElement()))
389         return true;
390     if (shouldResolvePseudoElement(element.afterPseudoElement()))
391         return true;
392
393     switch (parentDescendantsToResolve) {
394     case DescendantsToResolve::None:
395         return false;
396     case DescendantsToResolve::Children:
397     case DescendantsToResolve::All:
398         return true;
399     case DescendantsToResolve::ChildrenWithExplicitInherit:
400         auto* existingStyle = element.renderOrDisplayContentsStyle();
401         return existingStyle && existingStyle->hasExplicitlyInheritedProperties();
402     };
403     ASSERT_NOT_REACHED();
404     return false;
405 }
406
407 static void clearNeedsStyleResolution(Element& element)
408 {
409     element.setHasValidStyle();
410     if (auto* before = element.beforePseudoElement())
411         before->setHasValidStyle();
412     if (auto* after = element.afterPseudoElement())
413         after->setHasValidStyle();
414 }
415
416 static bool hasLoadingStylesheet(const Style::Scope& styleScope, const Element& element, bool checkDescendants)
417 {
418     if (!styleScope.hasPendingSheetsInBody())
419         return false;
420     if (styleScope.hasPendingSheetInBody(element))
421         return true;
422     if (!checkDescendants)
423         return false;
424     for (auto& descendant : descendantsOfType<Element>(element)) {
425         if (styleScope.hasPendingSheetInBody(descendant))
426             return true;
427     };
428     return false;
429 }
430
431 static std::unique_ptr<RenderStyle> createInheritedDisplayContentsStyleIfNeeded(const RenderStyle& parentElementStyle, const RenderStyle* parentBoxStyle)
432 {
433     if (parentElementStyle.display() != DisplayType::Contents)
434         return nullptr;
435     if (parentBoxStyle && !parentBoxStyle->inheritedNotEqual(&parentElementStyle))
436         return nullptr;
437     // Compute style for imaginary unstyled <span> around the text node.
438     auto style = RenderStyle::createPtr();
439     style->inheritFrom(parentElementStyle);
440     return style;
441 }
442
443 void TreeResolver::resolveComposedTree()
444 {
445     ASSERT(m_parentStack.size() == 1);
446     ASSERT(m_scopeStack.size() == 1);
447
448     auto descendants = composedTreeDescendants(m_document);
449     auto it = descendants.begin();
450     auto end = descendants.end();
451
452     while (it != end) {
453         popParentsToDepth(it.depth());
454
455         auto& node = *it;
456         auto& parent = this->parent();
457
458         ASSERT(node.isConnected());
459         ASSERT(node.containingShadowRoot() == scope().shadowRoot);
460         ASSERT(node.parentElement() == parent.element || is<ShadowRoot>(node.parentNode()) || node.parentElement()->shadowRoot());
461
462         if (is<Text>(node)) {
463             auto& text = downcast<Text>(node);
464             
465             if ((text.styleValidity() >= Validity::SubtreeAndRenderersInvalid && parent.change != Detach) || parent.style.display() == DisplayType::Contents) {
466                 TextUpdate textUpdate;
467                 textUpdate.inheritedDisplayContentsStyle = createInheritedDisplayContentsStyleIfNeeded(parent.style, parentBoxStyle());
468
469                 m_update->addText(text, parent.element, WTFMove(textUpdate));
470             }
471
472             text.setHasValidStyle();
473             it.traverseNextSkippingChildren();
474             continue;
475         }
476
477         auto& element = downcast<Element>(node);
478
479         if (it.depth() > Settings::defaultMaximumRenderTreeDepth) {
480             resetStyleForNonRenderedDescendants(element);
481             it.traverseNextSkippingChildren();
482             continue;
483         }
484
485         auto* style = element.renderOrDisplayContentsStyle();
486         auto change = NoChange;
487         auto descendantsToResolve = DescendantsToResolve::None;
488
489         bool shouldResolve = shouldResolveElement(element, parent.descendantsToResolve);
490         if (shouldResolve) {
491             if (!element.hasDisplayContents())
492                 element.resetComputedStyle();
493             element.resetStyleRelations();
494
495             if (element.hasCustomStyleResolveCallbacks())
496                 element.willRecalcStyle(parent.change);
497
498             auto elementUpdates = resolveElement(element);
499
500             if (element.hasCustomStyleResolveCallbacks())
501                 element.didRecalcStyle(elementUpdates.update.change);
502
503             style = elementUpdates.update.style.get();
504             change = elementUpdates.update.change;
505             descendantsToResolve = elementUpdates.descendantsToResolve;
506
507             if (elementUpdates.update.style)
508                 m_update->addElement(element, parent.element, WTFMove(elementUpdates));
509
510             clearNeedsStyleResolution(element);
511         }
512
513         if (!style)
514             resetStyleForNonRenderedDescendants(element);
515
516         bool shouldIterateChildren = style && (element.childNeedsStyleRecalc() || descendantsToResolve != DescendantsToResolve::None);
517
518         if (!m_didSeePendingStylesheet)
519             m_didSeePendingStylesheet = hasLoadingStylesheet(m_document.styleScope(), element, !shouldIterateChildren);
520
521         if (!shouldIterateChildren) {
522             it.traverseNextSkippingChildren();
523             continue;
524         }
525
526         pushParent(element, *style, change, descendantsToResolve);
527
528         it.traverseNext();
529     }
530
531     popParentsToDepth(1);
532 }
533
534 std::unique_ptr<Update> TreeResolver::resolve()
535 {
536     auto& renderView = *m_document.renderView();
537
538     Element* documentElement = m_document.documentElement();
539     if (!documentElement) {
540         m_document.styleScope().resolver();
541         return nullptr;
542     }
543     if (!documentElement->childNeedsStyleRecalc() && !documentElement->needsStyleRecalc())
544         return nullptr;
545
546     m_didSeePendingStylesheet = m_document.styleScope().hasPendingSheetsBeforeBody();
547
548     m_update = std::make_unique<Update>(m_document);
549     m_scopeStack.append(adoptRef(*new Scope(m_document)));
550     m_parentStack.append(Parent(m_document));
551
552     // Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
553     renderView.setUsesFirstLineRules(renderView.usesFirstLineRules() || scope().styleResolver.usesFirstLineRules());
554     renderView.setUsesFirstLetterRules(renderView.usesFirstLetterRules() || scope().styleResolver.usesFirstLetterRules());
555
556     resolveComposedTree();
557
558     renderView.setUsesFirstLineRules(scope().styleResolver.usesFirstLineRules());
559     renderView.setUsesFirstLetterRules(scope().styleResolver.usesFirstLetterRules());
560
561     ASSERT(m_scopeStack.size() == 1);
562     ASSERT(m_parentStack.size() == 1);
563     m_parentStack.clear();
564     popScope();
565
566     if (m_update->roots().isEmpty())
567         return { };
568
569     return WTFMove(m_update);
570 }
571
572 static Vector<Function<void ()>>& postResolutionCallbackQueue()
573 {
574     static NeverDestroyed<Vector<Function<void ()>>> vector;
575     return vector;
576 }
577
578 static Vector<RefPtr<Frame>>& memoryCacheClientCallsResumeQueue()
579 {
580     static NeverDestroyed<Vector<RefPtr<Frame>>> vector;
581     return vector;
582 }
583
584 void queuePostResolutionCallback(Function<void ()>&& callback)
585 {
586     postResolutionCallbackQueue().append(WTFMove(callback));
587 }
588
589 static void suspendMemoryCacheClientCalls(Document& document)
590 {
591     Page* page = document.page();
592     if (!page || !page->areMemoryCacheClientCallsEnabled())
593         return;
594
595     page->setMemoryCacheClientCallsEnabled(false);
596
597     memoryCacheClientCallsResumeQueue().append(&page->mainFrame());
598 }
599
600 static unsigned resolutionNestingDepth;
601
602 PostResolutionCallbackDisabler::PostResolutionCallbackDisabler(Document& document, DrainCallbacks drainCallbacks)
603     : m_drainCallbacks(drainCallbacks)
604 {
605     ++resolutionNestingDepth;
606
607     if (resolutionNestingDepth == 1)
608         platformStrategies()->loaderStrategy()->suspendPendingRequests();
609
610     // FIXME: It's strange to build this into the disabler.
611     suspendMemoryCacheClientCalls(document);
612 }
613
614 PostResolutionCallbackDisabler::~PostResolutionCallbackDisabler()
615 {
616     if (resolutionNestingDepth == 1) {
617         if (m_drainCallbacks == DrainCallbacks::Yes) {
618             // Get size each time through the loop because a callback can add more callbacks to the end of the queue.
619             auto& queue = postResolutionCallbackQueue();
620             for (size_t i = 0; i < queue.size(); ++i)
621                 queue[i]();
622             queue.clear();
623         }
624
625         auto& queue = memoryCacheClientCallsResumeQueue();
626         for (size_t i = 0; i < queue.size(); ++i) {
627             if (auto* page = queue[i]->page())
628                 page->setMemoryCacheClientCallsEnabled(true);
629         }
630         queue.clear();
631
632         platformStrategies()->loaderStrategy()->resumePendingRequests();
633     }
634
635     --resolutionNestingDepth;
636 }
637
638 bool postResolutionCallbacksAreSuspended()
639 {
640     return resolutionNestingDepth;
641 }
642
643 }
644 }