1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * 3. Neither the name of Google Inc. nor the names of its contributors 15 * may be used to endorse or promote products derived from this 16 * software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "core/dom/custom/CustomElementScheduler.h" 33 34 #include "core/dom/Document.h" 35 #include "core/dom/Element.h" 36 #include "core/dom/custom/CustomElementCallbackDispatcher.h" 37 #include "core/dom/custom/CustomElementCallbackInvocation.h" 38 #include "core/dom/custom/CustomElementLifecycleCallbacks.h" 39 #include "core/dom/custom/CustomElementMicrotaskDispatcher.h" 40 #include "core/dom/custom/CustomElementMicrotaskImportStep.h" 41 #include "core/dom/custom/CustomElementMicrotaskResolutionStep.h" 42 #include "core/dom/custom/CustomElementRegistrationContext.h" 43 #include "core/html/imports/HTMLImportChild.h" 44 45 namespace WebCore { 46 47 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CustomElementScheduler) 48 49 void CustomElementScheduler::scheduleCallback(PassRefPtr<CustomElementLifecycleCallbacks> callbacks, PassRefPtrWillBeRawPtr<Element> element, CustomElementLifecycleCallbacks::CallbackType type) 50 { 51 ASSERT(type != CustomElementLifecycleCallbacks::AttributeChanged); 52 53 if (!callbacks->hasCallback(type)) 54 return; 55 56 CustomElementCallbackQueue& queue = instance().schedule(element); 57 queue.append(CustomElementCallbackInvocation::createInvocation(callbacks, type)); 58 } 59 60 void CustomElementScheduler::scheduleAttributeChangedCallback(PassRefPtr<CustomElementLifecycleCallbacks> callbacks, PassRefPtrWillBeRawPtr<Element> element, const AtomicString& name, const AtomicString& oldValue, const AtomicString& newValue) 61 { 62 if (!callbacks->hasCallback(CustomElementLifecycleCallbacks::AttributeChanged)) 63 return; 64 65 CustomElementCallbackQueue& queue = instance().schedule(element); 66 queue.append(CustomElementCallbackInvocation::createAttributeChangedInvocation(callbacks, name, oldValue, newValue)); 67 } 68 69 void CustomElementScheduler::resolveOrScheduleResolution(PassRefPtrWillBeRawPtr<CustomElementRegistrationContext> context, PassRefPtrWillBeRawPtr<Element> element, const CustomElementDescriptor& descriptor) 70 { 71 if (CustomElementCallbackDispatcher::inCallbackDeliveryScope()) { 72 context->resolve(element.get(), descriptor); 73 return; 74 } 75 76 HTMLImportLoader* loader = element->document().importLoader(); 77 OwnPtrWillBeRawPtr<CustomElementMicrotaskResolutionStep> step = CustomElementMicrotaskResolutionStep::create(context, element, descriptor); 78 CustomElementMicrotaskDispatcher::instance().enqueue(loader, step.release()); 79 } 80 81 CustomElementMicrotaskImportStep* CustomElementScheduler::scheduleImport(HTMLImportChild* import) 82 { 83 ASSERT(!import->isDone()); 84 ASSERT(import->parent()); 85 86 // Ownership of the new step is transferred to the parent 87 // processing step, or the base queue. 88 OwnPtrWillBeRawPtr<CustomElementMicrotaskImportStep> step = CustomElementMicrotaskImportStep::create(import); 89 CustomElementMicrotaskImportStep* rawStep = step.get(); 90 CustomElementMicrotaskDispatcher::instance().enqueue(import->parent()->loader(), step.release(), import->isSync()); 91 return rawStep; 92 } 93 94 CustomElementScheduler& CustomElementScheduler::instance() 95 { 96 DEFINE_STATIC_LOCAL(OwnPtrWillBePersistent<CustomElementScheduler>, instance, (adoptPtrWillBeNoop (new CustomElementScheduler()))); 97 return *instance; 98 } 99 100 CustomElementCallbackQueue& CustomElementScheduler::ensureCallbackQueue(PassRefPtrWillBeRawPtr<Element> element) 101 { 102 ElementCallbackQueueMap::ValueType* it = m_elementCallbackQueueMap.add(element.get(), nullptr).storedValue; 103 if (!it->value) 104 it->value = CustomElementCallbackQueue::create(element); 105 return *it->value.get(); 106 } 107 108 void CustomElementScheduler::callbackDispatcherDidFinish() 109 { 110 if (CustomElementMicrotaskDispatcher::instance().elementQueueIsEmpty()) 111 instance().clearElementCallbackQueueMap(); 112 } 113 114 void CustomElementScheduler::microtaskDispatcherDidFinish() 115 { 116 ASSERT(!CustomElementCallbackDispatcher::inCallbackDeliveryScope()); 117 instance().clearElementCallbackQueueMap(); 118 } 119 120 void CustomElementScheduler::clearElementCallbackQueueMap() 121 { 122 ElementCallbackQueueMap emptyMap; 123 m_elementCallbackQueueMap.swap(emptyMap); 124 } 125 126 // Finds or creates the callback queue for element. 127 CustomElementCallbackQueue& CustomElementScheduler::schedule(PassRefPtrWillBeRawPtr<Element> passElement) 128 { 129 RefPtrWillBeRawPtr<Element> element(passElement); 130 131 CustomElementCallbackQueue& callbackQueue = ensureCallbackQueue(element); 132 if (callbackQueue.inCreatedCallback()) { 133 // Don't move it. Authors use the createdCallback like a 134 // constructor. By not moving it, the createdCallback 135 // completes before any other callbacks are entered for this 136 // element. 137 return callbackQueue; 138 } 139 140 if (CustomElementCallbackDispatcher::inCallbackDeliveryScope()) { 141 // The processing stack is active. 142 CustomElementCallbackDispatcher::instance().enqueue(&callbackQueue); 143 return callbackQueue; 144 } 145 146 CustomElementMicrotaskDispatcher::instance().enqueue(&callbackQueue); 147 return callbackQueue; 148 } 149 150 void CustomElementScheduler::trace(Visitor* visitor) 151 { 152 visitor->trace(m_elementCallbackQueueMap); 153 } 154 155 } // namespace WebCore 156