diff options
Diffstat (limited to 'Source/WebCore/dom')
53 files changed, 1468 insertions, 706 deletions
diff --git a/Source/WebCore/dom/CheckedRadioButtons.cpp b/Source/WebCore/dom/CheckedRadioButtons.cpp index 3cf8848..20662a2 100644 --- a/Source/WebCore/dom/CheckedRadioButtons.cpp +++ b/Source/WebCore/dom/CheckedRadioButtons.cpp @@ -77,7 +77,7 @@ void CheckedRadioButtons::removeButton(HTMLFormControlElement* element) if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element) return; - InputElement* inputElement = toInputElement(element); + InputElement* inputElement = element->toInputElement(); ASSERT_UNUSED(inputElement, inputElement); ASSERT(inputElement->isChecked()); ASSERT(element->isRadioButton()); diff --git a/Source/WebCore/dom/Clipboard.h b/Source/WebCore/dom/Clipboard.h index b8eadfb..40141af 100644 --- a/Source/WebCore/dom/Clipboard.h +++ b/Source/WebCore/dom/Clipboard.h @@ -33,6 +33,7 @@ namespace WebCore { + class DataTransferItems; class DragData; class FileList; class Frame; @@ -84,6 +85,7 @@ namespace WebCore { virtual bool hasData() = 0; void setAccessPolicy(ClipboardAccessPolicy); + ClipboardAccessPolicy policy() const { return m_policy; } DragOperation sourceOperation() const; DragOperation destinationOperation() const; @@ -91,11 +93,14 @@ namespace WebCore { void setDestinationOperation(DragOperation); void setDragHasStarted() { m_dragStarted = true; } + +#if ENABLE(DATA_TRANSFER_ITEMS) + virtual PassRefPtr<DataTransferItems> items() = 0; +#endif protected: Clipboard(ClipboardAccessPolicy, ClipboardType); - ClipboardAccessPolicy policy() const { return m_policy; } bool dragStarted() const { return m_dragStarted; } private: diff --git a/Source/WebCore/dom/Clipboard.idl b/Source/WebCore/dom/Clipboard.idl index 1024a36..d5bb331 100644 --- a/Source/WebCore/dom/Clipboard.idl +++ b/Source/WebCore/dom/Clipboard.idl @@ -42,6 +42,8 @@ module core { [RequiresAllArguments] boolean setData(in DOMString type, in DOMString data); [Custom] void setDragImage(in HTMLImageElement image, in long x, in long y) raises(DOMException); + + readonly attribute [Conditional=DATA_TRANSFER_ITEMS, EnabledAtRuntime=DataTransferItems] DataTransferItems items; }; } diff --git a/Source/WebCore/dom/ContainerNode.cpp b/Source/WebCore/dom/ContainerNode.cpp index 7424875..4014cca 100644 --- a/Source/WebCore/dom/ContainerNode.cpp +++ b/Source/WebCore/dom/ContainerNode.cpp @@ -735,7 +735,7 @@ void ContainerNode::insertedIntoDocument() { Node::insertedIntoDocument(); insertedIntoTree(false); - for (Node* child = m_firstChild; child; child = child->nextSibling()) + for (Node* child = m_firstChild; child && inDocument(); child = child->nextSibling()) child->insertedIntoDocument(); } diff --git a/Source/WebCore/dom/DOMAllInOne.cpp b/Source/WebCore/dom/DOMAllInOne.cpp index c88aecf..cc4888f 100644 --- a/Source/WebCore/dom/DOMAllInOne.cpp +++ b/Source/WebCore/dom/DOMAllInOne.cpp @@ -26,7 +26,6 @@ // This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build. #include "ActiveDOMObject.cpp" -#include "AsyncScriptRunner.cpp" #include "Attr.cpp" #include "Attribute.cpp" #include "BeforeProcessEvent.cpp" @@ -70,6 +69,7 @@ #include "ErrorEvent.cpp" #include "Event.cpp" #include "EventContext.cpp" +#include "EventDispatcher.cpp" #include "EventNames.cpp" #include "EventQueue.cpp" #include "EventTarget.cpp" @@ -104,6 +104,7 @@ #include "ScopedEventQueue.cpp" #include "ScriptElement.cpp" #include "ScriptExecutionContext.cpp" +#include "ScriptRunner.cpp" #include "ScriptableDocumentParser.cpp" #include "SelectElement.cpp" #include "SelectorNodeList.cpp" diff --git a/Source/WebCore/dom/DOMImplementation.cpp b/Source/WebCore/dom/DOMImplementation.cpp index 3ba1137..782f203 100644 --- a/Source/WebCore/dom/DOMImplementation.cpp +++ b/Source/WebCore/dom/DOMImplementation.cpp @@ -262,7 +262,8 @@ PassRefPtr<Document> DOMImplementation::createDocument(const String& namespaceUR // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was // created from a different implementation. // Hixie's interpretation of the DOM Core spec suggests we should prefer - // other exceptions to WRONG_DOCUMENT_ERR (based on order mentioned in spec). + // other exceptions to WRONG_DOCUMENT_ERR (based on order mentioned in spec), + // but this matches the new DOM Core spec (http://www.w3.org/TR/domcore/). if (doctype && doctype->document()) { ec = WRONG_DOCUMENT_ERR; return 0; diff --git a/Source/WebCore/dom/DataTransferItem.cpp b/Source/WebCore/dom/DataTransferItem.cpp new file mode 100644 index 0000000..8eec869 --- /dev/null +++ b/Source/WebCore/dom/DataTransferItem.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DataTransferItem.h" + +#if ENABLE(DATA_TRANSFER_ITEMS) + +namespace WebCore { + +const char DataTransferItem::kindString[] = "string"; +const char DataTransferItem::kindFile[] = "file"; + +} // namespace WebCore + +#endif // ENABLE(DATA_TRANSFER_ITEMS) diff --git a/Source/WebCore/dom/DataTransferItem.h b/Source/WebCore/dom/DataTransferItem.h new file mode 100644 index 0000000..7b5886a --- /dev/null +++ b/Source/WebCore/dom/DataTransferItem.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DataTransferItem_h +#define DataTransferItem_h + +#if ENABLE(DATA_TRANSFER_ITEMS) + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class StringCallback; + +class DataTransferItem : public RefCounted<DataTransferItem> { +public: + ~DataTransferItem() {} + + static const char kindString[]; + static const char kindFile[]; + + virtual String kind() const = 0; + virtual String type() const = 0; + + virtual void getAsString(PassRefPtr<StringCallback>) = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(DATA_TRANSFER_ITEMS) + +#endif // DataTransferItem_h diff --git a/Source/WebCore/dom/DataTransferItem.idl b/Source/WebCore/dom/DataTransferItem.idl new file mode 100644 index 0000000..a2c9942 --- /dev/null +++ b/Source/WebCore/dom/DataTransferItem.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module core { + + interface [ + Conditional=DATA_TRANSFER_ITEMS + ] DataTransferItem { + readonly attribute DOMString kind; + readonly attribute DOMString type; + + void getAsString(in [Callback] StringCallback callback); + }; + +} diff --git a/Source/WebCore/dom/DataTransferItems.h b/Source/WebCore/dom/DataTransferItems.h new file mode 100644 index 0000000..0873384 --- /dev/null +++ b/Source/WebCore/dom/DataTransferItems.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DataTransferItems_h +#define DataTransferItems_h + +#if ENABLE(DATA_TRANSFER_ITEMS) + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class DataTransferItem; + +typedef int ExceptionCode; + +class DataTransferItems : public RefCounted<DataTransferItems> { +public: + ~DataTransferItems() {} + + virtual unsigned long length() const = 0; + virtual PassRefPtr<DataTransferItem> item(unsigned long index) const = 0; + virtual void deleteItem(unsigned long index, ExceptionCode&) = 0; + virtual void clear() = 0; + + virtual void add(const String& data, const String& type, ExceptionCode&) = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(DATA_TRANSFER_ITEMS) + +#endif // DataTransferItems_h + diff --git a/Source/WebCore/dom/DataTransferItems.idl b/Source/WebCore/dom/DataTransferItems.idl new file mode 100644 index 0000000..1cb36b8 --- /dev/null +++ b/Source/WebCore/dom/DataTransferItems.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module core { + + interface [ + Conditional=DATA_TRANSFER_ITEMS, + HasIndexGetter, + CustomDeleteProperty, + ] DataTransferItems { + readonly attribute long length; + DataTransferItem item(in unsigned long index) getter; + + void clear(); + void add(in DOMString data, in DOMString type) raises(DOMException); + }; + +} diff --git a/Source/WebCore/dom/DatasetDOMStringMap.cpp b/Source/WebCore/dom/DatasetDOMStringMap.cpp index 6359d55..a143743 100644 --- a/Source/WebCore/dom/DatasetDOMStringMap.cpp +++ b/Source/WebCore/dom/DatasetDOMStringMap.cpp @@ -72,13 +72,30 @@ static String convertAttributeNameToPropertyName(const String& name) static bool propertyNameMatchesAttributeName(const String& propertyName, const String& attributeName) { - // FIXME: This should be able to match without creating a new string. - - if (!isValidAttributeName(attributeName)) + if (!attributeName.startsWith("data-")) return false; - String convertedName = convertAttributeNameToPropertyName(attributeName); - return (convertedName == propertyName); + const UChar* property = propertyName.characters(); + const UChar* attribute = attributeName.characters(); + unsigned propertyLength = propertyName.length(); + unsigned attributeLength = attributeName.length(); + + unsigned a = 5; + unsigned p = 0; + bool wordBoundary = false; + while (a < attributeLength && p < propertyLength) { + if (attribute[a] == '-' && a + 1 < attributeLength && attribute[a + 1] != '-') + wordBoundary = true; + else { + if ((wordBoundary ? toASCIIUpper(attribute[a]) : attribute[a]) != property[p]) + return false; + p++; + wordBoundary = false; + } + a++; + } + + return (a == attributeLength && p == propertyLength); } static bool isValidPropertyName(const String& name) diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp index a2a8040..233b798 100644 --- a/Source/WebCore/dom/Document.cpp +++ b/Source/WebCore/dom/Document.cpp @@ -7,7 +7,6 @@ * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008, 2009 Google Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) - * Copyright (C) Research In Motion, Limited 2010-2011. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -30,7 +29,6 @@ #include "AXObjectCache.h" #include "AnimationController.h" -#include "AsyncScriptRunner.h" #include "Attr.h" #include "Attribute.h" #include "CDATASection.h" @@ -44,6 +42,7 @@ #include "ChromeClient.h" #include "Comment.h" #include "Console.h" +#include "ContentSecurityPolicy.h" #include "CookieJar.h" #include "CustomEvent.h" #include "DateComponents.h" @@ -120,6 +119,7 @@ #include "RegisteredEventListener.h" #include "RenderArena.h" #include "RenderLayer.h" +#include "RenderLayerBacking.h" #include "RenderTextControl.h" #include "RenderView.h" #include "RenderWidget.h" @@ -128,6 +128,7 @@ #include "ScriptController.h" #include "ScriptElement.h" #include "ScriptEventListener.h" +#include "ScriptRunner.h" #include "SecurityOrigin.h" #include "SegmentedString.h" #include "SelectionController.h" @@ -333,7 +334,7 @@ static Widget* widgetForNode(Node* focusedNode) static bool acceptsEditingFocus(Node* node) { ASSERT(node); - ASSERT(node->isContentEditable()); + ASSERT(node->rendererIsEditable()); Node* root = node->rootEditableElement(); Frame* frame = node->document()->frame(); @@ -358,7 +359,7 @@ static bool disableRangeMutation(Page* page) static HashSet<Document*>* documentsThatNeedStyleRecalc = 0; -class DocumentWeakReference : public ThreadSafeShared<DocumentWeakReference> { +class DocumentWeakReference : public ThreadSafeRefCounted<DocumentWeakReference> { public: static PassRefPtr<DocumentWeakReference> create(Document* document) { @@ -387,14 +388,20 @@ private: Document* m_document; }; +uint64_t Document::s_globalTreeVersion = 0; + Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) : ContainerNode(0) , m_compatibilityMode(NoQuirksMode) , m_compatibilityModeLocked(false) +<<<<<<< HEAD , m_domTreeVersion(0) #ifdef ANDROID_STYLE_VERSION , m_styleVersion(0) #endif +======= + , m_domTreeVersion(++s_globalTreeVersion) +>>>>>>> webkit.org at r82507 , m_styleSheets(StyleSheetList::create(this)) , m_readyState(Complete) , m_styleRecalcTimer(this, &Document::styleRecalcTimerFired) @@ -410,7 +417,7 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) , m_startTime(currentTime()) , m_overMinimumLayoutThreshold(false) , m_extraLayoutDelay(0) - , m_asyncScriptRunner(AsyncScriptRunner::create(this)) + , m_scriptRunner(ScriptRunner::create(this)) , m_xmlVersion("1.0") , m_xmlStandalone(false) , m_savedRenderer(0) @@ -454,7 +461,6 @@ Document::Document(Frame* frame, const KURL& url, bool isXHTML, bool isHTML) , m_writingModeSetOnDocumentElement(false) , m_writeRecursionIsTooDeep(false) , m_writeRecursionDepth(0) - , m_pendingTasksTimer(this, &Document::pendingTasksTimerFired) { m_document = this; @@ -563,6 +569,11 @@ void Document::removedLastRef() m_cssCanvasElements.clear(); +#if ENABLE(REQUEST_ANIMATION_FRAME) + // FIXME: consider using ActiveDOMObject. + m_scriptedAnimationController = 0; +#endif + #ifndef NDEBUG m_inRemovedLastRefFunction = false; #endif @@ -584,7 +595,7 @@ Document::~Document() ASSERT(m_ranges.isEmpty()); ASSERT(!m_styleRecalcTimer.isActive()); - m_asyncScriptRunner.clear(); + m_scriptRunner.clear(); removeAllEventListeners(); @@ -1345,43 +1356,51 @@ static inline String canonicalizedTitle(Document* document, const String& title) return String::adopt(buffer); } -void Document::updateTitle() +void Document::updateTitle(const String& title) { + if (m_rawTitle == title) + return; + + m_rawTitle = title; m_title = canonicalizedTitle(this, m_rawTitle); if (Frame* f = frame()) f->loader()->setTitle(m_title); } -void Document::setTitle(const String& title, Element* titleElement) +void Document::setTitle(const String& title) { - if (!titleElement) { - // Title set by JavaScript -- overrides any title elements. - m_titleSetExplicitly = true; - if (!isHTMLDocument()) - m_titleElement = 0; - else if (!m_titleElement) { - if (HTMLElement* headElement = head()) { - m_titleElement = createElement(titleTag, false); - ExceptionCode ec = 0; - headElement->appendChild(m_titleElement, ec); - ASSERT(!ec); - } + // Title set by JavaScript -- overrides any title elements. + m_titleSetExplicitly = true; + if (!isHTMLDocument()) + m_titleElement = 0; + else if (!m_titleElement) { + if (HTMLElement* headElement = head()) { + m_titleElement = createElement(titleTag, false); + ExceptionCode ec = 0; + headElement->appendChild(m_titleElement, ec); + ASSERT(!ec); } - } else if (titleElement != m_titleElement) { + } + + updateTitle(title); + + if (m_titleElement) { + ASSERT(m_titleElement->hasTagName(titleTag)); + if (m_titleElement->hasTagName(titleTag)) + static_cast<HTMLTitleElement*>(m_titleElement.get())->setText(m_title); + } +} + +void Document::setTitleElement(const String& title, Element* titleElement) +{ + if (titleElement != m_titleElement) { if (m_titleElement || m_titleSetExplicitly) // Only allow the first title element to change the title -- others have no effect. return; m_titleElement = titleElement; } - if (m_rawTitle == title) - return; - - m_rawTitle = title; - updateTitle(); - - if (m_titleSetExplicitly && m_titleElement && m_titleElement->hasTagName(titleTag) && !titleElement) - static_cast<HTMLTitleElement*>(m_titleElement.get())->setText(m_title); + updateTitle(title); } void Document::removeTitle(Element* titleElement) @@ -1397,15 +1416,13 @@ void Document::removeTitle(Element* titleElement) for (Node* e = headElement->firstChild(); e; e = e->nextSibling()) if (e->hasTagName(titleTag)) { HTMLTitleElement* titleElement = static_cast<HTMLTitleElement*>(e); - setTitle(titleElement->text(), titleElement); + setTitleElement(titleElement->text(), titleElement); break; } } - if (!m_titleElement && !m_rawTitle.isEmpty()) { - m_rawTitle = ""; - updateTitle(); - } + if (!m_titleElement) + updateTitle(""); } String Document::nodeName() const @@ -1793,7 +1810,12 @@ void Document::detach() clearAXObjectCache(); stopActiveDOMObjects(); - + +#if ENABLE(REQUEST_ANIMATION_FRAME) + // FIXME: consider using ActiveDOMObject. + m_scriptedAnimationController = 0; +#endif + RenderObject* render = renderer(); // Send out documentWillBecomeInactive() notifications to registered elements, @@ -3994,7 +4016,7 @@ void Document::setInPageCache(bool flag) ASSERT(!m_savedRenderer); m_savedRenderer = renderer(); if (FrameView* v = view()) - v->resetScrollbars(); + v->resetScrollbarsAndClearContentsSize(); m_styleRecalcTimer.stop(); } else { ASSERT(!renderer() || renderer() == m_savedRenderer); @@ -4486,7 +4508,7 @@ void FormElementKey::deref() const unsigned FormElementKeyHash::hash(const FormElementKey& key) { - return WTF::StringHasher::createBlobHash<sizeof(FormElementKey)>(&key); + return StringHasher::hashMemory<sizeof(FormElementKey)>(&key); } void Document::setIconURL(const String& iconURL, const String& type) @@ -4551,7 +4573,7 @@ void Document::initSecurityContext() // loading URL with a fresh content security policy. m_cookieURL = m_url; ScriptExecutionContext::setSecurityOrigin(SecurityOrigin::create(m_url, m_frame->loader()->sandboxFlags())); - m_contentSecurityPolicy = ContentSecurityPolicy::create(); + m_contentSecurityPolicy = ContentSecurityPolicy::create(securityOrigin()); if (SecurityOrigin::allowSubstituteDataAccessToLocal()) { // If this document was loaded with substituteData, then the document can @@ -4780,59 +4802,22 @@ public: OwnPtr<ScriptExecutionContext::Task> task; }; -void Document::didReceiveTask(void* untypedContext) +static void performTask(void* ctx) { ASSERT(isMainThread()); - OwnPtr<PerformTaskContext> context = adoptPtr(static_cast<PerformTaskContext*>(untypedContext)); + PerformTaskContext* context = reinterpret_cast<PerformTaskContext*>(ctx); ASSERT(context); - Document* document = context->documentReference->document(); - if (!document) - return; - - Page* page = document->page(); - if ((page && page->defersLoading()) || !document->m_pendingTasks.isEmpty()) { - document->m_pendingTasks.append(context->task.release()); - return; - } + if (Document* document = context->documentReference->document()) + context->task->performTask(document); - context->task->performTask(document); + delete context; } void Document::postTask(PassOwnPtr<Task> task) { - callOnMainThread(didReceiveTask, new PerformTaskContext(m_weakReference, task)); -} - -void Document::pendingTasksTimerFired(Timer<Document>*) -{ - while (!m_pendingTasks.isEmpty()) { - OwnPtr<Task> task = m_pendingTasks[0].release(); - m_pendingTasks.remove(0); - task->performTask(this); - } -} - -void Document::suspendScheduledTasks() -{ - suspendScriptedAnimationControllerCallbacks(); - suspendActiveDOMObjects(ActiveDOMObject::WillShowDialog); - asyncScriptRunner()->suspend(); - m_pendingTasksTimer.stop(); - if (m_parser) - m_parser->suspendScheduledTasks(); -} - -void Document::resumeScheduledTasks() -{ - if (m_parser) - m_parser->resumeScheduledTasks(); - if (!m_pendingTasks.isEmpty()) - m_pendingTasksTimer.startOneShot(0); - asyncScriptRunner()->resume(); - resumeActiveDOMObjects(); - resumeScriptedAnimationControllerCallbacks(); + callOnMainThread(performTask, new PerformTaskContext(m_weakReference, task)); } void Document::suspendScriptedAnimationControllerCallbacks() @@ -4947,6 +4932,20 @@ bool Document::isXHTMLMPDocument() const #endif #if ENABLE(FULLSCREEN_API) +bool Document::fullScreenIsAllowedForElement(Element* element) const +{ + ASSERT(element); + while (HTMLFrameOwnerElement* ownerElement = element->document()->ownerElement()) { + if (!ownerElement->hasTagName(frameTag) && !ownerElement->hasTagName(iframeTag)) + continue; + + if (!static_cast<HTMLFrameElementBase*>(ownerElement)->allowFullScreen()) + return false; + element = ownerElement; + } + return true; +} + void Document::webkitRequestFullScreenForElement(Element* element, unsigned short flags) { if (!page() || !page()->settings()->fullScreenEnabled()) @@ -4955,7 +4954,13 @@ void Document::webkitRequestFullScreenForElement(Element* element, unsigned shor if (!element) element = documentElement(); - if (!page()->chrome()->client()->supportsFullScreenForElement(element)) + if (!fullScreenIsAllowedForElement(element)) + return; + + if (!ScriptController::processingUserGesture()) + return; + + if (!page()->chrome()->client()->supportsFullScreenForElement(element, flags & Element::ALLOW_KEYBOARD_INPUT)) return; m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT; @@ -4983,23 +4988,40 @@ void Document::webkitWillEnterFullScreenForElement(Element* element) recalcStyle(Force); - if (m_fullScreenRenderer) + if (m_fullScreenRenderer) { m_fullScreenRenderer->setAnimating(true); +#if USE(ACCELERATED_COMPOSITING) + view()->updateCompositingLayers(); + ASSERT(m_fullScreenRenderer->layer()->backing()); + page()->chrome()->client()->setRootFullScreenLayer(m_fullScreenRenderer->layer()->backing()->graphicsLayer()); +#endif + } } void Document::webkitDidEnterFullScreenForElement(Element*) { - if (m_fullScreenRenderer) + if (m_fullScreenRenderer) { m_fullScreenRenderer->setAnimating(false); +#if USE(ACCELERATED_COMPOSITING) + view()->updateCompositingLayers(); + ASSERT(!m_fullScreenRenderer->layer()->backing()); + page()->chrome()->client()->setRootFullScreenLayer(0); +#endif + } m_fullScreenChangeDelayTimer.startOneShot(0); } void Document::webkitWillExitFullScreenForElement(Element*) { - if (m_fullScreenRenderer) + if (m_fullScreenRenderer) { m_fullScreenRenderer->setAnimating(true); - - recalcStyle(Force); + m_fullScreenRenderer->setAnimating(true); +#if USE(ACCELERATED_COMPOSITING) + view()->updateCompositingLayers(); + ASSERT(m_fullScreenRenderer->layer()->backing()); + page()->chrome()->client()->setRootFullScreenLayer(m_fullScreenRenderer->layer()->backing()->graphicsLayer()); +#endif + } } void Document::webkitDidExitFullScreenForElement(Element*) @@ -5014,6 +5036,9 @@ void Document::webkitDidExitFullScreenForElement(Element*) m_fullScreenElement->detach(); setFullScreenRenderer(0); +#if USE(ACCELERATED_COMPOSITING) + page()->chrome()->client()->setRootFullScreenLayer(0); +#endif recalcStyle(Force); m_fullScreenChangeDelayTimer.startOneShot(0); @@ -5021,6 +5046,11 @@ void Document::webkitDidExitFullScreenForElement(Element*) void Document::setFullScreenRenderer(RenderFullScreen* renderer) { + if (renderer == m_fullScreenRenderer) + return; + + if (m_fullScreenRenderer) + m_fullScreenRenderer->destroy(); m_fullScreenRenderer = renderer; // This notification can come in after the page has been destroyed. @@ -5041,6 +5071,7 @@ void Document::setFullScreenRendererSize(const IntSize& size) newStyle->setTop(Length(0, WebCore::Fixed)); newStyle->setLeft(Length(0, WebCore::Fixed)); m_fullScreenRenderer->setStyle(newStyle); + updateLayout(); } } diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h index ae9b24e..c9a1b55 100644 --- a/Source/WebCore/dom/Document.h +++ b/Source/WebCore/dom/Document.h @@ -32,7 +32,6 @@ #include "CollectionType.h" #include "Color.h" #include "ContainerNode.h" -#include "ContentSecurityPolicy.h" #include "DOMTimeStamp.h" #include "DocumentOrderedMap.h" #include "DocumentTiming.h" @@ -51,20 +50,20 @@ namespace WebCore { -class AsyncScriptRunner; -class Attr; class AXObjectCache; +class Attr; class CDATASection; +class CSSPrimitiveValueCache; +class CSSStyleDeclaration; +class CSSStyleSelector; +class CSSStyleSheet; class CachedCSSStyleSheet; class CachedResourceLoader; class CachedScript; class CanvasRenderingContext; class CharacterData; -class CSSPrimitiveValueCache; -class CSSStyleDeclaration; -class CSSStyleSelector; -class CSSStyleSheet; class Comment; +class ContentSecurityPolicy; class DOMImplementation; class DOMSelection; class DOMWindow; @@ -115,6 +114,7 @@ class RenderView; class RenderFullScreen; class ScriptableDocumentParser; class ScriptElementData; +class ScriptRunner; class SecurityOrigin; class SerializedScriptValue; class SegmentedString; @@ -820,7 +820,8 @@ public: HTMLFrameOwnerElement* ownerElement() const; String title() const { return m_title; } - void setTitle(const String&, Element* titleElement = 0); + void setTitle(const String&); + void setTitleElement(const String& title, Element* titleElement); void removeTitle(Element* titleElement); String cookie(ExceptionCode&) const; @@ -908,7 +909,7 @@ public: int docID() const { return m_docID; } - AsyncScriptRunner* asyncScriptRunner() { return m_asyncScriptRunner.get(); } + ScriptRunner* scriptRunner() { return m_scriptRunner.get(); } #if ENABLE(XSLT) void applyXSLTransform(ProcessingInstruction* pi); @@ -919,8 +920,8 @@ public: TransformSource* transformSource() const { return m_transformSource.get(); } #endif - void incDOMTreeVersion() { ++m_domTreeVersion; } - unsigned domTreeVersion() const { return m_domTreeVersion; } + void incDOMTreeVersion() { m_domTreeVersion = ++s_globalTreeVersion; } + uint64_t domTreeVersion() const { return m_domTreeVersion; } #ifdef ANDROID_STYLE_VERSION void incStyleVersion() { ++m_styleVersion; } @@ -1106,6 +1107,7 @@ public: void setFullScreenRendererBackgroundColor(Color); void fullScreenChangeDelayTimerFired(Timer<Document>*); + bool fullScreenIsAllowedForElement(Element*) const; #endif // Used to allow element that loads data without going through a FrameLoader to delay the 'load' event. @@ -1135,9 +1137,6 @@ public: ContentSecurityPolicy* contentSecurityPolicy() { return m_contentSecurityPolicy.get(); } - void suspendScheduledTasks(); - void resumeScheduledTasks(); - protected: Document(Frame*, const KURL&, bool isXHTML, bool isHTML); @@ -1173,7 +1172,7 @@ private: String encoding() const; - void updateTitle(); + void updateTitle(const String& title); void updateFocusAppearanceTimerFired(Timer<Document>*); void updateBaseURL(); @@ -1185,10 +1184,6 @@ private: void loadEventDelayTimerFired(Timer<Document>*); - void pendingTasksTimerFired(Timer<Document>*); - - static void didReceiveTask(void*); - OwnPtr<CSSStyleSelector> m_styleSelector; bool m_didCalculateStyleSelector; bool m_hasDirtyStyleSelector; @@ -1258,10 +1253,15 @@ private: RefPtr<Node> m_activeNode; mutable RefPtr<Element> m_documentElement; +<<<<<<< HEAD unsigned m_domTreeVersion; #ifdef ANDROID_STYLE_VERSION unsigned m_styleVersion; #endif +======= + uint64_t m_domTreeVersion; + static uint64_t s_globalTreeVersion; +>>>>>>> webkit.org at r82507 HashSet<NodeIterator*> m_nodeIterators; HashSet<Range*> m_ranges; @@ -1341,7 +1341,7 @@ private: // points during the lifetime of the Document. int m_extraLayoutDelay; - OwnPtr<AsyncScriptRunner> m_asyncScriptRunner; + OwnPtr<ScriptRunner> m_scriptRunner; #if ENABLE(XSLT) OwnPtr<TransformSource> m_transformSource; @@ -1460,9 +1460,6 @@ private: #endif RefPtr<ContentSecurityPolicy> m_contentSecurityPolicy; - - Timer<Document> m_pendingTasksTimer; - Vector<OwnPtr<Task> > m_pendingTasks; }; inline bool Document::hasElementWithId(AtomicStringImpl* id) const diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp index 41c0fb1..81668cf 100644 --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp @@ -55,7 +55,9 @@ #include "RenderWidget.h" #include "Settings.h" #include "TextIterator.h" +#include "WebKitAnimationList.h" #include "XMLNames.h" +#include "htmlediting.h" #include <wtf/text/CString.h> #if ENABLE(SVG) @@ -1199,6 +1201,15 @@ bool Element::childTypeAllowed(NodeType type) return false; } +static void checkForEmptyStyleChange(Element* element, RenderStyle* style) +{ + if (!style) + return; + + if (style->affectedByEmpty() && (!style->emptyState() || element->hasChildNodes())) + element->setNeedsStyleRecalc(); +} + static void checkForSiblingStyleChanges(Element* e, RenderStyle* style, bool finishedParsingCallback, Node* beforeChange, Node* afterChange, int childCountDelta) { @@ -1275,17 +1286,18 @@ static void checkForSiblingStyleChanges(Element* e, RenderStyle* style, bool fin e->setNeedsStyleRecalc(); // :empty selector. - if (style->affectedByEmpty() && (!style->emptyState() || e->hasChildNodes())) - e->setNeedsStyleRecalc(); + checkForEmptyStyleChange(e, style); } void Element::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) { ContainerNode::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); - if (!changedByParser) + if (changedByParser) + checkForEmptyStyleChange(this, renderStyle()); + else checkForSiblingStyleChanges(this, renderStyle(), false, beforeChange, afterChange, childCountDelta); } - + void Element::beginParsingChildren() { clearIsParsingChildrenFinished(); @@ -1413,10 +1425,8 @@ PassRefPtr<Attr> Element::removeAttributeNode(Attr* attr, ExceptionCode& ec) ec = NOT_FOUND_ERR; return 0; } - if (document() != attr->document()) { - ec = WRONG_DOCUMENT_ERR; - return 0; - } + + ASSERT(document() == attr->document()); NamedNodeMap* attrs = attributes(true); if (!attrs) @@ -1508,6 +1518,9 @@ CSSStyleDeclaration *Element::style() void Element::focus(bool restorePreviousSelection) { + if (!inDocument()) + return; + Document* doc = document(); if (doc->focusedNode() == this) return; @@ -1558,7 +1571,7 @@ void Element::updateFocusAppearance(bool /*restorePreviousSelection*/) return; // FIXME: We should restore the previous selection if there is one. - VisibleSelection newSelection = VisibleSelection(firstPositionInNode(this), DOWNSTREAM); + VisibleSelection newSelection = VisibleSelection(firstPositionInOrBeforeNode(this), DOWNSTREAM); if (frame->selection()->shouldChangeSelection(newSelection)) { frame->selection()->setSelection(newSelection); @@ -1891,4 +1904,17 @@ bool Element::isSpellCheckingEnabled() const return true; } +PassRefPtr<WebKitAnimationList> Element::webkitGetAnimations() const +{ + if (!renderer()) + return 0; + + AnimationController* animController = renderer()->animation(); + + if (!animController) + return 0; + + return animController->animationsForRenderer(renderer()); +} + } // namespace WebCore diff --git a/Source/WebCore/dom/Element.h b/Source/WebCore/dom/Element.h index 5c54ce4..ba0870c 100644 --- a/Source/WebCore/dom/Element.h +++ b/Source/WebCore/dom/Element.h @@ -39,6 +39,7 @@ class DOMStringMap; class DOMTokenList; class ElementRareData; class IntSize; +class WebKitAnimationList; enum SpellcheckAttributeState { SpellcheckAttributeTrue, @@ -344,6 +345,8 @@ public: virtual bool isSpellCheckingEnabled() const; + PassRefPtr<WebKitAnimationList> webkitGetAnimations() const; + protected: Element(const QualifiedName& tagName, Document* document, ConstructionType type) : ContainerNode(document, type) diff --git a/Source/WebCore/dom/Element.idl b/Source/WebCore/dom/Element.idl index 652a75b..fc28642 100644 --- a/Source/WebCore/dom/Element.idl +++ b/Source/WebCore/dom/Element.idl @@ -96,6 +96,8 @@ module core { void scrollByLines(in long lines); void scrollByPages(in long pages); + WebKitAnimationList webkitGetAnimations(); + // HTML 5 NodeList getElementsByClassName(in DOMString name); diff --git a/Source/WebCore/dom/Event.cpp b/Source/WebCore/dom/Event.cpp index 795dace..a1352dc 100644 --- a/Source/WebCore/dom/Event.cpp +++ b/Source/WebCore/dom/Event.cpp @@ -22,6 +22,7 @@ #include "config.h" #include "Event.h" +#include "EventDispatcher.h" #include "EventTarget.h" #include "UserGestureIndicator.h" @@ -270,8 +271,16 @@ void Event::storeResult(const String&) { } +bool Event::dispatch(EventDispatcher* dispatcher) +{ + return dispatcher->dispatchEvent(this); +} + void Event::setTarget(PassRefPtr<EventTarget> target) { + if (m_target == target) + return; + m_target = target; if (m_target) receivedTarget(); diff --git a/Source/WebCore/dom/Event.h b/Source/WebCore/dom/Event.h index ba9576b..d4d7e06 100644 --- a/Source/WebCore/dom/Event.h +++ b/Source/WebCore/dom/Event.h @@ -32,6 +32,7 @@ namespace WebCore { class EventTarget; + class EventDispatcher; class Event : public RefCounted<Event> { public: @@ -165,6 +166,8 @@ namespace WebCore { virtual Clipboard* clipboard() const { return 0; } + virtual bool dispatch(EventDispatcher*); + protected: Event(); Event(const AtomicString& type, bool canBubble, bool cancelable); diff --git a/Source/WebCore/dom/EventDispatcher.cpp b/Source/WebCore/dom/EventDispatcher.cpp new file mode 100644 index 0000000..c8b330d --- /dev/null +++ b/Source/WebCore/dom/EventDispatcher.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "EventDispatcher.h" + +#include "Event.h" +#include "EventContext.h" +#include "EventTarget.h" +#include "FrameView.h" +#include "InspectorInstrumentation.h" +#include "MouseEvent.h" +#include "Node.h" +#include "PlatformWheelEvent.h" +#include "ScopedEventQueue.h" + +#if ENABLE(SVG) +#include "SVGElementInstance.h" +#include "SVGNames.h" +#include "SVGUseElement.h" +#endif + +#include "UIEvent.h" +#include "UIEventWithKeyState.h" +#include "WheelEvent.h" +#include "WindowEventContext.h" + +#include <wtf/RefPtr.h> + +namespace WebCore { + +static HashSet<Node*>* gNodesDispatchingSimulatedClicks = 0; + +bool EventDispatcher::dispatchEvent(Node* node, PassRefPtr<Event> prpEvent) +{ + RefPtr<Event> event = prpEvent; + + EventDispatcher dispatcher(node); + return event->dispatch(&dispatcher); +} + +static EventTarget* findElementInstance(Node* referenceNode) +{ +#if ENABLE(SVG) + // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included + // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects + for (Node* n = referenceNode; n; n = n->parentNode()) { + if (!n->isShadowRoot() || !n->isSVGElement()) + continue; + + Element* shadowTreeParentElement = n->shadowHost(); + ASSERT(shadowTreeParentElement->hasTagName(SVGNames::useTag)); + + if (SVGElementInstance* instance = static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode)) + return instance; + } +#else + // SVG elements with SVG disabled should not be possible. + ASSERT_NOT_REACHED(); +#endif + + return referenceNode; +} + +inline static EventTarget* eventTargetRespectingSVGTargetRules(Node* referenceNode) +{ + ASSERT(referenceNode); + + return referenceNode->isSVGElement() ? findElementInstance(referenceNode) : referenceNode; +} + +void EventDispatcher::dispatchScopedEvent(Node* node, PassRefPtr<Event> event) +{ + // We need to set the target here because it can go away by the time we actually fire the event. + event->setTarget(eventTargetRespectingSVGTargetRules(node)); + + ScopedEventQueue::instance()->enqueueEvent(event); +} + +void EventDispatcher::dispatchSimulatedClick(Node* node, PassRefPtr<Event> underlyingEvent, bool sendMouseEvents, bool showPressedLook) +{ + EventDispatcher dispatcher(node); + + if (!gNodesDispatchingSimulatedClicks) + gNodesDispatchingSimulatedClicks = new HashSet<Node*>; + else if (gNodesDispatchingSimulatedClicks->contains(node)) + return; + + gNodesDispatchingSimulatedClicks->add(node); + + // send mousedown and mouseup before the click, if requested + if (sendMouseEvents) + dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().mousedownEvent, node->document()->defaultView(), underlyingEvent)); + node->setActive(true, showPressedLook); + if (sendMouseEvents) + dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().mouseupEvent, node->document()->defaultView(), underlyingEvent)); + node->setActive(false); + + // always send click + dispatcher.dispatchEvent(SimulatedMouseEvent::create(eventNames().clickEvent, node->document()->defaultView(), underlyingEvent)); + + gNodesDispatchingSimulatedClicks->remove(node); +} + +inline static WheelEvent::Granularity granularity(const PlatformWheelEvent& event) +{ + return event.granularity() == ScrollByPageWheelEvent ? WheelEvent::Page : WheelEvent::Pixel; +} + +void EventDispatcher::dispatchWheelEvent(Node* node, PlatformWheelEvent& event) +{ + ASSERT(!eventDispatchForbidden()); + if (!(event.deltaX() || event.deltaY())) + return; + + EventDispatcher dispatcher(node); + + if (!dispatcher.m_view) + return; + + IntPoint position = dispatcher.m_view->windowToContents(event.pos()); + + int adjustedPageX = position.x(); + int adjustedPageY = position.y(); + if (Frame* frame = node->document()->frame()) { + float pageZoom = frame->pageZoomFactor(); + if (pageZoom != 1.0f) { + adjustedPageX = lroundf(position.x() / pageZoom); + adjustedPageY = lroundf(position.y() / pageZoom); + } + } + + RefPtr<WheelEvent> wheelEvent = WheelEvent::create(event.wheelTicksX(), event.wheelTicksY(), event.deltaX(), event.deltaY(), granularity(event), + node->document()->defaultView(), event.globalX(), event.globalY(), adjustedPageX, adjustedPageY, + event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey()); + + wheelEvent->setAbsoluteLocation(position); + + if (!dispatcher.dispatchEvent(wheelEvent) || wheelEvent->defaultHandled()) + event.accept(); + +} + +// FIXME: Once https://bugs.webkit.org/show_bug.cgi?id=52963 lands, this should +// be greatly improved. See https://bugs.webkit.org/show_bug.cgi?id=54025. +static Node* pullOutOfShadow(Node* node) +{ + Node* outermostShadowBoundary = node; + for (Node* n = node; n; n = n->parentOrHostNode()) { + if (n->isShadowRoot()) + outermostShadowBoundary = n->parentOrHostNode(); + } + return outermostShadowBoundary; +} + +EventDispatcher::EventDispatcher(Node* node) + : m_node(node) +{ + ASSERT(node); + m_view = node->document()->view(); +} + +void EventDispatcher::getEventAncestors(EventTarget* originalTarget, EventDispatchBehavior behavior) +{ + if (!m_node->inDocument()) + return; + + if (ancestorsInitialized()) + return; + + EventTarget* target = originalTarget; + Node* ancestor = m_node.get(); + bool shouldSkipNextAncestor = false; + while (true) { + if (ancestor->isShadowRoot()) { + if (behavior == StayInsideShadowDOM) + return; + ancestor = ancestor->shadowHost(); + if (!shouldSkipNextAncestor) + target = ancestor; + } else + ancestor = ancestor->parentNodeGuaranteedHostFree(); + + if (!ancestor) + return; + +#if ENABLE(SVG) + // Skip SVGShadowTreeRootElement. + shouldSkipNextAncestor = ancestor->isSVGElement() && ancestor->isShadowRoot(); + if (shouldSkipNextAncestor) + continue; +#endif + // FIXME: Unroll the extra loop inside eventTargetRespectingSVGTargetRules into this loop. + m_ancestors.append(EventContext(ancestor, eventTargetRespectingSVGTargetRules(ancestor), target)); + } +} + +bool EventDispatcher::dispatchEvent(PassRefPtr<Event> event) +{ + event->setTarget(eventTargetRespectingSVGTargetRules(m_node.get())); + + ASSERT(!eventDispatchForbidden()); + ASSERT(event->target()); + ASSERT(!event->type().isNull()); // JavaScript code can create an event with an empty name, but not null. + + RefPtr<EventTarget> originalTarget = event->target(); + getEventAncestors(originalTarget.get(), determineDispatchBehavior(event.get())); + + WindowEventContext windowContext(event.get(), m_node.get(), topEventContext()); + + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEvent(m_node->document(), *event, windowContext.window(), m_node.get(), m_ancestors); + + // Give the target node a chance to do some work before DOM event handlers get a crack. + void* data = m_node->preDispatchEventHandler(event.get()); + if (event->propagationStopped()) + goto doneDispatching; + + // Trigger capturing event handlers, starting at the top and working our way down. + event->setEventPhase(Event::CAPTURING_PHASE); + + if (windowContext.handleLocalEvents(event.get()) && event->propagationStopped()) + goto doneDispatching; + + for (size_t i = m_ancestors.size(); i; --i) { + m_ancestors[i - 1].handleLocalEvents(event.get()); + if (event->propagationStopped()) + goto doneDispatching; + } + + event->setEventPhase(Event::AT_TARGET); + event->setTarget(originalTarget.get()); + event->setCurrentTarget(eventTargetRespectingSVGTargetRules(m_node.get())); + m_node->handleLocalEvents(event.get()); + if (event->propagationStopped()) + goto doneDispatching; + + if (event->bubbles() && !event->cancelBubble()) { + // Trigger bubbling event handlers, starting at the bottom and working our way up. + event->setEventPhase(Event::BUBBLING_PHASE); + + size_t size = m_ancestors.size(); + for (size_t i = 0; i < size; ++i) { + m_ancestors[i].handleLocalEvents(event.get()); + if (event->propagationStopped() || event->cancelBubble()) + goto doneDispatching; + } + windowContext.handleLocalEvents(event.get()); + } + +doneDispatching: + event->setTarget(originalTarget.get()); + event->setCurrentTarget(0); + event->setEventPhase(0); + + // Pass the data from the preDispatchEventHandler to the postDispatchEventHandler. + m_node->postDispatchEventHandler(event.get(), data); + + // Call default event handlers. While the DOM does have a concept of preventing + // default handling, the detail of which handlers are called is an internal + // implementation detail and not part of the DOM. + if (!event->defaultPrevented() && !event->defaultHandled()) { + // Non-bubbling events call only one default event handler, the one for the target. + m_node->defaultEventHandler(event.get()); + ASSERT(!event->defaultPrevented()); + if (event->defaultHandled()) + goto doneWithDefault; + // For bubbling events, call default event handlers on the same targets in the + // same order as the bubbling phase. + if (event->bubbles()) { + size_t size = m_ancestors.size(); + for (size_t i = 0; i < size; ++i) { + m_ancestors[i].node()->defaultEventHandler(event.get()); + ASSERT(!event->defaultPrevented()); + if (event->defaultHandled()) + goto doneWithDefault; + } + } + } + +doneWithDefault: + + // Ensure that after event dispatch, the event's target object is the + // outermost shadow DOM boundary. + event->setTarget(windowContext.target()); + event->setCurrentTarget(0); + InspectorInstrumentation::didDispatchEvent(cookie); + + return !event->defaultPrevented(); +} +bool EventDispatcher::dispatchMouseEvent(Node* node, const PlatformMouseEvent& event, const AtomicString& eventType, + int detail, Node* relatedTargetArg) +{ + ASSERT(!eventDispatchForbidden()); + ASSERT(event.eventType() == MouseEventMoved || event.button() != NoButton); + + if (node->disabled()) // Don't even send DOM events for disabled controls.. + return true; + + if (eventType.isEmpty()) + return false; // Shouldn't happen. + + EventDispatcher dispatcher(node); + + // Attempting to dispatch with a non-EventTarget relatedTarget causes the relatedTarget to be silently ignored. + RefPtr<Node> relatedTarget = pullOutOfShadow(relatedTargetArg); + + IntPoint contentsPosition; + if (FrameView* view = node->document()->view()) + contentsPosition = view->windowToContents(event.pos()); + + IntPoint adjustedPagePosition = contentsPosition; + if (Frame* frame = node->document()->frame()) { + float pageZoom = frame->pageZoomFactor(); + if (pageZoom != 1.0f) { + // Adjust our pageX and pageY to account for the page zoom. + adjustedPagePosition.setX(lroundf(contentsPosition.x() / pageZoom)); + adjustedPagePosition.setY(lroundf(contentsPosition.y() / pageZoom)); + } + } + + RefPtr<MouseEvent> mouseEvent = MouseEvent::create(eventType, node->document()->defaultView(), event, adjustedPagePosition, detail, relatedTarget); + mouseEvent->setAbsoluteLocation(contentsPosition); + + bool swallowEvent = false; + + dispatcher.dispatchEvent(mouseEvent); + bool defaultHandled = mouseEvent->defaultHandled(); + bool defaultPrevented = mouseEvent->defaultPrevented(); + if (defaultHandled || defaultPrevented) + swallowEvent = true; + + // Special case: If it's a double click event, we also send the dblclick event. This is not part + // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated + // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same. + if (eventType == eventNames().clickEvent && detail == 2) { + RefPtr<Event> doubleClickEvent = MouseEvent::create(eventNames().dblclickEvent, node->document()->defaultView(), event, adjustedPagePosition, detail, relatedTarget); + if (defaultHandled) + doubleClickEvent->setDefaultHandled(); + dispatcher.dispatchEvent(doubleClickEvent); + if (doubleClickEvent->defaultHandled() || doubleClickEvent->defaultPrevented()) + swallowEvent = true; + } + + return swallowEvent; +} + +const EventContext* EventDispatcher::topEventContext() +{ + return m_ancestors.isEmpty() ? 0 : &m_ancestors.last(); +} + +bool EventDispatcher::ancestorsInitialized() const +{ + return m_ancestors.size(); +} + +EventDispatchBehavior EventDispatcher::determineDispatchBehavior(Event* event) +{ + // Per XBL 2.0 spec, mutation events should never cross shadow DOM boundary: + // http://dev.w3.org/2006/xbl2/#event-flow-and-targeting-across-shadow-s + if (event->isMutationEvent()) + return StayInsideShadowDOM; + + // WebKit never allowed selectstart event to cross the the shadow DOM boundary. + // Changing this breaks existing sites. + // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. + if (event->type() == eventNames().selectstartEvent) + return StayInsideShadowDOM; + + return RetargetEvent; +} + +} + diff --git a/Source/WebCore/dom/EventDispatcher.h b/Source/WebCore/dom/EventDispatcher.h new file mode 100644 index 0000000..d43127f --- /dev/null +++ b/Source/WebCore/dom/EventDispatcher.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef EventDispatcher_h +#define EventDispatcher_h + +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Event; +class EventContext; +class EventTarget; +class FrameView; +class Node; +class PlatformKeyboardEvent; +class PlatformMouseEvent; +class PlatformWheelEvent; + +enum EventDispatchBehavior { + RetargetEvent, + StayInsideShadowDOM +}; + +class EventDispatcher { +public: + static bool dispatchEvent(Node*, PassRefPtr<Event>); + static void dispatchScopedEvent(Node*, PassRefPtr<Event>); + + static bool dispatchMouseEvent(Node*, const PlatformMouseEvent&, const AtomicString& eventType, int clickCount = 0, Node* relatedTarget = 0); + static void dispatchSimulatedClick(Node*, PassRefPtr<Event> underlyingEvent, bool sendMouseEvents, bool showPressedLook); + static void dispatchWheelEvent(Node*, PlatformWheelEvent&); + + bool dispatchEvent(PassRefPtr<Event>); +private: + EventDispatcher(Node*); + + EventDispatchBehavior determineDispatchBehavior(Event*); + void getEventAncestors(EventTarget* originalTarget, EventDispatchBehavior); + const EventContext* topEventContext(); + bool ancestorsInitialized() const; + + bool dispatchMouseEvent(const AtomicString& eventType, int button, int detail, + int pageX, int pageY, int screenX, int screenY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, + bool isSimulated, Node* relatedTargetArg, PassRefPtr<Event> underlyingEvent); + + Vector<EventContext> m_ancestors; + RefPtr<Node> m_node; + RefPtr<EventTarget> m_originalTarget; + RefPtr<FrameView> m_view; +}; + +} + +#endif diff --git a/Source/WebCore/dom/ExceptionCode.h b/Source/WebCore/dom/ExceptionCode.h index dd976c7..881c3a3 100644 --- a/Source/WebCore/dom/ExceptionCode.h +++ b/Source/WebCore/dom/ExceptionCode.h @@ -57,13 +57,13 @@ namespace WebCore { NETWORK_ERR = 19, ABORT_ERR = 20, URL_MISMATCH_ERR = 21, - QUOTA_EXCEEDED_ERR = 22, + QUOTA_EXCEEDED_ERR = 22 // Introduced in File API: // http://www.w3.org/TR/file-upload/#dfn-fileerror #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) - NOT_READABLE_ERR = 24, - ENCODING_ERR = 26, + , NOT_READABLE_ERR = 24 + , ENCODING_ERR = 26 #endif }; diff --git a/Source/WebCore/dom/InputElement.cpp b/Source/WebCore/dom/InputElement.cpp index b60fd44..b467df3 100644 --- a/Source/WebCore/dom/InputElement.cpp +++ b/Source/WebCore/dom/InputElement.cpp @@ -38,22 +38,13 @@ #include "Event.h" #include "EventNames.h" #include "Frame.h" -#include "HTMLInputElement.h" -#include "HTMLNames.h" #include "Page.h" #include "RenderTextControlSingleLine.h" #include "SelectionController.h" #include "TextIterator.h" -#if ENABLE(WML) -#include "WMLInputElement.h" -#include "WMLNames.h" -#endif - namespace WebCore { -using namespace HTMLNames; - // FIXME: According to HTML4, the length attribute's value can be arbitrarily // large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things // get rather sluggish when a text field has a larger number of characters than @@ -287,19 +278,6 @@ const AtomicString& InputElementData::name() const return m_name.isNull() ? emptyAtom : m_name; } -InputElement* toInputElement(Element* element) -{ - if (element->isHTMLElement() && (element->hasTagName(inputTag) || element->hasTagName(isindexTag))) - return static_cast<HTMLInputElement*>(element); - -#if ENABLE(WML) - if (element->isWMLElement() && element->hasTagName(WMLNames::inputTag)) - return static_cast<WMLInputElement*>(element); -#endif - - return 0; -} - #if ENABLE(WCSS) static inline const AtomicString& formatCodes() { diff --git a/Source/WebCore/dom/InputElement.h b/Source/WebCore/dom/InputElement.h index 838adf5..26bf58d 100644 --- a/Source/WebCore/dom/InputElement.h +++ b/Source/WebCore/dom/InputElement.h @@ -160,8 +160,6 @@ private: #endif }; -InputElement* toInputElement(Element*); - } #endif diff --git a/Source/WebCore/dom/KeyboardEvent.cpp b/Source/WebCore/dom/KeyboardEvent.cpp index 7b0f3af..109135d 100644 --- a/Source/WebCore/dom/KeyboardEvent.cpp +++ b/Source/WebCore/dom/KeyboardEvent.cpp @@ -25,6 +25,7 @@ #include "Document.h" #include "DOMWindow.h" +#include "EventDispatcher.h" #include "EventNames.h" #include "EventHandler.h" #include "Frame.h" @@ -160,4 +161,10 @@ KeyboardEvent* findKeyboardEvent(Event* event) return 0; } +bool KeyboardEvent::dispatch(EventDispatcher* dispatcher) +{ + // Make sure not to return true if we already took default action while handling the event. + return dispatcher->dispatchEvent(this) && !defaultHandled(); +} + } // namespace WebCore diff --git a/Source/WebCore/dom/KeyboardEvent.h b/Source/WebCore/dom/KeyboardEvent.h index eeaef80..ebdb9c8 100644 --- a/Source/WebCore/dom/KeyboardEvent.h +++ b/Source/WebCore/dom/KeyboardEvent.h @@ -29,6 +29,8 @@ namespace WebCore { + class EventDispatcher; + class Node; class PlatformKeyboardEvent; #if PLATFORM(MAC) @@ -99,6 +101,7 @@ namespace WebCore { KeyboardEvent(const AtomicString& type, bool canBubble, bool cancelable, AbstractView*, const String& keyIdentifier, unsigned keyLocation, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, bool altGraphKey); + virtual bool dispatch(EventDispatcher*); OwnPtr<PlatformKeyboardEvent> m_keyEvent; String m_keyIdentifier; diff --git a/Source/WebCore/dom/MouseEvent.cpp b/Source/WebCore/dom/MouseEvent.cpp index bdd39d3..0acbd74 100644 --- a/Source/WebCore/dom/MouseEvent.cpp +++ b/Source/WebCore/dom/MouseEvent.cpp @@ -23,10 +23,23 @@ #include "config.h" #include "MouseEvent.h" +#include "Frame.h" +#include "FrameView.h" #include "EventNames.h" +#include "PlatformMouseEvent.h" namespace WebCore { +PassRefPtr<MouseEvent> MouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, const PlatformMouseEvent& event, const IntPoint& position, int detail, PassRefPtr<Node> relatedTarget) +{ + bool isCancelable = eventType != eventNames().mousemoveEvent; + + return MouseEvent::create(eventType, true, isCancelable, view, + detail, event.globalX(), event.globalY(), position.x(), position.y(), + event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), event.button(), + relatedTarget, 0, false); +} + MouseEvent::MouseEvent() : m_button(0) , m_buttonDown(false) @@ -115,4 +128,25 @@ Node* MouseEvent::fromElement() const return target() ? target()->toNode() : 0; } +PassRefPtr<SimulatedMouseEvent> SimulatedMouseEvent::create(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent) +{ + return adoptRef(new SimulatedMouseEvent(eventType, view, underlyingEvent)); +} + +SimulatedMouseEvent::~SimulatedMouseEvent() +{ +} + +SimulatedMouseEvent::SimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<AbstractView> view, PassRefPtr<Event> underlyingEvent) + : MouseEvent(eventType, true, true, view, 0, 0, 0, 0, 0, false, false, false, false, 0, 0, 0, true) +{ + if (UIEventWithKeyState* keyStateEvent = findEventWithKeyState(underlyingEvent.get())) { + m_ctrlKey = keyStateEvent->ctrlKey(); + m_altKey = keyStateEvent->altKey(); + m_shiftKey = keyStateEvent->shiftKey(); + m_metaKey = keyStateEvent->metaKey(); + } + setUnderlyingEvent(underlyingEvent); +} + } // namespace WebCore diff --git a/Source/WebCore/dom/MouseEvent.h b/Source/WebCore/dom/MouseEvent.h index 7454b04..06e6218 100644 --- a/Source/WebCore/dom/MouseEvent.h +++ b/Source/WebCore/dom/MouseEvent.h @@ -29,6 +29,8 @@ namespace WebCore { +class PlatformMouseEvent; + // Introduced in DOM Level 2 class MouseEvent : public MouseRelatedEvent { public: @@ -44,6 +46,8 @@ namespace WebCore { return adoptRef(new MouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, pageX, pageY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget, clipboard, isSimulated)); } + static PassRefPtr<MouseEvent> create(const AtomicString& eventType, PassRefPtr<AbstractView>, const PlatformMouseEvent&, const IntPoint&, int detail, PassRefPtr<Node> relatedTarget); + virtual ~MouseEvent(); void initMouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, @@ -68,19 +72,30 @@ namespace WebCore { virtual bool isDragEvent() const; virtual int which() const; - private: - MouseEvent(); + protected: MouseEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<AbstractView>, int detail, int screenX, int screenY, int pageX, int pageY, bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, unsigned short button, PassRefPtr<EventTarget> relatedTarget, PassRefPtr<Clipboard> clipboard, bool isSimulated); + private: + MouseEvent(); + unsigned short m_button; bool m_buttonDown; RefPtr<EventTarget> m_relatedTarget; RefPtr<Clipboard> m_clipboard; }; +class SimulatedMouseEvent : public MouseEvent { +public: + static PassRefPtr<SimulatedMouseEvent> create(const AtomicString& eventType, PassRefPtr<AbstractView>, PassRefPtr<Event> underlyingEvent); + virtual ~SimulatedMouseEvent(); + +private: + SimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<AbstractView>, PassRefPtr<Event> underlyingEvent); +}; + } // namespace WebCore #endif // MouseEvent_h diff --git a/Source/WebCore/dom/MouseRelatedEvent.cpp b/Source/WebCore/dom/MouseRelatedEvent.cpp index 072656e..f752670 100644 --- a/Source/WebCore/dom/MouseRelatedEvent.cpp +++ b/Source/WebCore/dom/MouseRelatedEvent.cpp @@ -44,6 +44,7 @@ MouseRelatedEvent::MouseRelatedEvent() , m_offsetX(0) , m_offsetY(0) , m_isSimulated(false) + , m_hasCachedRelativePosition(false) { } @@ -91,21 +92,20 @@ MouseRelatedEvent::MouseRelatedEvent(const AtomicString& eventType, bool canBubb void MouseRelatedEvent::initCoordinates() { // Set up initial values for coordinates. - // Correct values can't be computed until we have at target, so receivedTarget - // does the "real" computation. + // Correct values are computed lazily, see computeRelativePosition. m_layerX = m_pageX; m_layerY = m_pageY; m_offsetX = m_pageX; m_offsetY = m_pageY; computePageLocation(); + m_hasCachedRelativePosition = false; } void MouseRelatedEvent::initCoordinates(int clientX, int clientY) { // Set up initial values for coordinates. - // Correct values can't be computed until we have at target, so receivedTarget - // does the "real" computation. + // Correct values are computed lazily, see computeRelativePosition. m_clientX = clientX; m_clientY = clientY; m_pageX = clientX + contentsX(view()); @@ -116,9 +116,10 @@ void MouseRelatedEvent::initCoordinates(int clientX, int clientY) m_offsetY = m_pageY; computePageLocation(); + m_hasCachedRelativePosition = false; } -static float pageZoomFactor(UIEvent* event) +static float pageZoomFactor(const UIEvent* event) { DOMWindow* window = event->view(); if (!window) @@ -137,9 +138,13 @@ void MouseRelatedEvent::computePageLocation() void MouseRelatedEvent::receivedTarget() { - ASSERT(target()); - Node* targ = target()->toNode(); - if (!targ) + m_hasCachedRelativePosition = false; +} + +void MouseRelatedEvent::computeRelativePosition() +{ + Node* targetNode = target() ? target()->toNode() : 0; + if (!targetNode) return; // Compute coordinates that are based on the target. @@ -149,11 +154,11 @@ void MouseRelatedEvent::receivedTarget() m_offsetY = m_pageY; // Must have an updated render tree for this math to work correctly. - targ->document()->updateStyleIfNeeded(); + targetNode->document()->updateStyleIfNeeded(); // Adjust offsetX/Y to be relative to the target's position. if (!isSimulated()) { - if (RenderObject* r = targ->renderer()) { + if (RenderObject* r = targetNode->renderer()) { FloatPoint localPos = r->absoluteToLocal(absoluteLocation(), false, true); float zoomFactor = pageZoomFactor(this); m_offsetX = lroundf(localPos.x() / zoomFactor); @@ -166,17 +171,48 @@ void MouseRelatedEvent::receivedTarget() // Our RenderLayer is a more modern concept, and layerX/Y is some // other notion about groups of elements (left over from the Netscape 4 days?); // we should test and fix this. - Node* n = targ; + Node* n = targetNode; while (n && !n->renderer()) n = n->parentNode(); - if (n) { - RenderLayer* layer = n->renderer()->enclosingLayer(); + + RenderLayer* layer; + if (n && (layer = n->renderer()->enclosingLayer())) { layer->updateLayerPosition(); for (; layer; layer = layer->parent()) { m_layerX -= layer->x(); m_layerY -= layer->y(); } } + + m_hasCachedRelativePosition = true; +} + +int MouseRelatedEvent::layerX() +{ + if (!m_hasCachedRelativePosition) + computeRelativePosition(); + return m_layerX; +} + +int MouseRelatedEvent::layerY() +{ + if (!m_hasCachedRelativePosition) + computeRelativePosition(); + return m_layerY; +} + +int MouseRelatedEvent::offsetX() +{ + if (!m_hasCachedRelativePosition) + computeRelativePosition(); + return m_offsetX; +} + +int MouseRelatedEvent::offsetY() +{ + if (!m_hasCachedRelativePosition) + computeRelativePosition(); + return m_offsetY; } int MouseRelatedEvent::pageX() const diff --git a/Source/WebCore/dom/MouseRelatedEvent.h b/Source/WebCore/dom/MouseRelatedEvent.h index fc494d1..b03b28a 100644 --- a/Source/WebCore/dom/MouseRelatedEvent.h +++ b/Source/WebCore/dom/MouseRelatedEvent.h @@ -38,10 +38,10 @@ namespace WebCore { int screenY() const { return m_screenY; } int clientX() const { return m_clientX; } int clientY() const { return m_clientY; } - int layerX() const { return m_layerX; } - int layerY() const { return m_layerY; } - int offsetX() const { return m_offsetX; } - int offsetY() const { return m_offsetY; } + int layerX(); + int layerY(); + int offsetX(); + int offsetY(); bool isSimulated() const { return m_isSimulated; } virtual int pageX() const; virtual int pageY() const; @@ -64,6 +64,7 @@ namespace WebCore { virtual void receivedTarget(); void computePageLocation(); + void computeRelativePosition(); // Expose these so MouseEvent::initMouseEvent can set them. int m_screenX; @@ -80,6 +81,7 @@ namespace WebCore { int m_offsetY; IntPoint m_absoluteLocation; bool m_isSimulated; + bool m_hasCachedRelativePosition; }; } // namespace WebCore diff --git a/Source/WebCore/dom/NamedNodeMap.cpp b/Source/WebCore/dom/NamedNodeMap.cpp index 2861226..6fa30bf 100644 --- a/Source/WebCore/dom/NamedNodeMap.cpp +++ b/Source/WebCore/dom/NamedNodeMap.cpp @@ -100,12 +100,6 @@ PassRefPtr<Node> NamedNodeMap::setNamedItem(Node* arg, ExceptionCode& ec) return 0; } - // WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map. - if (arg->document() != m_element->document()) { - ec = WRONG_DOCUMENT_ERR; - return 0; - } - // Not mentioned in spec: throw a HIERARCHY_REQUEST_ERROR if the user passes in a non-attribute node if (!arg->isAttributeNode()) { ec = HIERARCHY_REQUEST_ERR; diff --git a/Source/WebCore/dom/Node.cpp b/Source/WebCore/dom/Node.cpp index c125d16..4def034 100644 --- a/Source/WebCore/dom/Node.cpp +++ b/Source/WebCore/dom/Node.cpp @@ -51,6 +51,7 @@ #include "Element.h" #include "Event.h" #include "EventContext.h" +#include "EventDispatcher.h" #include "EventException.h" #include "EventHandler.h" #include "EventListener.h" @@ -106,10 +107,13 @@ #if ENABLE(SVG) #include "SVGElementInstance.h" -#include "SVGNames.h" #include "SVGUseElement.h" #endif +#if ENABLE(WML) +#include "WMLNames.h" +#endif + #if ENABLE(XHTMLMP) #include "HTMLNoScriptElement.h" #endif @@ -126,8 +130,6 @@ namespace WebCore { using namespace HTMLNames; -static HashSet<Node*>* gNodesDispatchingSimulatedClicks = 0; - bool Node::isSupported(const String& feature, const String& version) { return DOMImplementation::hasFeature(feature, version); @@ -347,6 +349,12 @@ Node::StyleChange Node::diff(const RenderStyle* s1, const RenderStyle* s2) } } + // When text-combine property has been changed, we need to prepare a separate renderer object. + // When text-combine is on, we use RenderCombineText, otherwise RenderText. + // https://bugs.webkit.org/show_bug.cgi?id=55069 + if ((s1 && s2) && (s1->hasTextCombine() != s2->hasTextCombine())) + ch = Detach; + return ch; } @@ -506,6 +514,18 @@ void Node::setShadowHost(Element* host) setParent(host); } +InputElement* Node::toInputElement() +{ + // If one of the below ASSERTs trigger, you are calling this function + // directly or indirectly from a constructor or destructor of this object. + // Don't do this! + ASSERT(!(isHTMLElement() && hasTagName(inputTag))); +#if ENABLE(WML) + ASSERT(!(isWMLElement() && hasTagName(WMLNames::inputTag))); +#endif + return 0; +} + short Node::tabIndex() const { return hasRareData() ? rareData()->tabIndex() : 0; @@ -694,19 +714,36 @@ void Node::deprecatedParserAddChild(PassRefPtr<Node>) { } -bool Node::isContentEditable() const +bool Node::rendererIsEditable(EditableLevel editableLevel) const { - return parentOrHostNode() && parentOrHostNode()->isContentEditable(); -} + if (document()->inDesignMode() || (document()->frame() && document()->frame()->page() && document()->frame()->page()->isEditable())) + return true; -bool Node::isContentRichlyEditable() const -{ - return parentOrHostNode() && parentOrHostNode()->isContentRichlyEditable(); + // Ideally we'd call ASSERT(!needsStyleRecalc()) here, but + // ContainerNode::setFocus() calls setNeedsStyleRecalc(), so the assertion + // would fire in the middle of Document::setFocusedNode(). + + for (const Node* node = this; node; node = node->parentNode()) { + if ((node->isHTMLElement() || node->isDocumentNode()) && node->renderer()) { + switch (node->renderer()->style()->userModify()) { + case READ_ONLY: + return false; + case READ_WRITE: + return true; + case READ_WRITE_PLAINTEXT_ONLY: + return editableLevel != RichlyEditable; + } + ASSERT_NOT_REACHED(); + return false; + } + } + + return false; } bool Node::shouldUseInputMethod() const { - return isContentEditable(); + return rendererIsEditable(); } RenderBox* Node::renderBox() const @@ -764,10 +801,16 @@ bool Node::hasNonEmptyBoundingBox() const void Node::setDocumentRecursively(Document* document) { - // FIXME: To match Gecko, we should do this for nodes that are already in the document as well. - if (this->document() == document || this->inDocument()) + if (this->document() == document) return; + // If an element is moved from a document and then eventually back again the collection cache for + // that element may contain stale data as changes made to it will have updated the DOMTreeVersion + // of the document it was moved to. By increasing the DOMTreeVersion of the donating document here + // we ensure that the collection cache will be invalidated as needed when the element is moved back. + if (this->document()) + this->document()->incDOMTreeVersion(); + for (Node* node = this; node; node = node->traverseNextNode(this)) { node->setDocument(document); if (!node->isElementNode()) @@ -1147,37 +1190,25 @@ bool Node::canReplaceChild(Node* newChild, Node*) static void checkAcceptChild(Node* newParent, Node* newChild, ExceptionCode& ec) { - // Perform error checking as required by spec for adding a new child. Used by replaceChild(). - // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null if (!newChild) { ec = NOT_FOUND_ERR; return; } - // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly if (newParent->isReadOnlyNode()) { ec = NO_MODIFICATION_ALLOWED_ERR; return; } - - // WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that - // created this node. - // We assume that if newChild is a DocumentFragment, all children are created from the same document - // as the fragment itself (otherwise they could not have been added as children) - if (newChild->document() != newParent->document() && newChild->inDocument()) { - // but if the child is not in a document yet then loosen the - // restriction, so that e.g. creating an element with the Option() - // constructor and then adding it to a different document works, - // as it does in Mozilla and Mac IE. - ec = WRONG_DOCUMENT_ERR; + + if (newChild->inDocument() && newChild->nodeType() == Node::DOCUMENT_TYPE_NODE) { + ec = HIERARCHY_REQUEST_ERR; return; } - + // HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the // newChild node, or if the node to append is one of this node's ancestors. - // check for ancestor/same node if (newChild == newParent || newParent->isDescendantOf(newChild)) { ec = HIERARCHY_REQUEST_ERR; return; @@ -1186,6 +1217,11 @@ static void checkAcceptChild(Node* newParent, Node* newChild, ExceptionCode& ec) void Node::checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode& ec) { + if (!oldChild) { + ec = NOT_FOUND_ERR; + return; + } + checkAcceptChild(this, newChild, ec); if (ec) return; @@ -1481,7 +1517,7 @@ int Node::maxCharacterOffset() const // is obviously misplaced. bool Node::canStartSelection() const { - if (isContentEditable()) + if (rendererIsEditable()) return true; if (renderer()) { @@ -1559,7 +1595,7 @@ Element *Node::enclosingBlockFlowElement() const Element* Node::rootEditableElement() const { Element* result = 0; - for (Node* n = const_cast<Node*>(this); n && n->isContentEditable(); n = n->parentNode()) { + for (Node* n = const_cast<Node*>(this); n && n->rendererIsEditable(); n = n->parentNode()) { if (n->isElementNode()) result = static_cast<Element*>(n); if (n->hasTagName(bodyTag)) @@ -2638,200 +2674,14 @@ void Node::handleLocalEvents(Event* event) fireEventListeners(event); } -static inline EventTarget* eventTargetRespectingSVGTargetRules(Node* referenceNode) -{ - ASSERT(referenceNode); - -#if ENABLE(SVG) - if (!referenceNode->isSVGElement()) - return referenceNode; - - // Spec: The event handling for the non-exposed tree works as if the referenced element had been textually included - // as a deeply cloned child of the 'use' element, except that events are dispatched to the SVGElementInstance objects - for (Node* n = referenceNode; n; n = n->parentNode()) { - if (!n->isShadowRoot() || !n->isSVGElement()) - continue; - - Element* shadowTreeParentElement = n->shadowHost(); - ASSERT(shadowTreeParentElement->hasTagName(SVGNames::useTag)); - - if (SVGElementInstance* instance = static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode)) - return instance; - } -#endif - - return referenceNode; -} - -void Node::getEventAncestors(Vector<EventContext>& ancestors, EventTarget* originalTarget, EventDispatchBehavior behavior) -{ - if (!inDocument()) - return; - - EventTarget* target = originalTarget; - Node* ancestor = this; - bool shouldSkipNextAncestor = false; - while (true) { - if (ancestor->isShadowRoot()) { - if (behavior == StayInsideShadowDOM) - return; - ancestor = ancestor->shadowHost(); - if (!shouldSkipNextAncestor) - target = ancestor; - } else - ancestor = ancestor->parentNodeGuaranteedHostFree(); - - if (!ancestor) - return; - -#if ENABLE(SVG) - // Skip SVGShadowTreeRootElement. - shouldSkipNextAncestor = ancestor->isSVGElement() && ancestor->isShadowRoot(); - if (shouldSkipNextAncestor) - continue; -#endif - // FIXME: Unroll the extra loop inside eventTargetRespectingSVGTargetRules into this loop. - ancestors.append(EventContext(ancestor, eventTargetRespectingSVGTargetRules(ancestor), target)); - - } -} - -bool Node::dispatchEvent(PassRefPtr<Event> prpEvent) -{ - RefPtr<EventTarget> protect = this; - RefPtr<Event> event = prpEvent; - - event->setTarget(eventTargetRespectingSVGTargetRules(this)); - - RefPtr<FrameView> view = document()->view(); - return dispatchGenericEvent(event.release()); -} - void Node::dispatchScopedEvent(PassRefPtr<Event> event) { - // We need to set the target here because it can go away by the time we actually fire the event. - event->setTarget(eventTargetRespectingSVGTargetRules(this)); - - ScopedEventQueue::instance()->enqueueEvent(event); -} - -static const EventContext* topEventContext(const Vector<EventContext>& ancestors) -{ - return ancestors.isEmpty() ? 0 : &ancestors.last(); + EventDispatcher::dispatchScopedEvent(this, event); } -static EventDispatchBehavior determineDispatchBehavior(Event* event) +bool Node::dispatchEvent(PassRefPtr<Event> event) { - // Per XBL 2.0 spec, mutation events should never cross shadow DOM boundary: - // http://dev.w3.org/2006/xbl2/#event-flow-and-targeting-across-shadow-s - if (event->isMutationEvent()) - return StayInsideShadowDOM; - - // WebKit never allowed selectstart event to cross the the shadow DOM boundary. - // Changing this breaks existing sites. - // See https://bugs.webkit.org/show_bug.cgi?id=52195 for details. - if (event->type() == eventNames().selectstartEvent) - return StayInsideShadowDOM; - - return RetargetEvent; -} - -bool Node::dispatchGenericEvent(PassRefPtr<Event> prpEvent) -{ - RefPtr<Event> event(prpEvent); - - ASSERT(!eventDispatchForbidden()); - ASSERT(event->target()); - ASSERT(!event->type().isNull()); // JavaScript code can create an event with an empty name, but not null. - - // Make a vector of ancestors to send the event to. - // If the node is not in a document just send the event to it. - // Be sure to ref all of nodes since event handlers could result in the last reference going away. - RefPtr<Node> thisNode(this); - RefPtr<EventTarget> originalTarget = event->target(); - Vector<EventContext> ancestors; - getEventAncestors(ancestors, originalTarget.get(), determineDispatchBehavior(event.get())); - - WindowEventContext windowContext(event.get(), this, topEventContext(ancestors)); - - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEvent(document(), *event, windowContext.window(), this, ancestors); - - // Give the target node a chance to do some work before DOM event handlers get a crack. - void* data = preDispatchEventHandler(event.get()); - if (event->propagationStopped()) - goto doneDispatching; - - // Trigger capturing event handlers, starting at the top and working our way down. - event->setEventPhase(Event::CAPTURING_PHASE); - - if (windowContext.handleLocalEvents(event.get()) && event->propagationStopped()) - goto doneDispatching; - - for (size_t i = ancestors.size(); i; --i) { - ancestors[i - 1].handleLocalEvents(event.get()); - if (event->propagationStopped()) - goto doneDispatching; - } - - event->setEventPhase(Event::AT_TARGET); - event->setTarget(originalTarget.get()); - event->setCurrentTarget(eventTargetRespectingSVGTargetRules(this)); - handleLocalEvents(event.get()); - if (event->propagationStopped()) - goto doneDispatching; - - if (event->bubbles() && !event->cancelBubble()) { - // Trigger bubbling event handlers, starting at the bottom and working our way up. - event->setEventPhase(Event::BUBBLING_PHASE); - - size_t size = ancestors.size(); - for (size_t i = 0; i < size; ++i) { - ancestors[i].handleLocalEvents(event.get()); - if (event->propagationStopped() || event->cancelBubble()) - goto doneDispatching; - } - windowContext.handleLocalEvents(event.get()); - } - -doneDispatching: - event->setTarget(originalTarget.get()); - event->setCurrentTarget(0); - event->setEventPhase(0); - - // Pass the data from the preDispatchEventHandler to the postDispatchEventHandler. - postDispatchEventHandler(event.get(), data); - - // Call default event handlers. While the DOM does have a concept of preventing - // default handling, the detail of which handlers are called is an internal - // implementation detail and not part of the DOM. - if (!event->defaultPrevented() && !event->defaultHandled()) { - // Non-bubbling events call only one default event handler, the one for the target. - defaultEventHandler(event.get()); - ASSERT(!event->defaultPrevented()); - if (event->defaultHandled()) - goto doneWithDefault; - // For bubbling events, call default event handlers on the same targets in the - // same order as the bubbling phase. - if (event->bubbles()) { - size_t size = ancestors.size(); - for (size_t i = 0; i < size; ++i) { - ancestors[i].node()->defaultEventHandler(event.get()); - ASSERT(!event->defaultPrevented()); - if (event->defaultHandled()) - goto doneWithDefault; - } - } - } - -doneWithDefault: - - // Ensure that after event dispatch, the event's target object is the - // outermost shadow DOM boundary. - event->setTarget(windowContext.target()); - event->setCurrentTarget(0); - InspectorInstrumentation::didDispatchEvent(cookie); - - return !event->defaultPrevented(); + return EventDispatcher::dispatchEvent(this, event); } void Node::dispatchSubtreeModifiedEvent() @@ -2861,209 +2711,25 @@ void Node::dispatchUIEvent(const AtomicString& eventType, int detail, PassRefPtr dispatchScopedEvent(event.release()); } -bool Node::dispatchKeyEvent(const PlatformKeyboardEvent& key) +bool Node::dispatchKeyEvent(const PlatformKeyboardEvent& event) { - RefPtr<KeyboardEvent> keyboardEvent = KeyboardEvent::create(key, document()->defaultView()); - bool r = dispatchEvent(keyboardEvent); - - // we want to return false if default is prevented (already taken care of) - // or if the element is default-handled by the DOM. Otherwise we let it just - // let it get handled by AppKit - if (keyboardEvent->defaultHandled()) - r = false; - - return r; + return EventDispatcher::dispatchEvent(this, KeyboardEvent::create(event, document()->defaultView())); } bool Node::dispatchMouseEvent(const PlatformMouseEvent& event, const AtomicString& eventType, int detail, Node* relatedTarget) { - ASSERT(!eventDispatchForbidden()); - - IntPoint contentsPos; - if (FrameView* view = document()->view()) - contentsPos = view->windowToContents(event.pos()); - - short button = event.button(); - - ASSERT(event.eventType() == MouseEventMoved || button != NoButton); - - return dispatchMouseEvent(eventType, button, detail, - contentsPos.x(), contentsPos.y(), event.globalX(), event.globalY(), - event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), - false, relatedTarget, 0); -} - -void Node::dispatchSimulatedMouseEvent(const AtomicString& eventType, - PassRefPtr<Event> underlyingEvent) -{ - ASSERT(!eventDispatchForbidden()); - - bool ctrlKey = false; - bool altKey = false; - bool shiftKey = false; - bool metaKey = false; - if (UIEventWithKeyState* keyStateEvent = findEventWithKeyState(underlyingEvent.get())) { - ctrlKey = keyStateEvent->ctrlKey(); - altKey = keyStateEvent->altKey(); - shiftKey = keyStateEvent->shiftKey(); - metaKey = keyStateEvent->metaKey(); - } - - // Like Gecko, we just pass 0 for everything when we make a fake mouse event. - // Internet Explorer instead gives the current mouse position and state. - dispatchMouseEvent(eventType, 0, 0, 0, 0, 0, 0, - ctrlKey, altKey, shiftKey, metaKey, true, 0, underlyingEvent); + return EventDispatcher::dispatchMouseEvent(this, event, eventType, detail, relatedTarget); } void Node::dispatchSimulatedClick(PassRefPtr<Event> event, bool sendMouseEvents, bool showPressedLook) { - if (!gNodesDispatchingSimulatedClicks) - gNodesDispatchingSimulatedClicks = new HashSet<Node*>; - else if (gNodesDispatchingSimulatedClicks->contains(this)) - return; - - gNodesDispatchingSimulatedClicks->add(this); - - // send mousedown and mouseup before the click, if requested - if (sendMouseEvents) - dispatchSimulatedMouseEvent(eventNames().mousedownEvent, event.get()); - setActive(true, showPressedLook); - if (sendMouseEvents) - dispatchSimulatedMouseEvent(eventNames().mouseupEvent, event.get()); - setActive(false); - - // always send click - dispatchSimulatedMouseEvent(eventNames().clickEvent, event); - - gNodesDispatchingSimulatedClicks->remove(this); -} - -// FIXME: Once https://bugs.webkit.org/show_bug.cgi?id=52963 lands, this should -// be greatly improved. See https://bugs.webkit.org/show_bug.cgi?id=54025. -static Node* pullOutOfShadow(Node* node) -{ - Node* outermostShadowBoundary = node; - for (Node* n = node; n; n = n->parentOrHostNode()) { - if (n->isShadowRoot()) - outermostShadowBoundary = n->parentOrHostNode(); - } - return outermostShadowBoundary; -} - -bool Node::dispatchMouseEvent(const AtomicString& eventType, int button, int detail, - int pageX, int pageY, int screenX, int screenY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, - bool isSimulated, Node* relatedTargetArg, PassRefPtr<Event> underlyingEvent) -{ - ASSERT(!eventDispatchForbidden()); - if (disabled()) // Don't even send DOM events for disabled controls.. - return true; - - if (eventType.isEmpty()) - return false; // Shouldn't happen. - - // Dispatching the first event can easily result in this node being destroyed. - // Since we dispatch up to three events here, we need to make sure we're referenced - // so the pointer will be good for the two subsequent ones. - RefPtr<Node> protect(this); - - bool cancelable = eventType != eventNames().mousemoveEvent; - - bool swallowEvent = false; - - // Attempting to dispatch with a non-EventTarget relatedTarget causes the relatedTarget to be silently ignored. - RefPtr<Node> relatedTarget = pullOutOfShadow(relatedTargetArg); - - int adjustedPageX = pageX; - int adjustedPageY = pageY; - if (Frame* frame = document()->frame()) { - float pageZoom = frame->pageZoomFactor(); - if (pageZoom != 1.0f) { - // Adjust our pageX and pageY to account for the page zoom. - adjustedPageX = lroundf(pageX / pageZoom); - adjustedPageY = lroundf(pageY / pageZoom); - } - } - - RefPtr<MouseEvent> mouseEvent = MouseEvent::create(eventType, - true, cancelable, document()->defaultView(), - detail, screenX, screenY, adjustedPageX, adjustedPageY, - ctrlKey, altKey, shiftKey, metaKey, button, - relatedTarget, 0, isSimulated); - mouseEvent->setUnderlyingEvent(underlyingEvent.get()); - mouseEvent->setAbsoluteLocation(IntPoint(pageX, pageY)); - - dispatchEvent(mouseEvent); - bool defaultHandled = mouseEvent->defaultHandled(); - bool defaultPrevented = mouseEvent->defaultPrevented(); - if (defaultHandled || defaultPrevented) - swallowEvent = true; - - // Special case: If it's a double click event, we also send the dblclick event. This is not part - // of the DOM specs, but is used for compatibility with the ondblclick="" attribute. This is treated - // as a separate event in other DOM-compliant browsers like Firefox, and so we do the same. - if (eventType == eventNames().clickEvent && detail == 2) { - RefPtr<Event> doubleClickEvent = MouseEvent::create(eventNames().dblclickEvent, - true, cancelable, document()->defaultView(), - detail, screenX, screenY, adjustedPageX, adjustedPageY, - ctrlKey, altKey, shiftKey, metaKey, button, - relatedTarget, 0, isSimulated); - doubleClickEvent->setUnderlyingEvent(underlyingEvent.get()); - if (defaultHandled) - doubleClickEvent->setDefaultHandled(); - dispatchEvent(doubleClickEvent); - if (doubleClickEvent->defaultHandled() || doubleClickEvent->defaultPrevented()) - swallowEvent = true; - } - - return swallowEvent; + EventDispatcher::dispatchSimulatedClick(this, event, sendMouseEvents, showPressedLook); } void Node::dispatchWheelEvent(PlatformWheelEvent& e) { - ASSERT(!eventDispatchForbidden()); - if (e.deltaX() == 0 && e.deltaY() == 0) - return; - - FrameView* view = document()->view(); - if (!view) - return; - - IntPoint pos = view->windowToContents(e.pos()); - - int adjustedPageX = pos.x(); - int adjustedPageY = pos.y(); - if (Frame* frame = document()->frame()) { - float pageZoom = frame->pageZoomFactor(); - if (pageZoom != 1.0f) { - // Adjust our pageX and pageY to account for the page zoom. - adjustedPageX = lroundf(pos.x() / pageZoom); - adjustedPageY = lroundf(pos.y() / pageZoom); - } - } - - WheelEvent::Granularity granularity; - switch (e.granularity()) { - case ScrollByPageWheelEvent: - granularity = WheelEvent::Page; - break; - case ScrollByPixelWheelEvent: - default: - granularity = WheelEvent::Pixel; - break; - } - - RefPtr<WheelEvent> we = WheelEvent::create(e.wheelTicksX(), e.wheelTicksY(), e.deltaX(), e.deltaY(), granularity, - document()->defaultView(), e.globalX(), e.globalY(), adjustedPageX, adjustedPageY, - e.ctrlKey(), e.altKey(), e.shiftKey(), e.metaKey()); - - we->setAbsoluteLocation(IntPoint(pos.x(), pos.y())); - - if (!dispatchEvent(we) || we->defaultHandled()) - e.accept(); - - we.release(); + EventDispatcher::dispatchWheelEvent(this, e); } void Node::dispatchFocusEvent() diff --git a/Source/WebCore/dom/Node.h b/Source/WebCore/dom/Node.h index 7ef7e80..31f6ae8 100644 --- a/Source/WebCore/dom/Node.h +++ b/Source/WebCore/dom/Node.h @@ -53,6 +53,7 @@ class EventContext; class EventListener; class FloatPoint; class Frame; +class InputElement; class IntRect; class KeyboardEvent; class NSResolver; @@ -86,11 +87,6 @@ enum StyleChangeType { SyntheticStyleChange = 3 << nodeStyleChangeShift }; -enum EventDispatchBehavior { - RetargetEvent, - StayInsideShadowDOM -}; - class Node : public EventTarget, public TreeShared<ContainerNode>, public ScriptWrappable { friend class Document; public: @@ -224,9 +220,6 @@ public: // Returns the enclosing event parent node (or self) that, when clicked, would trigger a navigation. Node* enclosingLinkEventParentOrSelf(); - // Node ancestors when concerned about event flow. - void getEventAncestors(Vector<EventContext>& ancestors, EventTarget*, EventDispatchBehavior = RetargetEvent); - bool isBlockFlow() const; bool isBlockFlowOrBlockTable() const; @@ -326,8 +319,12 @@ public: virtual bool isKeyboardFocusable(KeyboardEvent*) const; virtual bool isMouseFocusable() const; - virtual bool isContentEditable() const; - virtual bool isContentRichlyEditable() const; +#if PLATFORM(MAC) + // Objective-C extensions + bool isContentEditable() const { return rendererIsEditable(Editable); } +#endif + bool rendererIsEditable() const { return rendererIsEditable(Editable); } + bool rendererIsRichlyEditable() const { return rendererIsEditable(RichlyEditable); } virtual bool shouldUseInputMethod() const; virtual IntRect getRect() const; IntRect renderRect(bool* isReplaced); @@ -529,6 +526,8 @@ public: virtual Node* toNode() { return this; } + virtual InputElement* toInputElement(); + virtual ScriptExecutionContext* scriptExecutionContext() const; virtual bool addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); @@ -543,7 +542,6 @@ public: bool dispatchEvent(PassRefPtr<Event>); void dispatchScopedEvent(PassRefPtr<Event>); - bool dispatchGenericEvent(PassRefPtr<Event>); virtual void handleLocalEvents(Event*); void dispatchSubtreeModifiedEvent(); @@ -551,11 +549,6 @@ public: bool dispatchKeyEvent(const PlatformKeyboardEvent&); void dispatchWheelEvent(PlatformWheelEvent&); bool dispatchMouseEvent(const PlatformMouseEvent&, const AtomicString& eventType, int clickCount = 0, Node* relatedTarget = 0); - bool dispatchMouseEvent(const AtomicString& eventType, int button, int clickCount, - int pageX, int pageY, int screenX, int screenY, - bool ctrlKey, bool altKey, bool shiftKey, bool metaKey, - bool isSimulated, Node* relatedTarget, PassRefPtr<Event> underlyingEvent); - void dispatchSimulatedMouseEvent(const AtomicString& eventType, PassRefPtr<Event> underlyingEvent); void dispatchSimulatedClick(PassRefPtr<Event> underlyingEvent, bool sendMouseEvents = false, bool showPressedLook = true); virtual void dispatchFocusEvent(); @@ -668,6 +661,9 @@ private: void markCachedNodeListsSlow(JSC::MarkStack&, JSC::JSGlobalData&); #endif + enum EditableLevel { Editable, RichlyEditable }; + bool rendererIsEditable(EditableLevel) const; + void setStyleChange(StyleChangeType); // Used to share code between lazyAttach and setNeedsStyleRecalc. diff --git a/Source/WebCore/dom/Position.cpp b/Source/WebCore/dom/Position.cpp index 473610a..1b58a42 100644 --- a/Source/WebCore/dom/Position.cpp +++ b/Source/WebCore/dom/Position.cpp @@ -46,7 +46,7 @@ using namespace HTMLNames; static Node* nextRenderedEditable(Node* node) { while ((node = node->nextLeafNode())) { - if (!node->isContentEditable()) + if (!node->rendererIsEditable()) continue; RenderObject* renderer = node->renderer(); if (!renderer) @@ -60,7 +60,7 @@ static Node* nextRenderedEditable(Node* node) static Node* previousRenderedEditable(Node* node) { while ((node = node->previousLeafNode())) { - if (!node->isContentEditable()) + if (!node->rendererIsEditable()) continue; RenderObject* renderer = node->renderer(); if (!renderer) @@ -94,11 +94,13 @@ Position::Position(PassRefPtr<Node> anchorNode, int offset, AnchorType anchorTyp , m_anchorType(anchorType) , m_isLegacyEditingPosition(false) { + ASSERT(!m_anchorNode || !editingIgnoresContent(m_anchorNode.get())); ASSERT(anchorType == PositionIsOffsetInAnchor); } void Position::moveToPosition(PassRefPtr<Node> node, int offset) { + ASSERT(!editingIgnoresContent(node.get())); ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition); m_anchorNode = node; m_offset = offset; @@ -161,12 +163,12 @@ Position Position::parentAnchoredEquivalent() const return Position(); // FIXME: This should only be necessary for legacy positions, but is also needed for positions before and after Tables - if (m_offset <= 0) { + if (m_offset <= 0 && m_anchorType != PositionIsAfterAnchor) { if (m_anchorNode->parentNode() && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get()))) return positionInParentBeforeNode(m_anchorNode.get()); - return Position(m_anchorNode, 0, PositionIsOffsetInAnchor); + return firstPositionInOrBeforeNode(m_anchorNode.get()); } - if (!m_anchorNode->offsetInCharacters() && static_cast<unsigned>(m_offset) == m_anchorNode->childNodeCount() + if (!m_anchorNode->offsetInCharacters() && (m_anchorType == PositionIsAfterAnchor || static_cast<unsigned>(m_offset) == m_anchorNode->childNodeCount()) && (editingIgnoresContent(m_anchorNode.get()) || isTableElement(m_anchorNode.get()))) { return positionInParentAfterNode(m_anchorNode.get()); } @@ -240,15 +242,15 @@ Position Position::previous(PositionMoveType moveType) const Node* n = deprecatedNode(); if (!n) return *this; - - int o = m_offset; + + int o = deprecatedEditingOffset(); // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. ASSERT(o >= 0); if (o > 0) { Node* child = n->childNode(o - 1); if (child) - return lastDeepEditingPositionForNode(child); + return lastPositionInOrAfterNode(child); // There are two reasons child might be 0: // 1) The node is node like a text node that is not an element, and therefore has no children. @@ -279,15 +281,15 @@ Position Position::next(PositionMoveType moveType) const Node* n = deprecatedNode(); if (!n) return *this; - - int o = m_offset; + + int o = deprecatedEditingOffset(); // FIXME: Negative offsets shouldn't be allowed. We should catch this earlier. ASSERT(o >= 0); Node* child = n->childNode(o); if (child || (!n->hasChildNodes() && o < lastOffsetForEditing(n))) { if (child) - return firstDeepEditingPositionForNode(child); + return firstPositionInOrBeforeNode(child); // There are two reasons child might be 0: // 1) The node is node like a text node that is not an element, and therefore has no children. @@ -323,14 +325,14 @@ bool Position::atFirstEditingPositionForNode() const { if (isNull()) return true; - return m_offset <= 0; + return m_anchorType == PositionIsBeforeAnchor || m_offset <= 0; } bool Position::atLastEditingPositionForNode() const { if (isNull()) return true; - return m_offset >= lastOffsetForEditing(deprecatedNode()); + return m_anchorType == PositionIsAfterAnchor || m_offset >= lastOffsetForEditing(deprecatedNode()); } // A position is considered at editing boundary if one of the following is true: @@ -343,15 +345,15 @@ bool Position::atLastEditingPositionForNode() const bool Position::atEditingBoundary() const { Position nextPosition = downstream(CanCrossEditingBoundary); - if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.deprecatedNode()->isContentEditable()) + if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable()) return true; Position prevPosition = upstream(CanCrossEditingBoundary); - if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->isContentEditable()) + if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable()) return true; - return nextPosition.isNotNull() && !nextPosition.deprecatedNode()->isContentEditable() - && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->isContentEditable(); + return nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable() + && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable(); } Node* Position::parentEditingBoundary() const @@ -364,7 +366,7 @@ Node* Position::parentEditingBoundary() const return 0; Node* boundary = m_anchorNode.get(); - while (boundary != documentElement && boundary->parentNode() && m_anchorNode->isContentEditable() == boundary->parentNode()->isContentEditable()) + while (boundary != documentElement && boundary->parentNode() && m_anchorNode->rendererIsEditable() == boundary->parentNode()->rendererIsEditable()) boundary = boundary->parentNode(); return boundary; @@ -523,17 +525,17 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const // FIXME: PositionIterator should respect Before and After positions. PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? Position(m_anchorNode, caretMaxOffset(m_anchorNode.get())) : *this; PositionIterator currentPos = lastVisible; - bool startEditable = startNode->isContentEditable(); + bool startEditable = startNode->rendererIsEditable(); Node* lastNode = startNode; bool boundaryCrossed = false; for (; !currentPos.atStart(); currentPos.decrement()) { Node* currentNode = currentPos.node(); // Don't check for an editability change if we haven't moved to a different node, - // to avoid the expense of computing isContentEditable(). + // to avoid the expense of computing rendererIsEditable(). if (currentNode != lastNode) { // Don't change editability. - bool currentEditable = currentNode->isContentEditable(); + bool currentEditable = currentNode->rendererIsEditable(); if (startEditable != currentEditable) { if (rule == CannotCrossEditingBoundary) break; @@ -569,7 +571,7 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const // Return position after tables and nodes which have content that can be ignored. if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { if (currentPos.atEndOfNode()) - return lastDeepEditingPositionForNode(currentNode); + return positionAfterNode(currentNode); continue; } @@ -645,17 +647,17 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const // FIXME: PositionIterator should respect Before and After positions. PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? Position(m_anchorNode, caretMaxOffset(m_anchorNode.get())) : *this; PositionIterator currentPos = lastVisible; - bool startEditable = startNode->isContentEditable(); + bool startEditable = startNode->rendererIsEditable(); Node* lastNode = startNode; bool boundaryCrossed = false; for (; !currentPos.atEnd(); currentPos.increment()) { Node* currentNode = currentPos.node(); // Don't check for an editability change if we haven't moved to a different node, - // to avoid the expense of computing isContentEditable(). + // to avoid the expense of computing rendererIsEditable(). if (currentNode != lastNode) { // Don't change editability. - bool currentEditable = currentNode->isContentEditable(); + bool currentEditable = currentNode->rendererIsEditable(); if (startEditable != currentEditable) { if (rule == CannotCrossEditingBoundary) break; @@ -781,7 +783,8 @@ bool Position::isCandidate() const return false; if (renderer->isBR()) - return !m_offset && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); + // FIXME: The condition should be m_anchorType == PositionIsBeforeAnchor, but for now we still need to support legacy positions. + return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); if (renderer->isText()) return !nodeIsUserSelectNone(deprecatedNode()) && inRenderedText(); @@ -796,10 +799,10 @@ bool Position::isCandidate() const if (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)) { if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer)) return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(deprecatedNode()); - return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); + return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); } } else - return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); + return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary(); return false; } @@ -982,7 +985,7 @@ Position Position::trailingWhitespacePosition(EAffinity, bool considerNonCollaps VisiblePosition v(*this); UChar c = v.characterAfter(); // The space must not be in another paragraph and it must be editable. - if (!isEndOfParagraph(v) && v.next(true).isNotNull()) + if (!isEndOfParagraph(v) && v.next(CannotCrossEditingBoundary).isNotNull()) if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c)) return *this; @@ -1053,7 +1056,7 @@ static Position upstreamIgnoringEditingBoundaries(Position position) void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const { - caretOffset = m_offset; + caretOffset = deprecatedEditingOffset(); RenderObject* renderer = deprecatedNode()->renderer(); if (!renderer->isText()) { diff --git a/Source/WebCore/dom/Position.h b/Source/WebCore/dom/Position.h index 4e1eff4..a72664e 100644 --- a/Source/WebCore/dom/Position.h +++ b/Source/WebCore/dom/Position.h @@ -133,6 +133,7 @@ public: // These can be either inside or just before/after the node, depending on // if the node is ignored by editing or not. + // FIXME: These should go away. They only make sense for legacy positions. bool atFirstEditingPositionForNode() const; bool atLastEditingPositionForNode() const; @@ -196,7 +197,7 @@ inline bool operator==(const Position& a, const Position& b) { // FIXME: In <div><img></div> [div, 0] != [img, 0] even though most of the // editing code will treat them as identical. - return a.anchorNode() == b.anchorNode() && a.deprecatedEditingOffset() == b.deprecatedEditingOffset(); + return a.anchorNode() == b.anchorNode() && a.deprecatedEditingOffset() == b.deprecatedEditingOffset() && a.anchorType() == b.anchorType(); } inline bool operator!=(const Position& a, const Position& b) diff --git a/Source/WebCore/dom/PositionIterator.cpp b/Source/WebCore/dom/PositionIterator.cpp index 5de9d9d..6821308 100644 --- a/Source/WebCore/dom/PositionIterator.cpp +++ b/Source/WebCore/dom/PositionIterator.cpp @@ -38,10 +38,13 @@ PositionIterator::operator Position() const { if (m_nodeAfterPositionInAnchor) { ASSERT(m_nodeAfterPositionInAnchor->parentNode() == m_anchorNode); + // FIXME: This check is inadaquete because any ancestor could be ignored by editing + if (editingIgnoresContent(m_nodeAfterPositionInAnchor->parentNode())) + return positionBeforeNode(m_anchorNode); return positionInParentBeforeNode(m_nodeAfterPositionInAnchor); } if (m_anchorNode->hasChildNodes()) - return lastDeepEditingPositionForNode(m_anchorNode); + return lastPositionInOrAfterNode(m_anchorNode); return Position(m_anchorNode, m_offsetInAnchor); } @@ -166,7 +169,7 @@ bool PositionIterator::isCandidate() const if (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)) { if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer)) return atStartOfNode() && !Position::nodeIsUserSelectNone(m_anchorNode); - return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).atEditingBoundary(); + return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).atEditingBoundary(); } } diff --git a/Source/WebCore/dom/QualifiedName.h b/Source/WebCore/dom/QualifiedName.h index cb95f20..192e7bc 100644 --- a/Source/WebCore/dom/QualifiedName.h +++ b/Source/WebCore/dom/QualifiedName.h @@ -113,7 +113,7 @@ inline bool operator!=(const QualifiedName& q, const AtomicString& a) { return a inline unsigned hashComponents(const QualifiedNameComponents& buf) { - return WTF::StringHasher::createBlobHash<sizeof(QualifiedNameComponents)>(&buf); + return StringHasher::hashMemory<sizeof(QualifiedNameComponents)>(&buf); } struct QualifiedNameHash { diff --git a/Source/WebCore/dom/Range.cpp b/Source/WebCore/dom/Range.cpp index a0370fa..423d43f 100644 --- a/Source/WebCore/dom/Range.cpp +++ b/Source/WebCore/dom/Range.cpp @@ -932,13 +932,6 @@ void Range::insertNode(PassRefPtr<Node> prpNewNode, ExceptionCode& ec) return; } - // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were - // not created from the same document. - if (newNode->document() != m_start.container()->document()) { - ec = WRONG_DOCUMENT_ERR; - return; - } - // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that // does not allow children of the type of newNode or if newNode is an ancestor of the container. @@ -1423,13 +1416,6 @@ void Range::surroundContents(PassRefPtr<Node> passNewParent, ExceptionCode& ec) return; } - // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were - // not created from the same document. - if (newParent->document() != m_start.container()->document()) { - ec = WRONG_DOCUMENT_ERR; - return; - } - // Raise a HIERARCHY_REQUEST_ERR if m_start.container() doesn't accept children like newParent. Node* parentOfNewParent = m_start.container(); diff --git a/Source/WebCore/dom/ScriptElement.cpp b/Source/WebCore/dom/ScriptElement.cpp index 3bba9a0..9a07bb8 100644 --- a/Source/WebCore/dom/ScriptElement.cpp +++ b/Source/WebCore/dom/ScriptElement.cpp @@ -24,9 +24,9 @@ #include "config.h" #include "ScriptElement.h" -#include "AsyncScriptRunner.h" #include "CachedScript.h" #include "CachedResourceLoader.h" +#include "ContentSecurityPolicy.h" #include "Document.h" #include "DocumentParser.h" #include "Frame.h" @@ -37,6 +37,7 @@ #include "MIMETypeRegistry.h" #include "Page.h" #include "ScriptController.h" +#include "ScriptRunner.h" #include "ScriptSourceCode.h" #include "ScriptValue.h" #include "Settings.h" @@ -61,6 +62,8 @@ ScriptElement::ScriptElement(Element* element, bool parserInserted, bool already , m_willBeParserExecuted(false) , m_readyToBeParserExecuted(false) , m_willExecuteWhenDocumentFinishedParsing(false) + , m_forceAsync(!parserInserted) + , m_willExecuteInOrder(false) { ASSERT(m_element); } @@ -96,6 +99,11 @@ void ScriptElement::handleSourceAttribute(const String& sourceUrl) prepareScript(); // FIXME: Provide a real starting line number here. } +void ScriptElement::handleAsyncAttribute() +{ + m_forceAsync = false; +} + // Helper function static bool isLegacySupportedJavaScriptLanguage(const String& language) { @@ -159,7 +167,8 @@ bool ScriptElement::prepareScript(const TextPosition1& scriptStartPosition, Lega } else wasParserInserted = false; - // FIXME: HTML5 spec says we should set forceAsync. + if (wasParserInserted && !asyncAttributeValue()) + m_forceAsync = true; // FIXME: HTML5 spec says we should check that all children are either comments or empty text nodes. if (!hasSourceAttribute() && !m_element->firstChild()) @@ -171,8 +180,10 @@ bool ScriptElement::prepareScript(const TextPosition1& scriptStartPosition, Lega if (!isScriptTypeSupported(supportLegacyTypes)) return false; - if (wasParserInserted) + if (wasParserInserted) { m_parserInserted = true; + m_forceAsync = false; + } m_alreadyStarted = true; @@ -207,6 +218,10 @@ bool ScriptElement::prepareScript(const TextPosition1& scriptStartPosition, Lega else if (!hasSourceAttribute() && m_parserInserted && !m_element->document()->haveStylesheetsLoaded()) { m_willBeParserExecuted = true; m_readyToBeParserExecuted = true; + } else if (hasSourceAttribute() && !asyncAttributeValue() && !m_forceAsync) { + m_willExecuteInOrder = true; + m_element->document()->scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::IN_ORDER_EXECUTION); + m_cachedScript->addClient(this); } else if (hasSourceAttribute()) m_cachedScript->addClient(this); else @@ -217,9 +232,6 @@ bool ScriptElement::prepareScript(const TextPosition1& scriptStartPosition, Lega bool ScriptElement::requestScript(const String& sourceUrl) { - if (!m_element->document()->contentSecurityPolicy()->canLoadExternalScriptFromSrc(sourceUrl)) - return false; - RefPtr<Document> originalDocument = m_element->document(); if (!m_element->dispatchBeforeLoadEvent(sourceUrl)) return false; @@ -286,7 +298,10 @@ void ScriptElement::notifyFinished(CachedResource* o) { ASSERT(!m_willBeParserExecuted); ASSERT_UNUSED(o, o == m_cachedScript); - m_element->document()->asyncScriptRunner()->executeScriptSoon(this, m_cachedScript); + if (m_willExecuteInOrder) + m_element->document()->scriptRunner()->notifyInOrderScriptReady(); + else + m_element->document()->scriptRunner()->queueScriptForExecution(this, m_cachedScript, ScriptRunner::ASYNC_EXECUTION); m_cachedScript = 0; } diff --git a/Source/WebCore/dom/ScriptElement.h b/Source/WebCore/dom/ScriptElement.h index 79dff33..9ce66e5 100644 --- a/Source/WebCore/dom/ScriptElement.h +++ b/Source/WebCore/dom/ScriptElement.h @@ -63,12 +63,14 @@ protected: void setHaveFiredLoadEvent(bool haveFiredLoad) { m_haveFiredLoad = haveFiredLoad; } bool isParserInserted() const { return m_parserInserted; } bool alreadyStarted() const { return m_alreadyStarted; } + bool forceAsync() const { return m_forceAsync; } // Helper functions used by our parent classes. void insertedIntoDocument(); void removedFromDocument(); void childrenChanged(); void handleSourceAttribute(const String& sourceUrl); + void handleAsyncAttribute(); private: bool ignoresLoadRequest() const; @@ -98,6 +100,8 @@ private: bool m_willBeParserExecuted : 1; // Same as "The parser will handle executing the script." bool m_readyToBeParserExecuted : 1; bool m_willExecuteWhenDocumentFinishedParsing : 1; + bool m_forceAsync : 1; + bool m_willExecuteInOrder : 1; String m_characterEncoding; String m_fallbackCharacterEncoding; }; diff --git a/Source/WebCore/dom/ScriptExecutionContext.cpp b/Source/WebCore/dom/ScriptExecutionContext.cpp index 6685416..b9a1e92 100644 --- a/Source/WebCore/dom/ScriptExecutionContext.cpp +++ b/Source/WebCore/dom/ScriptExecutionContext.cpp @@ -382,6 +382,8 @@ KURL ScriptExecutionContext::createPublicBlobURL(Blob* blob) if (!blob) return KURL(); KURL publicURL = BlobURL::createPublicURL(securityOrigin()); + if (publicURL.isEmpty()) + return KURL(); ThreadableBlobRegistry::registerBlobURL(publicURL, blob->url()); m_publicBlobURLs.add(publicURL.string()); return publicURL; diff --git a/Source/WebCore/dom/AsyncScriptRunner.cpp b/Source/WebCore/dom/ScriptRunner.cpp index 28b1b31..10198bf 100644 --- a/Source/WebCore/dom/AsyncScriptRunner.cpp +++ b/Source/WebCore/dom/ScriptRunner.cpp @@ -24,7 +24,7 @@ */ #include "config.h" -#include "AsyncScriptRunner.h" +#include "ScriptRunner.h" #include "CachedScript.h" #include "Document.h" @@ -34,52 +34,79 @@ namespace WebCore { -AsyncScriptRunner::AsyncScriptRunner(Document* document) +ScriptRunner::ScriptRunner(Document* document) : m_document(document) - , m_timer(this, &AsyncScriptRunner::timerFired) + , m_timer(this, &ScriptRunner::timerFired) { ASSERT(document); } -AsyncScriptRunner::~AsyncScriptRunner() +ScriptRunner::~ScriptRunner() { for (size_t i = 0; i < m_scriptsToExecuteSoon.size(); ++i) m_document->decrementLoadEventDelayCount(); + for (size_t i = 0; i < m_scriptsToExecuteInOrder.size(); ++i) + m_document->decrementLoadEventDelayCount(); } -void AsyncScriptRunner::executeScriptSoon(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript) +void ScriptRunner::queueScriptForExecution(ScriptElement* scriptElement, CachedResourceHandle<CachedScript> cachedScript, ExecutionType executionType) { - ASSERT_ARG(scriptElement, scriptElement); + ASSERT(scriptElement); Element* element = scriptElement->element(); ASSERT(element); ASSERT(element->inDocument()); m_document->incrementLoadEventDelayCount(); - m_scriptsToExecuteSoon.append(PendingScript(element, cachedScript.get())); - if (!m_timer.isActive()) - m_timer.startOneShot(0); + + switch (executionType) { + case ASYNC_EXECUTION: + m_scriptsToExecuteSoon.append(PendingScript(element, cachedScript.get())); + if (!m_timer.isActive()) + m_timer.startOneShot(0); + break; + + case IN_ORDER_EXECUTION: + m_scriptsToExecuteInOrder.append(PendingScript(element, cachedScript.get())); + break; + + default: + ASSERT_NOT_REACHED(); + } } -void AsyncScriptRunner::suspend() +void ScriptRunner::suspend() { m_timer.stop(); } -void AsyncScriptRunner::resume() +void ScriptRunner::resume() { if (hasPendingScripts()) m_timer.startOneShot(0); } -void AsyncScriptRunner::timerFired(Timer<AsyncScriptRunner>* timer) +void ScriptRunner::notifyInOrderScriptReady() +{ + ASSERT(!m_scriptsToExecuteInOrder.isEmpty()); + m_timer.startOneShot(0); +} + +void ScriptRunner::timerFired(Timer<ScriptRunner>* timer) { ASSERT_UNUSED(timer, timer == &m_timer); RefPtr<Document> protect(m_document); - + Vector<PendingScript> scripts; scripts.swap(m_scriptsToExecuteSoon); + + size_t numInOrderScriptsToExecute = 0; + for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].cachedScript()->isLoaded(); ++numInOrderScriptsToExecute) + scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]); + if (numInOrderScriptsToExecute) + m_scriptsToExecuteInOrder.remove(0, numInOrderScriptsToExecute); + size_t size = scripts.size(); for (size_t i = 0; i < size; ++i) { CachedScript* cachedScript = scripts[i].cachedScript(); diff --git a/Source/WebCore/dom/AsyncScriptRunner.h b/Source/WebCore/dom/ScriptRunner.h index 6a75323..d6d7411 100644 --- a/Source/WebCore/dom/AsyncScriptRunner.h +++ b/Source/WebCore/dom/ScriptRunner.h @@ -23,8 +23,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AsyncScriptRunner_h -#define AsyncScriptRunner_h +#ifndef ScriptRunner_h +#define ScriptRunner_h #include "CachedResourceHandle.h" #include "Timer.h" @@ -38,26 +38,29 @@ class CachedScript; class Document; class PendingScript; class ScriptElement; - -class AsyncScriptRunner { - WTF_MAKE_NONCOPYABLE(AsyncScriptRunner); WTF_MAKE_FAST_ALLOCATED; + +class ScriptRunner { + WTF_MAKE_NONCOPYABLE(ScriptRunner); WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<AsyncScriptRunner> create(Document* document) { return new AsyncScriptRunner(document); } - ~AsyncScriptRunner(); + static PassOwnPtr<ScriptRunner> create(Document* document) { return new ScriptRunner(document); } + ~ScriptRunner(); - void executeScriptSoon(ScriptElement*, CachedResourceHandle<CachedScript>); - bool hasPendingScripts() const { return !m_scriptsToExecuteSoon.isEmpty(); } + enum ExecutionType { ASYNC_EXECUTION, IN_ORDER_EXECUTION }; + void queueScriptForExecution(ScriptElement*, CachedResourceHandle<CachedScript>, ExecutionType); + bool hasPendingScripts() const { return !m_scriptsToExecuteSoon.isEmpty() || !m_scriptsToExecuteInOrder.isEmpty(); } void suspend(); void resume(); + void notifyInOrderScriptReady(); private: - AsyncScriptRunner(Document*); + ScriptRunner(Document*); - void timerFired(Timer<AsyncScriptRunner>*); + void timerFired(Timer<ScriptRunner>*); Document* m_document; + Vector<PendingScript> m_scriptsToExecuteInOrder; Vector<PendingScript> m_scriptsToExecuteSoon; // http://www.whatwg.org/specs/web-apps/current-work/#set-of-scripts-that-will-execute-as-soon-as-possible - Timer<AsyncScriptRunner> m_timer; + Timer<ScriptRunner> m_timer; }; } diff --git a/Source/WebCore/dom/StringCallback.cpp b/Source/WebCore/dom/StringCallback.cpp new file mode 100644 index 0000000..c6285b1 --- /dev/null +++ b/Source/WebCore/dom/StringCallback.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StringCallback.h" + +#include "ScriptExecutionContext.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +namespace { + +class DispatchCallbackTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<DispatchCallbackTask> create(PassRefPtr<StringCallback> callback, const String& data) + { + return adoptPtr(new DispatchCallbackTask(callback, data)); + } + + virtual void performTask(ScriptExecutionContext*) + { + m_callback->handleEvent(m_data); + } + +private: + DispatchCallbackTask(PassRefPtr<StringCallback> callback, const String& data) + : m_callback(callback) + , m_data(data) + { + } + + RefPtr<StringCallback> m_callback; + const String m_data; +}; + +} // namespace + +void StringCallback::scheduleCallback(ScriptExecutionContext* context, const String& data) +{ + context->postTask(DispatchCallbackTask::create(this, data)); +} + +} // namespace WebCore diff --git a/Source/WebCore/dom/StringCallback.h b/Source/WebCore/dom/StringCallback.h new file mode 100644 index 0000000..92e83e2 --- /dev/null +++ b/Source/WebCore/dom/StringCallback.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef StringCallback_h +#define StringCallback_h + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class ScriptExecutionContext; + +class StringCallback : public RefCounted<StringCallback> { +public: + virtual ~StringCallback() { } + virtual bool handleEvent(const String& data) = 0; + + // Helper to post callback task. + void scheduleCallback(ScriptExecutionContext*, const String& data); +}; + +} // namespace WebCore + +#endif // StringCallback_h diff --git a/Source/WebCore/dom/StringCallback.idl b/Source/WebCore/dom/StringCallback.idl new file mode 100644 index 0000000..1e18d83 --- /dev/null +++ b/Source/WebCore/dom/StringCallback.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module core { + interface [ + Callback + ] StringCallback { + boolean handleEvent(in DOMString data); + }; +} diff --git a/Source/WebCore/dom/StyleElement.cpp b/Source/WebCore/dom/StyleElement.cpp index f984fda..e9d035d 100644 --- a/Source/WebCore/dom/StyleElement.cpp +++ b/Source/WebCore/dom/StyleElement.cpp @@ -103,8 +103,15 @@ void StyleElement::process(Element* e) for (Node* c = e->firstChild(); c; c = c->nextSibling()) { if (isValidStyleChild(c)) { unsigned length = c->nodeValue().length(); +<<<<<<< HEAD if (length > std::numeric_limits<unsigned>::max() - resultLength) CRASH(); +======= + if (length > std::numeric_limits<unsigned>::max() - resultLength) { + createSheet(e, m_startLineNumber, ""); + return; + } +>>>>>>> webkit.org at r82507 resultLength += length; } } diff --git a/Source/WebCore/dom/StyledElement.cpp b/Source/WebCore/dom/StyledElement.cpp index d6a532f..3c55591 100644 --- a/Source/WebCore/dom/StyledElement.cpp +++ b/Source/WebCore/dom/StyledElement.cpp @@ -412,7 +412,7 @@ unsigned MappedAttributeHash::hash(const MappedAttributeKey& key) COMPILE_ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8, key_name_size); COMPILE_ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8, key_value_size); - WTF::StringHasher hasher; + StringHasher hasher; const UChar* data; data = reinterpret_cast<const UChar*>(&key.name); diff --git a/Source/WebCore/dom/UIEvent.cpp b/Source/WebCore/dom/UIEvent.cpp index 401015b..dfdd34d 100644 --- a/Source/WebCore/dom/UIEvent.cpp +++ b/Source/WebCore/dom/UIEvent.cpp @@ -69,12 +69,12 @@ int UIEvent::charCode() const return 0; } -int UIEvent::layerX() const +int UIEvent::layerX() { return 0; } -int UIEvent::layerY() const +int UIEvent::layerY() { return 0; } diff --git a/Source/WebCore/dom/UIEvent.h b/Source/WebCore/dom/UIEvent.h index 8e330a5..a2f4e65 100644 --- a/Source/WebCore/dom/UIEvent.h +++ b/Source/WebCore/dom/UIEvent.h @@ -53,8 +53,8 @@ namespace WebCore { virtual int keyCode() const; virtual int charCode() const; - virtual int layerX() const; - virtual int layerY() const; + virtual int layerX(); + virtual int layerY(); virtual int pageX() const; virtual int pageY() const; diff --git a/Source/WebCore/dom/ViewportArguments.cpp b/Source/WebCore/dom/ViewportArguments.cpp index e75a3eb..49c94d7 100644 --- a/Source/WebCore/dom/ViewportArguments.cpp +++ b/Source/WebCore/dom/ViewportArguments.cpp @@ -257,32 +257,32 @@ static float findScaleValue(const String& keyString, const String& valueString, return value; } -static bool findUserScalableValue(const String& keyString, const String& valueString, Document* document) +static float findUserScalableValue(const String& keyString, const String& valueString, Document* document) { // yes and no are used as keywords. // Numbers >= 1, numbers <= -1, device-width and device-height are mapped to yes. // Numbers in the range <-1, 1>, and unknown values, are mapped to no. if (equalIgnoringCase(valueString, "yes")) - return true; + return 1; if (equalIgnoringCase(valueString, "no")) - return false; + return 0; if (equalIgnoringCase(valueString, "desktop-width")) - return true; + return 1; if (equalIgnoringCase(valueString, "device-width")) - return true; + return 1; if (equalIgnoringCase(valueString, "device-height")) - return true; + return 1; bool ok; float value = numericPrefix(keyString, valueString, document, &ok); if (!ok) - return false; + return 0; if (fabs(value) < 1) - return false; + return 0; - return true; + return 1; } static float findTargetDensityDPIValue(const String& keyString, const String& valueString, Document* document) diff --git a/Source/WebCore/dom/ViewportArguments.h b/Source/WebCore/dom/ViewportArguments.h index 2e0fd19..66bb281 100644 --- a/Source/WebCore/dom/ViewportArguments.h +++ b/Source/WebCore/dom/ViewportArguments.h @@ -51,7 +51,7 @@ struct ViewportAttributes { float minimumScale; float maximumScale; - bool userScalable; + float userScalable; }; struct ViewportArguments { @@ -74,7 +74,7 @@ struct ViewportArguments { , width(ValueAuto) , height(ValueAuto) , targetDensityDpi(ValueAuto) - , userScalable(true) + , userScalable(ValueAuto) { } @@ -84,8 +84,7 @@ struct ViewportArguments { float width; float height; float targetDensityDpi; - - bool userScalable; + float userScalable; bool operator==(const ViewportArguments& other) const { diff --git a/Source/WebCore/dom/default/PlatformMessagePortChannel.h b/Source/WebCore/dom/default/PlatformMessagePortChannel.h index 2aad952..651810b 100644 --- a/Source/WebCore/dom/default/PlatformMessagePortChannel.h +++ b/Source/WebCore/dom/default/PlatformMessagePortChannel.h @@ -44,7 +44,7 @@ namespace WebCore { // PlatformMessagePortChannel is a platform-dependent interface to the remote side of a message channel. // This default implementation supports multiple threads running within a single process. Implementations for multi-process platforms should define these public APIs in their own platform-specific PlatformMessagePortChannel file. // The goal of this implementation is to eliminate contention except when cloning or closing the port, so each side of the channel has its own separate mutex. - class PlatformMessagePortChannel : public ThreadSafeShared<PlatformMessagePortChannel> { + class PlatformMessagePortChannel : public ThreadSafeRefCounted<PlatformMessagePortChannel> { public: static void createChannel(PassRefPtr<MessagePort>, PassRefPtr<MessagePort>); @@ -59,7 +59,7 @@ namespace WebCore { MessagePort* locallyEntangledPort(const ScriptExecutionContext*); // Wrapper for MessageQueue that allows us to do thread safe sharing by two proxies. - class MessagePortQueue : public ThreadSafeShared<MessagePortQueue> { + class MessagePortQueue : public ThreadSafeRefCounted<MessagePortQueue> { public: static PassRefPtr<MessagePortQueue> create() { return adoptRef(new MessagePortQueue()); } |