diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:05:15 -0800 |
commit | 1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch) | |
tree | 4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/page | |
parent | 9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff) | |
download | external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2 |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/page')
282 files changed, 19626 insertions, 16951 deletions
diff --git a/WebCore/page/AXObjectCache.cpp b/WebCore/page/AXObjectCache.cpp new file mode 100644 index 0000000..d9c4c9a --- /dev/null +++ b/WebCore/page/AXObjectCache.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AXObjectCache.h" + +#include "AccessibilityList.h" +#include "AccessibilityListBox.h" +#include "AccessibilityListBoxOption.h" +#include "AccessibilityImageMapLink.h" +#include "AccessibilityRenderObject.h" +#include "AccessibilityTable.h" +#include "AccessibilityTableCell.h" +#include "AccessibilityTableColumn.h" +#include "AccessibilityTableHeaderContainer.h" +#include "AccessibilityTableRow.h" +#include "HTMLNames.h" +#include "RenderObject.h" + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +bool AXObjectCache::gAccessibilityEnabled = false; +bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false; + +AXObjectCache::~AXObjectCache() +{ + HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end(); + for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) { + AccessibilityObject* obj = (*it).second.get(); + detachWrapper(obj); + obj->detach(); + } +} + +AccessibilityObject* AXObjectCache::get(RenderObject* renderer) +{ + if (!renderer) + return 0; + + RefPtr<AccessibilityObject> obj = 0; + AXID axID = m_renderObjectMapping.get(renderer); + ASSERT(!HashTraits<AXID>::isDeletedValue(axID)); + + if (axID) + obj = m_objects.get(axID).get(); + + Node* element = renderer->element(); + if (!obj) { + if (renderer->isListBox()) + obj = AccessibilityListBox::create(renderer); + else if (element && (element->hasTagName(ulTag) || element->hasTagName(olTag) || element->hasTagName(dlTag))) + obj = AccessibilityList::create(renderer); + else if (renderer->isTable()) + obj = AccessibilityTable::create(renderer); + else if (renderer->isTableRow()) + obj = AccessibilityTableRow::create(renderer); + else if (renderer->isTableCell()) + obj = AccessibilityTableCell::create(renderer); + else + obj = AccessibilityRenderObject::create(renderer); + + getAXID(obj.get()); + + m_renderObjectMapping.set(renderer, obj.get()->axObjectID()); + m_objects.set(obj.get()->axObjectID(), obj); + attachWrapper(obj.get()); + } + + return obj.get(); +} + +AccessibilityObject* AXObjectCache::get(AccessibilityRole role) +{ + RefPtr<AccessibilityObject> obj = 0; + + // will be filled in... + switch (role) { + case ListBoxOptionRole: + obj = AccessibilityListBoxOption::create(); + break; + case ImageMapLinkRole: + obj = AccessibilityImageMapLink::create(); + break; + case ColumnRole: + obj = AccessibilityTableColumn::create(); + break; + case TableHeaderContainerRole: + obj = AccessibilityTableHeaderContainer::create(); + break; + default: + obj = 0; + } + + if (obj) + getAXID(obj.get()); + else + return 0; + + m_objects.set(obj->axObjectID(), obj); + attachWrapper(obj.get()); + return obj.get(); +} + +void AXObjectCache::remove(AXID axID) +{ + if (!axID) + return; + + // first fetch object to operate some cleanup functions on it + AccessibilityObject* obj = m_objects.get(axID).get(); + if (!obj) + return; + + detachWrapper(obj); + obj->detach(); + removeAXID(obj); + + // finally remove the object + if (!m_objects.take(axID)) { + return; + } + + ASSERT(m_objects.size() >= m_idsInUse.size()); +} + +void AXObjectCache::remove(RenderObject* renderer) +{ + if (!renderer) + return; + + AXID axID = m_renderObjectMapping.get(renderer); + remove(axID); + m_renderObjectMapping.remove(renderer); +} + +AXID AXObjectCache::getAXID(AccessibilityObject* obj) +{ + // check for already-assigned ID + AXID objID = obj->axObjectID(); + if (objID) { + ASSERT(m_idsInUse.contains(objID)); + return objID; + } + + // generate a new ID + static AXID lastUsedID = 0; + objID = lastUsedID; + do + ++objID; + while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID)); + m_idsInUse.add(objID); + lastUsedID = objID; + obj->setAXObjectID(objID); + + return objID; +} + +void AXObjectCache::removeAXID(AccessibilityObject* obj) +{ + AXID objID = obj->axObjectID(); + if (objID == 0) + return; + ASSERT(!HashTraits<AXID>::isDeletedValue(objID)); + ASSERT(m_idsInUse.contains(objID)); + obj->setAXObjectID(0); + m_idsInUse.remove(objID); +} + +void AXObjectCache::childrenChanged(RenderObject* renderer) +{ + if (!renderer) + return; + + AXID axID = m_renderObjectMapping.get(renderer); + if (!axID) + return; + + AccessibilityObject* obj = m_objects.get(axID).get(); + if (obj) + obj->childrenChanged(); +} + +#if HAVE(ACCESSIBILITY) +void AXObjectCache::selectedChildrenChanged(RenderObject* renderer) +{ + postNotificationToElement(renderer, "AXSelectedChildrenChanged"); +} +#endif + +#if HAVE(ACCESSIBILITY) +void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer) +{ + if (!renderer) + return; + AccessibilityObject* obj = get(renderer); + if (obj) + obj->handleActiveDescendantChanged(); +} + +void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer) +{ + if (!renderer) + return; + AccessibilityObject* obj = get(renderer); + if (obj && obj->isAccessibilityRenderObject()) + static_cast<AccessibilityRenderObject*>(obj)->setAriaRole(); +} +#endif + +} // namespace WebCore diff --git a/WebCore/page/AXObjectCache.h b/WebCore/page/AXObjectCache.h new file mode 100644 index 0000000..5e95f74 --- /dev/null +++ b/WebCore/page/AXObjectCache.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2003, 2006, 2007, 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 AXObjectCache_h +#define AXObjectCache_h + +#include "AccessibilityObject.h" +#include <limits.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> + +#ifdef __OBJC__ +@class WebCoreTextMarker; +#else +class WebCoreTextMarker; +#endif + +namespace WebCore { + + class RenderObject; + class String; + class VisiblePosition; + class AccessibilityObject; + class Node; + + typedef unsigned AXID; + + struct TextMarkerData { + AXID axID; + Node* node; + int offset; + EAffinity affinity; + }; + + class AXObjectCache { + public: + ~AXObjectCache(); + + // to be used with render objects + AccessibilityObject* get(RenderObject*); + + // used for objects without backing elements + AccessibilityObject* get(AccessibilityRole); + + void remove(RenderObject*); + void remove(AXID); + + void detachWrapper(AccessibilityObject*); + void attachWrapper(AccessibilityObject*); + void postNotification(RenderObject*, const String&); + void postNotificationToElement(RenderObject*, const String&); + void childrenChanged(RenderObject*); + void selectedChildrenChanged(RenderObject*); + void handleActiveDescendantChanged(RenderObject*); + void handleAriaRoleChanged(RenderObject*); + void handleFocusedUIElementChanged(); + static void enableAccessibility() { gAccessibilityEnabled = true; } + static void enableEnhancedUserInterfaceAccessibility() { gAccessibilityEnhancedUserInterfaceEnabled = true; } + + static bool accessibilityEnabled() { return gAccessibilityEnabled; } + static bool accessibilityEnhancedUserInterfaceEnabled() { return gAccessibilityEnhancedUserInterfaceEnabled; } + + void removeAXID(AccessibilityObject*); + bool isIDinUse(AXID id) const { return m_idsInUse.contains(id); } + + private: + HashMap<AXID, RefPtr<AccessibilityObject> > m_objects; + HashMap<RenderObject*, AXID> m_renderObjectMapping; + static bool gAccessibilityEnabled; + static bool gAccessibilityEnhancedUserInterfaceEnabled; + + HashSet<AXID> m_idsInUse; + + AXID getAXID(AccessibilityObject*); + }; + +#if !HAVE(ACCESSIBILITY) + inline void AXObjectCache::handleActiveDescendantChanged(RenderObject*) { } + inline void AXObjectCache::handleAriaRoleChanged(RenderObject*) { } + inline void AXObjectCache::handleFocusedUIElementChanged() { } + inline void AXObjectCache::detachWrapper(AccessibilityObject*) { } + inline void AXObjectCache::attachWrapper(AccessibilityObject*) { } + inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { } + inline void AXObjectCache::postNotification(RenderObject*, const String&) { } + inline void AXObjectCache::postNotificationToElement(RenderObject*, const String&) { } +#endif + +} + +#endif diff --git a/WebCore/page/AbstractView.idl b/WebCore/page/AbstractView.idl index ca02c82..5b7ce89 100644 --- a/WebCore/page/AbstractView.idl +++ b/WebCore/page/AbstractView.idl @@ -27,7 +27,9 @@ module views { // Introduced in DOM Level 2: - interface AbstractView { + interface [ + ObjCCustomImplementation + ] AbstractView { readonly attribute Document document; }; diff --git a/WebCore/page/AccessibilityImageMapLink.cpp b/WebCore/page/AccessibilityImageMapLink.cpp new file mode 100644 index 0000000..5557446 --- /dev/null +++ b/WebCore/page/AccessibilityImageMapLink.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityImageMapLink.h" + +#include "AccessibilityRenderObject.h" +#include "AXObjectCache.h" +#include "Document.h" +#include "HTMLNames.h" +#include "IntRect.h" +#include "RenderObject.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityImageMapLink::AccessibilityImageMapLink() + : m_areaElement(0), + m_mapElement(0) +{ +} + +AccessibilityImageMapLink::~AccessibilityImageMapLink() +{ +} + +PassRefPtr<AccessibilityImageMapLink> AccessibilityImageMapLink::create() +{ + return adoptRef(new AccessibilityImageMapLink()); +} + +AccessibilityObject* AccessibilityImageMapLink::parentObject() const +{ + if (m_parent) + return m_parent; + + if (!m_mapElement || !m_mapElement->renderer()) + return 0; + + return m_mapElement->document()->axObjectCache()->get(m_mapElement->renderer()); +} + +Element* AccessibilityImageMapLink::actionElement() const +{ + return anchorElement(); +} + +Element* AccessibilityImageMapLink::anchorElement() const +{ + return m_areaElement; +} + +String AccessibilityImageMapLink::accessibilityDescription() const +{ + if (!m_areaElement) + return String(); + + const AtomicString& alt = m_areaElement->getAttribute(altAttr); + if (!alt.isEmpty()) + return alt; + + return String(); +} + +String AccessibilityImageMapLink::title() const +{ + if (!m_areaElement) + return String(); + + const AtomicString& title = m_areaElement->getAttribute(titleAttr); + if (!title.isEmpty()) + return title; + const AtomicString& summary = m_areaElement->getAttribute(summaryAttr); + if (!summary.isEmpty()) + return summary; + + return String(); +} + +IntRect AccessibilityImageMapLink::elementRect() const +{ + if (!m_mapElement || !m_areaElement) + return IntRect(); + + RenderObject* renderer; + if (m_parent && m_parent->isAccessibilityRenderObject()) + renderer = static_cast<AccessibilityRenderObject*>(m_parent)->renderer(); + else + renderer = m_mapElement->renderer(); + + if (!renderer) + return IntRect(); + + return m_areaElement->getRect(renderer); +} + +IntSize AccessibilityImageMapLink::size() const +{ + return elementRect().size(); +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityImageMapLink.h b/WebCore/page/AccessibilityImageMapLink.h new file mode 100644 index 0000000..7fc8d3c --- /dev/null +++ b/WebCore/page/AccessibilityImageMapLink.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityImageMapLink_h +#define AccessibilityImageMapLink_h + +#include "AccessibilityObject.h" +#include "HTMLAreaElement.h" +#include "HTMLMapElement.h" + +namespace WebCore { + +class AccessibilityImageMapLink : public AccessibilityObject { + +private: + AccessibilityImageMapLink(); +public: + static PassRefPtr<AccessibilityImageMapLink> create(); + virtual ~AccessibilityImageMapLink(); + + void setHTMLAreaElement(HTMLAreaElement* element) { m_areaElement = element; } + void setHTMLMapElement(HTMLMapElement* element) { m_mapElement = element; } + void setParent(AccessibilityObject* parent) { m_parent = parent; } + + virtual AccessibilityRole roleValue() const { return WebCoreLinkRole; } + virtual bool accessibilityIsIgnored() const { return false; } + + virtual AccessibilityObject* parentObject() const; + virtual Element* anchorElement() const; + virtual Element* actionElement() const; + + virtual bool isLink() const { return true; } + virtual String title() const; + virtual String accessibilityDescription() const; + + virtual IntSize size() const; + virtual IntRect elementRect() const; + +private: + HTMLAreaElement* m_areaElement; + HTMLMapElement* m_mapElement; + AccessibilityObject* m_parent; +}; + +} // namespace WebCore + +#endif // AccessibilityImageMapLink_h diff --git a/WebCore/page/AccessibilityList.cpp b/WebCore/page/AccessibilityList.cpp new file mode 100644 index 0000000..ad71ff4 --- /dev/null +++ b/WebCore/page/AccessibilityList.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityList.h" + +#include "AXObjectCache.h" +#include "HTMLNames.h" +#include "RenderObject.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityList::AccessibilityList(RenderObject* renderer) + : AccessibilityRenderObject(renderer) +{ +} + +AccessibilityList::~AccessibilityList() +{ +} + +PassRefPtr<AccessibilityList> AccessibilityList::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityList(renderer)); +} + +bool AccessibilityList::accessibilityIsIgnored() const +{ + // lists don't appear on tiger/leopard on the mac +#if PLATFORM(MAC) && (defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)) + return true; +#else + return false; +#endif +} + +bool AccessibilityList::isUnorderedList() const +{ + if (!m_renderer) + return false; + + Node* element = m_renderer->element(); + return element && element->hasTagName(ulTag); +} + +bool AccessibilityList::isOrderedList() const +{ + if (!m_renderer) + return false; + + Node* element = m_renderer->element(); + return element && element->hasTagName(olTag); +} + +bool AccessibilityList::isDefinitionList() const +{ + if (!m_renderer) + return false; + + Node* element = m_renderer->element(); + return element && element->hasTagName(dlTag); +} + + +} // namespace WebCore diff --git a/WebCore/page/inspector/ResourcePanel.js b/WebCore/page/AccessibilityList.h index b165c2b..315ccac 100644 --- a/WebCore/page/inspector/ResourcePanel.js +++ b/WebCore/page/AccessibilityList.h @@ -1,18 +1,18 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Apple 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: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * 2. 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. + * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -26,25 +26,31 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -WebInspector.ResourcePanel = function(resource, views) -{ - WebInspector.Panel.call(this, views); - this.resource = resource; -} +#ifndef AccessibilityList_h +#define AccessibilityList_h -WebInspector.ResourcePanel.prototype = { - show: function() - { - WebInspector.Panel.prototype.show.call(this); - this.resource.listItem.select(true); // passing true prevents a cycle - this.resource.listItem.reveal(); - }, +#include "AccessibilityRenderObject.h" - hide: function() - { - this.resource.listItem.deselect(true); // passing true prevents a cycle - WebInspector.Panel.prototype.hide.call(this); - } -} +namespace WebCore { + +class AccessibilityList : public AccessibilityRenderObject { + +private: + AccessibilityList(RenderObject*); +public: + static PassRefPtr<AccessibilityList> create(RenderObject*); + virtual ~AccessibilityList(); + + virtual bool isList() const { return true; }; + bool isUnorderedList() const; + bool isOrderedList() const; + bool isDefinitionList() const; -WebInspector.ResourcePanel.prototype.__proto__ = WebInspector.Panel.prototype; + virtual AccessibilityRole roleValue() const { return ListRole; } + virtual bool accessibilityIsIgnored() const; + +}; + +} // namespace WebCore + +#endif // AccessibilityList_h diff --git a/WebCore/page/AccessibilityListBox.cpp b/WebCore/page/AccessibilityListBox.cpp new file mode 100644 index 0000000..912351e --- /dev/null +++ b/WebCore/page/AccessibilityListBox.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityListBox.h" + +#include "AXObjectCache.h" +#include "AccessibilityListBoxOption.h" +#include "HitTestResult.h" +#include "HTMLNames.h" +#include "HTMLSelectElement.h" +#include "RenderListBox.h" +#include "RenderObject.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityListBox::AccessibilityListBox(RenderObject* renderer) + : AccessibilityRenderObject(renderer) +{ +} + +AccessibilityListBox::~AccessibilityListBox() +{ +} + +PassRefPtr<AccessibilityListBox> AccessibilityListBox::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityListBox(renderer)); +} + +bool AccessibilityListBox::canSetSelectedChildrenAttribute() const +{ + Node* selectNode = m_renderer->node(); + if (!selectNode) + return false; + + return !static_cast<HTMLSelectElement*>(selectNode)->disabled(); +} + +void AccessibilityListBox::addChildren() +{ + Node* selectNode = m_renderer->node(); + if (!selectNode) + return; + + m_haveChildren = true; + + const Vector<HTMLElement*>& listItems = static_cast<HTMLSelectElement*>(selectNode)->listItems(); + unsigned length = listItems.size(); + for (unsigned i = 0; i < length; i++) { + AccessibilityObject* listOption = listBoxOptionAccessibilityObject(listItems[i]); + if (listOption) + m_children.append(listOption); + } +} + +void AccessibilityListBox::setSelectedChildren(AccessibilityChildrenVector& children) +{ + if (!canSetSelectedChildrenAttribute()) + return; + + Node* selectNode = m_renderer->node(); + if (!selectNode) + return; + + // disable any selected options + unsigned length = m_children.size(); + for (unsigned i = 0; i < length; i++) { + AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(m_children[i].get()); + if (listBoxOption->isSelected()) + listBoxOption->setSelected(false); + } + + length = children.size(); + for (unsigned i = 0; i < length; i++) { + AccessibilityObject* obj = children[i].get(); + if (obj->roleValue() != ListBoxOptionRole) + continue; + + static_cast<AccessibilityListBoxOption*>(obj)->setSelected(true); + } +} + +void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result) +{ + ASSERT(result.isEmpty()); + + if (!hasChildren()) + addChildren(); + + unsigned length = m_children.size(); + for (unsigned i = 0; i < length; i++) { + if (static_cast<AccessibilityListBoxOption*>(m_children[i].get())->isSelected()) + result.append(m_children[i]); + } +} + +void AccessibilityListBox::visibleChildren(AccessibilityChildrenVector& result) +{ + ASSERT(result.isEmpty()); + + if (!hasChildren()) + addChildren(); + + unsigned length = m_children.size(); + for (unsigned i = 0; i < length; i++) { + if (static_cast<RenderListBox*>(m_renderer)->listIndexIsVisible(i)) + result.append(m_children[i]); + } +} + +AccessibilityObject* AccessibilityListBox::listBoxOptionAccessibilityObject(HTMLElement* element) const +{ + // skip hr elements + if (!element || element->hasTagName(hrTag)) + return 0; + + AccessibilityObject* listBoxObject = m_renderer->document()->axObjectCache()->get(ListBoxOptionRole); + static_cast<AccessibilityListBoxOption*>(listBoxObject)->setHTMLElement(element); + + return listBoxObject; +} + +AccessibilityObject* AccessibilityListBox::doAccessibilityHitTest(const IntPoint& point) +{ + // the internal HTMLSelectElement methods for returning a listbox option at a point + // ignore optgroup elements. + if (!m_renderer) + return 0; + + Node *element = m_renderer->element(); + if (!element) + return 0; + + if (!hasChildren()) + addChildren(); + + IntRect parentRect = boundingBoxRect(); + + unsigned length = m_children.size(); + for (unsigned i = 0; i < length; i++) { + IntRect rect = static_cast<RenderListBox*>(m_renderer)->itemBoundingBoxRect(parentRect.x(), parentRect.y(), i); + if (rect.contains(point)) + return m_children[i].get(); + } + + return this; +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityListBox.h b/WebCore/page/AccessibilityListBox.h new file mode 100644 index 0000000..f95a921 --- /dev/null +++ b/WebCore/page/AccessibilityListBox.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityListBox_h +#define AccessibilityListBox_h + +#include "AccessibilityObject.h" +#include "AccessibilityRenderObject.h" + +namespace WebCore { + +class AccessibilityListBox : public AccessibilityRenderObject { + +private: + AccessibilityListBox(RenderObject*); +public: + static PassRefPtr<AccessibilityListBox> create(RenderObject*); + virtual ~AccessibilityListBox(); + + virtual AccessibilityObject* doAccessibilityHitTest(const IntPoint&); + virtual bool isListBox() const { return true; }; + + virtual bool canSetFocusAttribute() const { return true; } + virtual bool canSetSelectedChildrenAttribute() const; + void setSelectedChildren(AccessibilityChildrenVector&); + virtual AccessibilityRole roleValue() const { return ListBoxRole; } + + virtual bool accessibilityIsIgnored() const { return false; } + + virtual void selectedChildren(AccessibilityChildrenVector&); + virtual void visibleChildren(AccessibilityChildrenVector&); + + virtual void addChildren(); + +private: + AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const; +}; + +} // namespace WebCore + +#endif // AccessibilityListBox_h diff --git a/WebCore/page/AccessibilityListBoxOption.cpp b/WebCore/page/AccessibilityListBoxOption.cpp new file mode 100644 index 0000000..fedfe91 --- /dev/null +++ b/WebCore/page/AccessibilityListBoxOption.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityListBoxOption.h" + +#include "AXObjectCache.h" +#include "AccessibilityListBox.h" +#include "Element.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "HTMLOptionElement.h" +#include "HTMLOptGroupElement.h" +#include "HTMLSelectElement.h" +#include "IntRect.h" +#include "RenderObject.h" +#include "RenderListBox.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityListBoxOption::AccessibilityListBoxOption() + : m_optionElement(0) +{ +} + +AccessibilityListBoxOption::~AccessibilityListBoxOption() +{ +} + +PassRefPtr<AccessibilityListBoxOption> AccessibilityListBoxOption::create() +{ + return adoptRef(new AccessibilityListBoxOption()); +} + +bool AccessibilityListBoxOption::isEnabled() const +{ + if (!m_optionElement) + return false; + + if (m_optionElement->hasTagName(optgroupTag)) + return false; + + return true; +} + +bool AccessibilityListBoxOption::isSelected() const +{ + if (!m_optionElement) + return false; + + if (!m_optionElement->hasTagName(optionTag)) + return false; + + return static_cast<HTMLOptionElement*>(m_optionElement)->selected(); +} + +IntRect AccessibilityListBoxOption::elementRect() const +{ + IntRect rect; + if (!m_optionElement) + return rect; + + HTMLSelectElement* listBoxParentNode = listBoxOptionParentNode(); + if (!listBoxParentNode) + return rect; + + RenderObject* listBoxRenderer = listBoxParentNode->renderer(); + if (!listBoxRenderer) + return rect; + + IntRect parentRect = listBoxRenderer->document()->axObjectCache()->get(listBoxRenderer)->boundingBoxRect(); + int index = listBoxOptionIndex(); + if (index != -1) + rect = static_cast<RenderListBox*>(listBoxRenderer)->itemBoundingBoxRect(parentRect.x(), parentRect.y(), index); + + return rect; +} + +bool AccessibilityListBoxOption::canSetSelectedAttribute() const +{ + if (!m_optionElement) + return false; + + if (!m_optionElement->hasTagName(optionTag)) + return false; + + if (m_optionElement->disabled()) + return false; + + HTMLSelectElement* selectElement = listBoxOptionParentNode(); + if (selectElement && selectElement->disabled()) + return false; + + return true; +} + +String AccessibilityListBoxOption::stringValue() const +{ + if (!m_optionElement) + return String(); + + if (m_optionElement->hasTagName(optionTag)) + return static_cast<HTMLOptionElement*>(m_optionElement)->text(); + + if (m_optionElement->hasTagName(optgroupTag)) + return static_cast<HTMLOptGroupElement*>(m_optionElement)->groupLabelText(); + + return String(); +} + +IntSize AccessibilityListBoxOption::size() const +{ + return elementRect().size(); +} + +Element* AccessibilityListBoxOption::actionElement() const +{ + return m_optionElement; +} + +AccessibilityObject* AccessibilityListBoxOption::parentObject() const +{ + HTMLSelectElement* parentNode = listBoxOptionParentNode(); + if (!parentNode) + return 0; + + return m_optionElement->document()->axObjectCache()->get(parentNode->renderer()); +} + +void AccessibilityListBoxOption::setSelected(bool selected) +{ + HTMLSelectElement* selectElement = listBoxOptionParentNode(); + if (!selectElement) + return; + + if (!canSetSelectedAttribute()) + return; + + bool isOptionSelected = isSelected(); + if ((isOptionSelected && selected) || (!isOptionSelected && !selected)) + return; + + selectElement->accessKeySetSelectedIndex(listBoxOptionIndex()); +} + +HTMLSelectElement* AccessibilityListBoxOption::listBoxOptionParentNode() const +{ + if (!m_optionElement) + return 0; + + if (m_optionElement->hasTagName(optionTag)) + return static_cast<HTMLOptionElement*>(m_optionElement)->ownerSelectElement(); + + if (m_optionElement->hasTagName(optgroupTag)) + return static_cast<HTMLOptGroupElement*>(m_optionElement)->ownerSelectElement(); + + return 0; +} + +int AccessibilityListBoxOption::listBoxOptionIndex() const +{ + if (!m_optionElement) + return -1; + + HTMLSelectElement* selectElement = listBoxOptionParentNode(); + if (!selectElement) + return -1; + + const Vector<HTMLElement*>& listItems = selectElement->listItems(); + unsigned length = listItems.size(); + for (unsigned i = 0; i < length; i++) + if (listItems[i] == m_optionElement) + return i; + + return -1; +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityListBoxOption.h b/WebCore/page/AccessibilityListBoxOption.h new file mode 100644 index 0000000..1b588cd --- /dev/null +++ b/WebCore/page/AccessibilityListBoxOption.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityListBoxOption_h +#define AccessibilityListBoxOption_h + +#include "AccessibilityObject.h" +#include "HTMLElement.h" + +namespace WebCore { + +class AccessibilityListBox; +class Element; +class HTMLElement; +class HTMLSelectElement; +class String; + +class AccessibilityListBoxOption : public AccessibilityObject { + +private: + AccessibilityListBoxOption(); +public: + static PassRefPtr<AccessibilityListBoxOption> create(); + virtual ~AccessibilityListBoxOption(); + + void setHTMLElement(HTMLElement* element) { m_optionElement = element; } + + virtual AccessibilityRole roleValue() const { return ListBoxOptionRole; } + virtual bool accessibilityIsIgnored() const { return false; } + virtual bool isSelected() const; + virtual bool isEnabled() const; + virtual String stringValue() const; + virtual Element* actionElement() const; + + virtual void setSelected(bool); + virtual bool canSetSelectedAttribute() const; + + virtual IntRect elementRect() const; + virtual IntSize size() const; + virtual AccessibilityObject* parentObject() const; + bool isListBoxOption() const { return true; }; + +private: + HTMLElement* m_optionElement; + + HTMLSelectElement* listBoxOptionParentNode() const; + int listBoxOptionIndex() const; + IntRect listBoxOptionRect() const; + AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement* element) const; +}; + +} // namespace WebCore + +#endif // AccessibilityListBoxOption_h diff --git a/WebCore/page/AccessibilityObject.cpp b/WebCore/page/AccessibilityObject.cpp new file mode 100644 index 0000000..6be8c1a --- /dev/null +++ b/WebCore/page/AccessibilityObject.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityObject.h" + +#include "AccessibilityRenderObject.h" +#include "AXObjectCache.h" +#include "CharacterNames.h" +#include "FloatRect.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "LocalizedStrings.h" +#include "NodeList.h" +#include "NotImplemented.h" +#include "Page.h" +#include "RenderImage.h" +#include "RenderListMarker.h" +#include "RenderMenuList.h" +#include "RenderTextControl.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "SelectionController.h" +#include "TextIterator.h" +#include "htmlediting.h" +#include "visible_units.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityObject::AccessibilityObject() + : m_id(0) + , m_haveChildren(false) +#if PLATFORM(GTK) + , m_wrapper(0) +#endif +{ +} + +AccessibilityObject::~AccessibilityObject() +{ + ASSERT(isDetached()); +} + +void AccessibilityObject::detach() +{ + removeAXObjectID(); +#if HAVE(ACCESSIBILITY) + setWrapper(0); +#endif +} + +AccessibilityObject* AccessibilityObject::firstChild() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::lastChild() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::previousSibling() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::nextSibling() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::parentObject() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::parentObjectUnignored() const +{ + AccessibilityObject* parent; + for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) + ; + return parent; +} + +int AccessibilityObject::layoutCount() const +{ + return 0; +} + +String AccessibilityObject::text() const +{ + return String(); +} + +String AccessibilityObject::helpText() const +{ + return String(); +} + +String AccessibilityObject::textUnderElement() const +{ + return String(); +} + +bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole) +{ + return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole; +} + +bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole) +{ + return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole + || ariaRole == ComboBoxRole || ariaRole == SliderRole; +} + +int AccessibilityObject::intValue() const +{ + return 0; +} + +String AccessibilityObject::stringValue() const +{ + return String(); +} + +String AccessibilityObject::ariaAccessiblityName(const String&) const +{ + return String(); +} + +String AccessibilityObject::ariaLabeledByAttribute() const +{ + return String(); +} + +String AccessibilityObject::title() const +{ + return String(); +} + +String AccessibilityObject::ariaDescribedByAttribute() const +{ + return String(); +} + +String AccessibilityObject::accessibilityDescription() const +{ + return String(); +} + +IntRect AccessibilityObject::boundingBoxRect() const +{ + return IntRect(); +} + +IntRect AccessibilityObject::elementRect() const +{ + return IntRect(); +} + +IntSize AccessibilityObject::size() const +{ + return IntSize(); +} + +IntPoint AccessibilityObject::clickPoint() const +{ + IntRect rect = elementRect(); + return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); +} + +void AccessibilityObject::linkedUIElements(AccessibilityChildrenVector&) const +{ + return; +} + +AccessibilityObject* AccessibilityObject::titleUIElement() const +{ + return 0; +} + +int AccessibilityObject::textLength() const +{ + return 0; +} + +PassRefPtr<Range> AccessibilityObject::ariaSelectedTextDOMRange() const +{ + return 0; +} + +String AccessibilityObject::selectedText() const +{ + return String(); +} + +const AtomicString& AccessibilityObject::accessKey() const +{ + return nullAtom; +} + +Selection AccessibilityObject::selection() const +{ + return Selection(); +} + +PlainTextRange AccessibilityObject::selectedTextRange() const +{ + return PlainTextRange(); +} + +unsigned AccessibilityObject::selectionStart() const +{ + return selectedTextRange().start; +} + +unsigned AccessibilityObject::selectionEnd() const +{ + return selectedTextRange().length; +} + +void AccessibilityObject::setSelectedText(const String&) +{ + // TODO: set selected text (ReplaceSelectionCommand). <rdar://problem/4712125> + notImplemented(); +} + +void AccessibilityObject::setSelectedTextRange(const PlainTextRange& range) +{ +} + +void AccessibilityObject::makeRangeVisible(const PlainTextRange&) +{ + // TODO: make range visible (scrollRectToVisible). <rdar://problem/4712101> + notImplemented(); +} + +KURL AccessibilityObject::url() const +{ + return KURL(); +} + +void AccessibilityObject::setFocused(bool on) +{ +} + +void AccessibilityObject::setValue(const String& string) +{ +} + +void AccessibilityObject::setSelected(bool) +{ +} + +bool AccessibilityObject::press() const +{ + Element* actionElem = actionElement(); + if (!actionElem) + return false; + if (Frame* f = actionElem->document()->frame()) + f->loader()->resetMultipleFormSubmissionProtection(); + actionElem->accessKeyAction(true); + return true; +} + +AXObjectCache* AccessibilityObject::axObjectCache() const +{ + return 0; +} + +Widget* AccessibilityObject::widget() const +{ + return 0; +} + +Widget* AccessibilityObject::widgetForAttachmentView() const +{ + return 0; +} + +Element* AccessibilityObject::anchorElement() const +{ + return 0; +} + +Element* AccessibilityObject::actionElement() const +{ + return 0; +} + +// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns +// a Range that we can convert to a WebCoreRange in the Obj-C file +VisiblePositionRange AccessibilityObject::visiblePositionRange() const +{ + return VisiblePositionRange(); +} + +VisiblePositionRange AccessibilityObject::visiblePositionRangeForLine(unsigned lineCount) const +{ + return VisiblePositionRange(); +} + +VisiblePosition AccessibilityObject::visiblePositionForIndex(int index) const +{ + return VisiblePosition(); +} + +int AccessibilityObject::indexForVisiblePosition(const VisiblePosition& pos) const +{ + return 0; +} + +VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const +{ + if (visiblePos1.isNull() || visiblePos2.isNull()) + return VisiblePositionRange(); + + VisiblePosition startPos; + VisiblePosition endPos; + bool alreadyInOrder; + + // upstream is ordered before downstream for the same position + if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM) + alreadyInOrder = false; + + // use selection order to see if the positions are in order + else + alreadyInOrder = Selection(visiblePos1, visiblePos2).isBaseFirst(); + + if (alreadyInOrder) { + startPos = visiblePos1; + endPos = visiblePos2; + } else { + startPos = visiblePos2; + endPos = visiblePos1; + } + + return VisiblePositionRange(startPos, endPos); +} + +VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const +{ + VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary); + VisiblePosition endPosition = endOfWord(startPosition); + return VisiblePositionRange(startPosition, endPosition); +} + +VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const +{ + VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary); + VisiblePosition endPosition = endOfWord(startPosition); + return VisiblePositionRange(startPosition, endPosition); +} + +static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition) +{ + // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line. + // So let's update the position to include that. + VisiblePosition tempPosition; + VisiblePosition startPosition = visiblePosition; + Position p; + RenderObject* renderer; + while (true) { + tempPosition = startPosition.previous(); + if (tempPosition.isNull()) + break; + p = tempPosition.deepEquivalent(); + if (!p.node()) + break; + renderer = p.node()->renderer(); + if (!renderer || renderer->isRenderBlock() && !p.offset()) + break; + InlineBox* box; + int ignoredCaretOffset; + p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset); + if (box) + break; + startPosition = tempPosition; + } + + return startPosition; +} + +VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePositionRange(); + + // make a caret selection for the position before marker position (to make sure + // we move off of a line start) + VisiblePosition prevVisiblePos = visiblePos.previous(); + if (prevVisiblePos.isNull()) + return VisiblePositionRange(); + + VisiblePosition startPosition = startOfLine(prevVisiblePos); + + // keep searching for a valid line start position. Unless the VisiblePosition is at the very beginning, there should + // always be a valid line range. However, startOfLine will return null for position next to a floating object, + // since floating object doesn't really belong to any line. + // This check will reposition the marker before the floating object, to ensure we get a line start. + if (startPosition.isNull()) { + while (startPosition.isNull() && prevVisiblePos.isNotNull()) { + prevVisiblePos = prevVisiblePos.previous(); + startPosition = startOfLine(prevVisiblePos); + } + } else + startPosition = updateAXLineStartForVisiblePosition(startPosition); + + VisiblePosition endPosition = endOfLine(prevVisiblePos); + return VisiblePositionRange(startPosition, endPosition); +} + +VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePositionRange(); + + // make sure we move off of a line end + VisiblePosition nextVisiblePos = visiblePos.next(); + if (nextVisiblePos.isNull()) + return VisiblePositionRange(); + + VisiblePosition startPosition = startOfLine(nextVisiblePos); + + // fetch for a valid line start position + if (startPosition.isNull() ) { + startPosition = visiblePos; + nextVisiblePos = nextVisiblePos.next(); + } else + startPosition = updateAXLineStartForVisiblePosition(startPosition); + + VisiblePosition endPosition = endOfLine(nextVisiblePos); + + // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position + // Unless the VisiblePosition is at the very end, there should always be a valid line range. However, endOfLine will + // return null for position by a floating object, since floating object doesn't really belong to any line. + // This check will reposition the marker after the floating object, to ensure we get a line end. + while (endPosition.isNull() && nextVisiblePos.isNotNull()) { + nextVisiblePos = nextVisiblePos.next(); + endPosition = endOfLine(nextVisiblePos); + } + + return VisiblePositionRange(startPosition, endPosition); +} + +VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const +{ + // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer) + // Related? <rdar://problem/3927736> Text selection broken in 8A336 + VisiblePosition startPosition = startOfSentence(visiblePos); + VisiblePosition endPosition = endOfSentence(startPosition); + return VisiblePositionRange(startPosition, endPosition); +} + +VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const +{ + VisiblePosition startPosition = startOfParagraph(visiblePos); + VisiblePosition endPosition = endOfParagraph(startPosition); + return VisiblePositionRange(startPosition, endPosition); +} + +static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos) +{ + RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer(); + RenderObject* startRenderer = renderer; + RenderStyle* style = renderer->style(); + + // traverse backward by renderer to look for style change + for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) { + // skip non-leaf nodes + if (r->firstChild()) + continue; + + // stop at style change + if (r->style() != style) + break; + + // remember match + startRenderer = r; + } + + return VisiblePosition(startRenderer->node(), 0, VP_DEFAULT_AFFINITY); +} + +static VisiblePosition endOfStyleRange(const VisiblePosition visiblePos) +{ + RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer(); + RenderObject* endRenderer = renderer; + RenderStyle* style = renderer->style(); + + // traverse forward by renderer to look for style change + for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) { + // skip non-leaf nodes + if (r->firstChild()) + continue; + + // stop at style change + if (r->style() != style) + break; + + // remember match + endRenderer = r; + } + + return VisiblePosition(endRenderer->node(), maxDeepOffset(endRenderer->node()), VP_DEFAULT_AFFINITY); +} + +VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePositionRange(); + + return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos)); +} + +// NOTE: Consider providing this utility method as AX API +VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const +{ + if (range.start + range.length > text().length()) + return VisiblePositionRange(); + + VisiblePosition startPosition = visiblePositionForIndex(range.start); + startPosition.setAffinity(DOWNSTREAM); + VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length); + return VisiblePositionRange(startPosition, endPosition); +} + +static bool replacedNodeNeedsCharacter(Node* replacedNode) +{ + // we should always be given a rendered node and a replaced node, but be safe + // replaced nodes are either attachments (widgets) or images + if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) { + return false; + } + + // create an AX object, but skip it if it is not supposed to be seen + AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer()); + if (object->accessibilityIsIgnored()) + return false; + + return true; +} + +String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const +{ + if (visiblePositionRange.isNull()) + return String(); + + Vector<UChar> resultVector; + RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end); + for (TextIterator it(range.get()); !it.atEnd(); it.advance()) { + // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) + if (it.length() != 0) { + resultVector.append(it.characters(), it.length()); + } else { + // locate the node and starting offset for this replaced range + int exception = 0; + Node* node = it.range()->startContainer(exception); + ASSERT(node == it.range()->endContainer(exception)); + int offset = it.range()->startOffset(exception); + + if (replacedNodeNeedsCharacter(node->childNode(offset))) { + resultVector.append(objectReplacementCharacter); + } + } + } + + return String::adopt(resultVector); +} + +IntRect AccessibilityObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const +{ + return IntRect(); +} + +int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const +{ + // FIXME: Multi-byte support + if (visiblePositionRange.isNull()) + return -1; + + int length = 0; + RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end); + for (TextIterator it(range.get()); !it.atEnd(); it.advance()) { + // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) + if (it.length() != 0) { + length += it.length(); + } else { + // locate the node and starting offset for this replaced range + int exception = 0; + Node* node = it.range()->startContainer(exception); + ASSERT(node == it.range()->endContainer(exception)); + int offset = it.range()->startOffset(exception); + + if (replacedNodeNeedsCharacter(node->childNode(offset))) + length++; + } + } + + return length; +} + +void AccessibilityObject::setSelectedVisiblePositionRange(const VisiblePositionRange&) const +{ +} + +VisiblePosition AccessibilityObject::visiblePositionForPoint(const IntPoint& point) const +{ + return VisiblePosition(); +} + +VisiblePosition AccessibilityObject::nextVisiblePosition(const VisiblePosition& visiblePos) const +{ + return visiblePos.next(); +} + +VisiblePosition AccessibilityObject::previousVisiblePosition(const VisiblePosition& visiblePos) const +{ + return visiblePos.previous(); +} + +VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePosition(); + + // make sure we move off of a word end + VisiblePosition nextVisiblePos = visiblePos.next(); + if (nextVisiblePos.isNull()) + return VisiblePosition(); + + return endOfWord(nextVisiblePos, LeftWordIfOnBoundary); +} + +VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePosition(); + + // make sure we move off of a word start + VisiblePosition prevVisiblePos = visiblePos.previous(); + if (prevVisiblePos.isNull()) + return VisiblePosition(); + + return startOfWord(prevVisiblePos, RightWordIfOnBoundary); +} + +VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePosition(); + + // to make sure we move off of a line end + VisiblePosition nextVisiblePos = visiblePos.next(); + if (nextVisiblePos.isNull()) + return VisiblePosition(); + + VisiblePosition endPosition = endOfLine(nextVisiblePos); + + // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position + // There are cases like when the position is next to a floating object that'll return null for end of line. This code will avoid returning null. + while (endPosition.isNull() && nextVisiblePos.isNotNull()) { + nextVisiblePos = nextVisiblePos.next(); + endPosition = endOfLine(nextVisiblePos); + } + + return endPosition; +} + +VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePosition(); + + // make sure we move off of a line start + VisiblePosition prevVisiblePos = visiblePos.previous(); + if (prevVisiblePos.isNull()) + return VisiblePosition(); + + VisiblePosition startPosition = startOfLine(prevVisiblePos); + + // as long as the position hasn't reached the beginning of the doc, keep searching for a valid line start position + // There are cases like when the position is next to a floating object that'll return null for start of line. This code will avoid returning null. + if (startPosition.isNull()) { + while (startPosition.isNull() && prevVisiblePos.isNotNull()) { + prevVisiblePos = prevVisiblePos.previous(); + startPosition = startOfLine(prevVisiblePos); + } + } else + startPosition = updateAXLineStartForVisiblePosition(startPosition); + + return startPosition; +} + +VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const +{ + // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer) + // Related? <rdar://problem/3927736> Text selection broken in 8A336 + if (visiblePos.isNull()) + return VisiblePosition(); + + // make sure we move off of a sentence end + VisiblePosition nextVisiblePos = visiblePos.next(); + if (nextVisiblePos.isNull()) + return VisiblePosition(); + + // an empty line is considered a sentence. If it's skipped, then the sentence parser will not + // see this empty line. Instead, return the end position of the empty line. + VisiblePosition endPosition; + String lineString = plainText(makeRange(startOfLine(visiblePos), endOfLine(visiblePos)).get()); + if (lineString.isEmpty()) + endPosition = nextVisiblePos; + else + endPosition = endOfSentence(nextVisiblePos); + + return endPosition; +} + +VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const +{ + // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer) + // Related? <rdar://problem/3927736> Text selection broken in 8A336 + if (visiblePos.isNull()) + return VisiblePosition(); + + // make sure we move off of a sentence start + VisiblePosition previousVisiblePos = visiblePos.previous(); + if (previousVisiblePos.isNull()) + return VisiblePosition(); + + // treat empty line as a separate sentence. + VisiblePosition startPosition; + String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get()); + if (lineString.isEmpty()) + startPosition = previousVisiblePos; + else + startPosition = startOfSentence(previousVisiblePos); + + return startPosition; +} + +VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePosition(); + + // make sure we move off of a paragraph end + VisiblePosition nextPos = visiblePos.next(); + if (nextPos.isNull()) + return VisiblePosition(); + + return endOfParagraph(nextPos); +} + +VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return VisiblePosition(); + + // make sure we move off of a paragraph start + VisiblePosition previousPos = visiblePos.previous(); + if (previousPos.isNull()) + return VisiblePosition(); + + return startOfParagraph(previousPos); +} + +// NOTE: Consider providing this utility method as AX API +VisiblePosition AccessibilityObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const +{ + return VisiblePosition(); +} + +AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return 0; + + RenderObject* obj = visiblePos.deepEquivalent().node()->renderer(); + if (!obj) + return 0; + + return obj->document()->axObjectCache()->get(obj); +} + +int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const +{ + if (visiblePos.isNull()) + return 0; + + unsigned lineCount = 0; + VisiblePosition currentVisiblePos = visiblePos; + VisiblePosition savedVisiblePos; + + // move up until we get to the top + // FIXME: This only takes us to the top of the rootEditableElement, not the top of the + // top document. + while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))) { + ++lineCount; + savedVisiblePos = currentVisiblePos; + VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0); + currentVisiblePos = prevVisiblePos; + } + + return lineCount - 1; +} + +// NOTE: Consider providing this utility method as AX API +PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const +{ + int index1 = index(positionRange.start); + int index2 = index(positionRange.end); + if (index1 < 0 || index2 < 0 || index1 > index2) + return PlainTextRange(); + + return PlainTextRange(index1, index2 - index1); +} + +// NOTE: Consider providing this utility method as AX API +int AccessibilityObject::index(const VisiblePosition& position) const +{ + return -1; +} + +// Given a line number, the range of characters of the text associated with this accessibility +// object that contains the line number. +PlainTextRange AccessibilityObject::doAXRangeForLine(unsigned lineNumber) const +{ + return PlainTextRange(); +} + +// The composed character range in the text associated with this accessibility object that +// is specified by the given screen coordinates. This parameterized attribute returns the +// complete range of characters (including surrogate pairs of multi-byte glyphs) at the given +// screen coordinates. +// NOTE: This varies from AppKit when the point is below the last line. AppKit returns an +// an error in that case. We return textControl->text().length(), 1. Does this matter? +PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const +{ + int i = index(visiblePositionForPoint(point)); + if (i < 0) + return PlainTextRange(); + + return PlainTextRange(i, 1); +} + +// The composed character range in the text associated with this accessibility object that +// is specified by the given index value. This parameterized attribute returns the complete +// range of characters (including surrogate pairs of multi-byte glyphs) at the given index. +PlainTextRange AccessibilityObject::doAXRangeForIndex(unsigned index) const +{ + return PlainTextRange(); +} + +// Given a character index, the range of text associated with this accessibility object +// over which the style in effect at that character index applies. +PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const +{ + VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false)); + return plainTextRangeForVisiblePositionRange(range); +} + +// A substring of the text associated with this accessibility object that is +// specified by the given character range. +String AccessibilityObject::doAXStringForRange(const PlainTextRange& range) const +{ + return String(); +} + +// The bounding rectangle of the text associated with this accessibility object that is +// specified by the given range. This is the bounding rectangle a sighted user would see +// on the display screen, in pixels. +IntRect AccessibilityObject::doAXBoundsForRange(const PlainTextRange& range) const +{ + return IntRect(); +} + +// Given an indexed character, the line number of the text associated with this accessibility +// object that contains the character. +unsigned AccessibilityObject::doAXLineForIndex(unsigned index) +{ + return lineForPosition(visiblePositionForIndex(index, false)); +} + +FrameView* AccessibilityObject::documentFrameView() const +{ + const AccessibilityObject* object = this; + while (object && !object->isAccessibilityRenderObject()) + object = object->parentObject(); + + if (!object) + return 0; + + return object->documentFrameView(); +} + +AccessibilityObject* AccessibilityObject::doAccessibilityHitTest(const IntPoint& point) const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::focusedUIElement() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::observableObject() const +{ + return 0; +} + +AccessibilityRole AccessibilityObject::roleValue() const +{ + return UnknownRole; +} + +AccessibilityRole AccessibilityObject::ariaRoleAttribute() const +{ + return UnknownRole; +} + +bool AccessibilityObject::isPresentationalChildOfAriaRole() const +{ + return false; +} + +bool AccessibilityObject::ariaRoleHasPresentationalChildren() const +{ + return false; +} + +void AccessibilityObject::clearChildren() +{ + m_haveChildren = false; + m_children.clear(); +} + +void AccessibilityObject::childrenChanged() +{ + return; +} + +void AccessibilityObject::addChildren() +{ +} + +void AccessibilityObject::selectedChildren(AccessibilityChildrenVector&) +{ +} + +void AccessibilityObject::visibleChildren(AccessibilityChildrenVector&) +{ +} + +unsigned AccessibilityObject::axObjectID() const +{ + return m_id; +} + +void AccessibilityObject::setAXObjectID(unsigned axObjectID) +{ + m_id = axObjectID; +} + +void AccessibilityObject::removeAXObjectID() +{ + return; +} + +const String& AccessibilityObject::actionVerb() const +{ + // FIXME: Need to add verbs for select elements. + static const String buttonAction = AXButtonActionVerb(); + static const String textFieldAction = AXTextFieldActionVerb(); + static const String radioButtonAction = AXRadioButtonActionVerb(); + static const String checkedCheckBoxAction = AXCheckedCheckBoxActionVerb(); + static const String uncheckedCheckBoxAction = AXUncheckedCheckBoxActionVerb(); + static const String linkAction = AXLinkActionVerb(); + static const String noAction; + + switch (roleValue()) { + case ButtonRole: + return buttonAction; + case TextFieldRole: + case TextAreaRole: + return textFieldAction; + case RadioButtonRole: + return radioButtonAction; + case CheckBoxRole: + return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction; + case LinkRole: + case WebCoreLinkRole: + return linkAction; + default: + return noAction; + } +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityObject.h b/WebCore/page/AccessibilityObject.h new file mode 100644 index 0000000..947757a --- /dev/null +++ b/WebCore/page/AccessibilityObject.h @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nuanti Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityObject_h +#define AccessibilityObject_h + +#include "VisiblePosition.h" +#include <wtf/Platform.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +#if PLATFORM(MAC) +#include <wtf/RetainPtr.h> +#elif PLATFORM(WIN) +#include "AccessibilityObjectWrapperWin.h" +#include "COMPtr.h" +#elif PLATFORM(CHROMIUM) +#include "AccessibilityObjectWrapper.h" +#endif + +typedef struct _NSRange NSRange; + +#ifdef __OBJC__ +@class AccessibilityObjectWrapper; +@class NSArray; +@class NSAttributedString; +@class NSData; +@class NSMutableAttributedString; +@class NSString; +@class NSValue; +@class NSView; +#else +class NSArray; +class NSAttributedString; +class NSData; +class NSMutableAttributedString; +class NSString; +class NSValue; +class NSView; +#if PLATFORM(GTK) +typedef struct _AtkObject AtkObject; +typedef struct _AtkObject AccessibilityObjectWrapper; +#else +class AccessibilityObjectWrapper; +#endif +#endif + +namespace WebCore { + +class AXObjectCache; +class Element; +class Frame; +class FrameView; +class HTMLAnchorElement; +class HTMLAreaElement; +class IntPoint; +class IntSize; +class Node; +class RenderObject; +class Selection; +class String; +class Widget; + +enum AccessibilityRole { + UnknownRole = 1, + ButtonRole, + RadioButtonRole, + CheckBoxRole, + SliderRole, + TabGroupRole, + TextFieldRole, + StaticTextRole, + TextAreaRole, + ScrollAreaRole, + PopUpButtonRole, + MenuButtonRole, + TableRole, + ApplicationRole, + GroupRole, + RadioGroupRole, + ListRole, + ScrollBarRole, + ValueIndicatorRole, + ImageRole, + MenuBarRole, + MenuRole, + MenuItemRole, + ColumnRole, + RowRole, + ToolbarRole, + BusyIndicatorRole, + ProgressIndicatorRole, + WindowRole, + DrawerRole, + SystemWideRole, + OutlineRole, + IncrementorRole, + BrowserRole, + ComboBoxRole, + SplitGroupRole, + SplitterRole, + ColorWellRole, + GrowAreaRole, + SheetRole, + HelpTagRole, + MatteRole, + RulerRole, + RulerMarkerRole, + LinkRole, + DisclosureTriangleRole, + GridRole, + CellRole, + // AppKit includes SortButtonRole but it is misnamed and really a subrole of ButtonRole so we do not include it here. + + // WebCore-specific roles + WebCoreLinkRole, + ImageMapLinkRole, + ImageMapRole, + ListMarkerRole, + WebAreaRole, + HeadingRole, + ListBoxRole, + ListBoxOptionRole, + TableHeaderContainerRole, + DefinitionListTermRole, + DefinitionListDefinitionRole +}; + +struct VisiblePositionRange { + + VisiblePosition start; + VisiblePosition end; + + VisiblePositionRange() {} + + VisiblePositionRange(const VisiblePosition& s, const VisiblePosition& e) + : start(s) + , end(e) + { } + + bool isNull() const { return start.isNull() || end.isNull(); } +}; + +struct PlainTextRange { + + unsigned start; + unsigned length; + + PlainTextRange() + : start(0) + , length(0) + { } + + PlainTextRange(unsigned s, unsigned l) + : start(s) + , length(l) + { } + + bool isNull() const { return start == 0 && length == 0; } +}; + +class AccessibilityObject : public RefCounted<AccessibilityObject> { +protected: + AccessibilityObject(); +public: + virtual ~AccessibilityObject(); + + typedef Vector<RefPtr<AccessibilityObject> > AccessibilityChildrenVector; + + virtual bool isAccessibilityRenderObject() const { return false; }; + virtual bool isAnchor() const { return false; }; + virtual bool isAttachment() const { return false; }; + virtual bool isHeading() const { return false; }; + virtual bool isLink() const { return false; }; + virtual bool isImage() const { return false; }; + virtual bool isNativeImage() const { return false; }; + virtual bool isImageButton() const { return false; }; + virtual bool isPasswordField() const { return false; }; + virtual bool isTextControl() const { return false; }; + virtual bool isNativeTextControl() const { return false; }; + virtual bool isWebArea() const { return false; }; + virtual bool isCheckboxOrRadio() const { return false; }; + virtual bool isListBox() const { return roleValue() == ListBoxRole; }; + virtual bool isMenuRelated() const { return false; } + virtual bool isMenu() const { return false; } + virtual bool isMenuBar() const { return false; } + virtual bool isMenuButton() const { return false; } + virtual bool isMenuItem() const { return false; } + virtual bool isFileUploadButton() const { return false; }; + virtual bool isProgressIndicator() const { return false; }; + virtual bool isSlider() const { return false; }; + virtual bool isControl() const { return false; }; + virtual bool isList() const { return false; }; + virtual bool isDataTable() const { return false; }; + virtual bool isTableRow() const { return false; }; + virtual bool isTableColumn() const { return false; }; + virtual bool isTableCell() const { return false; }; + virtual bool isFieldset() const { return false; }; + + virtual bool isChecked() const { return false; }; + virtual bool isEnabled() const { return false; }; + virtual bool isSelected() const { return false; }; + virtual bool isFocused() const { return false; }; + virtual bool isHovered() const { return false; }; + virtual bool isIndeterminate() const { return false; }; + virtual bool isLoaded() const { return false; }; + virtual bool isMultiSelect() const { return false; }; + virtual bool isOffScreen() const { return false; }; + virtual bool isPressed() const { return false; }; + virtual bool isReadOnly() const { return false; }; + virtual bool isVisited() const { return false; }; + + virtual bool canSetFocusAttribute() const { return false; }; + virtual bool canSetTextRangeAttributes() const { return false; }; + virtual bool canSetValueAttribute() const { return false; }; + virtual bool canSetSelectedAttribute() const { return false; } + virtual bool canSetSelectedChildrenAttribute() const { return false; } + + virtual bool hasIntValue() const { return false; }; + + bool accessibilityShouldUseUniqueId() const { return true; }; + virtual bool accessibilityIsIgnored() const { return true; }; + + virtual int intValue() const; + virtual float valueForRange() const { return 0.0f; } + virtual float maxValueForRange() const { return 0.0f; } + virtual float minValueForRange() const {return 0.0f; } + virtual int layoutCount() const; + static bool isARIAControl(AccessibilityRole); + static bool isARIAInput(AccessibilityRole); + unsigned axObjectID() const; + + virtual AccessibilityObject* doAccessibilityHitTest(const IntPoint&) const; + virtual AccessibilityObject* focusedUIElement() const; + virtual AccessibilityObject* firstChild() const; + virtual AccessibilityObject* lastChild() const; + virtual AccessibilityObject* previousSibling() const; + virtual AccessibilityObject* nextSibling() const; + virtual AccessibilityObject* parentObject() const; + virtual AccessibilityObject* parentObjectUnignored() const; + virtual AccessibilityObject* observableObject() const; + virtual void linkedUIElements(AccessibilityChildrenVector&) const; + virtual AccessibilityObject* titleUIElement() const; + virtual AccessibilityRole ariaRoleAttribute() const; + virtual bool isPresentationalChildOfAriaRole() const; + virtual bool ariaRoleHasPresentationalChildren() const; + + virtual AccessibilityRole roleValue() const; + virtual AXObjectCache* axObjectCache() const; + + virtual Element* anchorElement() const; + virtual Element* actionElement() const; + virtual IntRect boundingBoxRect() const; + virtual IntRect elementRect() const; + virtual IntSize size() const; + IntPoint clickPoint() const; + + virtual KURL url() const; + virtual PlainTextRange selectedTextRange() const; + virtual Selection selection() const; + unsigned selectionStart() const; + unsigned selectionEnd() const; + virtual String stringValue() const; + virtual String ariaAccessiblityName(const String&) const; + virtual String ariaLabeledByAttribute() const; + virtual String title() const; + virtual String ariaDescribedByAttribute() const; + virtual String accessibilityDescription() const; + virtual String helpText() const; + virtual String textUnderElement() const; + virtual String text() const; + virtual int textLength() const; + virtual PassRefPtr<Range> ariaSelectedTextDOMRange() const; + virtual String selectedText() const; + virtual const AtomicString& accessKey() const; + const String& actionVerb() const; + virtual Widget* widget() const; + virtual Widget* widgetForAttachmentView() const; + virtual Document* document() const { return 0; } + virtual FrameView* topDocumentFrameView() const { return 0; } + virtual FrameView* documentFrameView() const; + + void setAXObjectID(unsigned); + virtual void setFocused(bool); + virtual void setSelectedText(const String&); + virtual void setSelectedTextRange(const PlainTextRange&); + virtual void setValue(const String&); + virtual void setSelected(bool); + + virtual void detach(); + virtual void makeRangeVisible(const PlainTextRange&); + virtual bool press() const; + bool performDefaultAction() const { return press(); } + + virtual void childrenChanged(); + virtual const AccessibilityChildrenVector& children() { return m_children; } + virtual void addChildren(); + virtual bool canHaveChildren() const { return true; } + virtual bool hasChildren() const { return m_haveChildren; }; + virtual void selectedChildren(AccessibilityChildrenVector&); + virtual void visibleChildren(AccessibilityChildrenVector&); + virtual bool shouldFocusActiveDescendant() const { return false; } + virtual AccessibilityObject* activeDescendant() const { return 0; } + virtual void handleActiveDescendantChanged() { } + + virtual VisiblePositionRange visiblePositionRange() const; + virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const; + + VisiblePositionRange visiblePositionRangeForUnorderedPositions(const VisiblePosition&, const VisiblePosition&) const; + VisiblePositionRange positionOfLeftWord(const VisiblePosition&) const; + VisiblePositionRange positionOfRightWord(const VisiblePosition&) const; + VisiblePositionRange leftLineVisiblePositionRange(const VisiblePosition&) const; + VisiblePositionRange rightLineVisiblePositionRange(const VisiblePosition&) const; + VisiblePositionRange sentenceForPosition(const VisiblePosition&) const; + VisiblePositionRange paragraphForPosition(const VisiblePosition&) const; + VisiblePositionRange styleRangeForPosition(const VisiblePosition&) const; + VisiblePositionRange visiblePositionRangeForRange(const PlainTextRange&) const; + + String stringForVisiblePositionRange(const VisiblePositionRange&) const; + virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const; + int lengthForVisiblePositionRange(const VisiblePositionRange&) const; + virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const; + + virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const; + VisiblePosition nextVisiblePosition(const VisiblePosition&) const; + VisiblePosition previousVisiblePosition(const VisiblePosition&) const; + VisiblePosition nextWordEnd(const VisiblePosition&) const; + VisiblePosition previousWordStart(const VisiblePosition&) const; + VisiblePosition nextLineEndPosition(const VisiblePosition&) const; + VisiblePosition previousLineStartPosition(const VisiblePosition&) const; + VisiblePosition nextSentenceEndPosition(const VisiblePosition&) const; + VisiblePosition previousSentenceStartPosition(const VisiblePosition&) const; + VisiblePosition nextParagraphEndPosition(const VisiblePosition&) const; + VisiblePosition previousParagraphStartPosition(const VisiblePosition&) const; + virtual VisiblePosition visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const; + + virtual VisiblePosition visiblePositionForIndex(int) const; + virtual int indexForVisiblePosition(const VisiblePosition&) const; + + AccessibilityObject* accessibilityObjectForPosition(const VisiblePosition&) const; + int lineForPosition(const VisiblePosition&) const; + PlainTextRange plainTextRangeForVisiblePositionRange(const VisiblePositionRange&) const; + virtual int index(const VisiblePosition&) const; + + virtual PlainTextRange doAXRangeForLine(unsigned) const; + PlainTextRange doAXRangeForPosition(const IntPoint&) const; + virtual PlainTextRange doAXRangeForIndex(unsigned) const; + PlainTextRange doAXStyleRangeForIndex(unsigned) const; + + virtual String doAXStringForRange(const PlainTextRange&) const; + virtual IntRect doAXBoundsForRange(const PlainTextRange&) const; + + unsigned doAXLineForIndex(unsigned); + +#if HAVE(ACCESSIBILITY) +#if PLATFORM(GTK) + AccessibilityObjectWrapper* wrapper() const; + void setWrapper(AccessibilityObjectWrapper*); +#else + AccessibilityObjectWrapper* wrapper() const { return m_wrapper.get(); } + void setWrapper(AccessibilityObjectWrapper* wrapper) + { + m_wrapper = wrapper; + } +#endif +#endif + + // a platform-specific method for determining if an attachment is ignored + bool accessibilityIgnoreAttachment() const; + +protected: + unsigned m_id; + AccessibilityChildrenVector m_children; + mutable bool m_haveChildren; + + virtual void clearChildren(); + virtual void removeAXObjectID(); + virtual bool isDetached() const { return true; } + +#if PLATFORM(MAC) + RetainPtr<AccessibilityObjectWrapper> m_wrapper; +#elif PLATFORM(WIN) + COMPtr<AccessibilityObjectWrapper> m_wrapper; +#elif PLATFORM(GTK) + AtkObject* m_wrapper; +#elif PLATFORM(CHROMIUM) + RefPtr<AccessibilityObjectWrapper> m_wrapper; +#endif +}; + +} // namespace WebCore + +#endif // AccessibilityObject_h diff --git a/WebCore/page/AccessibilityRenderObject.cpp b/WebCore/page/AccessibilityRenderObject.cpp new file mode 100644 index 0000000..144aab0 --- /dev/null +++ b/WebCore/page/AccessibilityRenderObject.cpp @@ -0,0 +1,2378 @@ +/* +* Copyright (C) 2008 Apple 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: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. 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. +* 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityRenderObject.h" + +#include "AXObjectCache.h" +#include "AccessibilityListBox.h" +#include "AccessibilityImageMapLink.h" +#include "CharacterNames.h" +#include "EventNames.h" +#include "FloatRect.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTMLAreaElement.h" +#include "HTMLFrameElementBase.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLLabelElement.h" +#include "HTMLMapElement.h" +#include "HTMLOptGroupElement.h" +#include "HTMLOptionElement.h" +#include "HTMLOptionsCollection.h" +#include "HTMLSelectElement.h" +#include "HTMLTextAreaElement.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "LocalizedStrings.h" +#include "NodeList.h" +#include "NotImplemented.h" +#include "Page.h" +#include "RenderFieldset.h" +#include "RenderFileUploadControl.h" +#include "RenderImage.h" +#include "RenderListBox.h" +#include "RenderListMarker.h" +#include "RenderMenuList.h" +#include "RenderTextControl.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "SelectionController.h" +#include "Text.h" +#include "TextIterator.h" +#include "htmlediting.h" +#include "visible_units.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer) + : m_renderer(renderer) + , m_ariaRole(UnknownRole) +{ + setAriaRole(); +#ifndef NDEBUG + m_renderer->setHasAXObject(true); +#endif +} + +AccessibilityRenderObject::~AccessibilityRenderObject() +{ + ASSERT(isDetached()); +} + +PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityRenderObject(renderer)); +} + +void AccessibilityRenderObject::detach() +{ + clearChildren(); + AccessibilityObject::detach(); + +#ifndef NDEBUG + if (m_renderer) + m_renderer->setHasAXObject(false); +#endif + m_renderer = 0; +} + +AccessibilityObject* AccessibilityRenderObject::firstChild() const +{ + if (!m_renderer) + return 0; + + RenderObject* firstChild = m_renderer->firstChild(); + if (!firstChild) + return 0; + + return m_renderer->document()->axObjectCache()->get(firstChild); +} + +AccessibilityObject* AccessibilityRenderObject::lastChild() const +{ + if (!m_renderer) + return 0; + + RenderObject* lastChild = m_renderer->lastChild(); + if (!lastChild) + return 0; + + return m_renderer->document()->axObjectCache()->get(lastChild); +} + +AccessibilityObject* AccessibilityRenderObject::previousSibling() const +{ + if (!m_renderer) + return 0; + + RenderObject* previousSibling = m_renderer->previousSibling(); + if (!previousSibling) + return 0; + + return m_renderer->document()->axObjectCache()->get(previousSibling); +} + +AccessibilityObject* AccessibilityRenderObject::nextSibling() const +{ + if (!m_renderer) + return 0; + + RenderObject* nextSibling = m_renderer->nextSibling(); + if (!nextSibling) + return 0; + + return m_renderer->document()->axObjectCache()->get(nextSibling); +} + +AccessibilityObject* AccessibilityRenderObject::parentObject() const +{ + if (!m_renderer) + return 0; + + RenderObject *parent = m_renderer->parent(); + if (!parent) + return 0; + + if (ariaRoleAttribute() == MenuBarRole) + return m_renderer->document()->axObjectCache()->get(parent); + + // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child + if (ariaRoleAttribute() == MenuRole) { + AccessibilityObject* parent = menuButtonForMenu(); + if (parent) + return parent; + } + + return m_renderer->document()->axObjectCache()->get(parent); +} + +bool AccessibilityRenderObject::isWebArea() const +{ + return roleValue() == WebAreaRole; +} + +bool AccessibilityRenderObject::isImageButton() const +{ + return isNativeImage() && roleValue() == ButtonRole; +} + +bool AccessibilityRenderObject::isAnchor() const +{ + return !isNativeImage() && isLink(); +} + +bool AccessibilityRenderObject::isNativeTextControl() const +{ + return m_renderer->isTextField() || m_renderer->isTextArea(); +} + +bool AccessibilityRenderObject::isTextControl() const +{ + AccessibilityRole role = roleValue(); + return role == TextAreaRole || role == TextFieldRole; +} + +bool AccessibilityRenderObject::isNativeImage() const +{ + return m_renderer->isImage(); +} + +bool AccessibilityRenderObject::isImage() const +{ + return roleValue() == ImageRole; +} + +bool AccessibilityRenderObject::isAttachment() const +{ + // Widgets are the replaced elements that we represent to AX as attachments + bool isWidget = m_renderer && m_renderer->isWidget(); + ASSERT(!isWidget || (m_renderer->isReplaced() && !isImage())); + return isWidget && ariaRoleAttribute() == UnknownRole; +} + +bool AccessibilityRenderObject::isPasswordField() const +{ + ASSERT(m_renderer); + if (!m_renderer->element() || !m_renderer->element()->isHTMLElement()) + return false; + return static_cast<HTMLElement*>(m_renderer->element())->isPasswordField() && ariaRoleAttribute() == UnknownRole; +} + +bool AccessibilityRenderObject::isCheckboxOrRadio() const +{ + AccessibilityRole role = roleValue(); + return role == RadioButtonRole || role == CheckBoxRole; +} + +bool AccessibilityRenderObject::isFileUploadButton() const +{ + if (m_renderer && m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); + return input->inputType() == HTMLInputElement::FILE; + } + + return false; +} + +bool AccessibilityRenderObject::isProgressIndicator() const +{ + return roleValue() == ProgressIndicatorRole; +} + +bool AccessibilityRenderObject::isSlider() const +{ + return roleValue() == SliderRole; +} + +bool AccessibilityRenderObject::isMenuRelated() const +{ + AccessibilityRole role = roleValue(); + return role == MenuRole || + role == MenuBarRole || + role == MenuButtonRole || + role == MenuItemRole; +} + +bool AccessibilityRenderObject::isMenu() const +{ + return roleValue() == MenuRole; +} + +bool AccessibilityRenderObject::isMenuBar() const +{ + return roleValue() == MenuBarRole; +} + +bool AccessibilityRenderObject::isMenuButton() const +{ + return roleValue() == MenuButtonRole; +} + +bool AccessibilityRenderObject::isMenuItem() const +{ + return roleValue() == MenuItemRole; +} + +bool AccessibilityRenderObject::isPressed() const +{ + ASSERT(m_renderer); + if (roleValue() != ButtonRole) + return false; + + Node* node = m_renderer->node(); + if (!node) + return false; + + // If this is an ARIA button, check the aria-pressed attribute rather than node()->active() + if (ariaRoleAttribute() == ButtonRole) { + if (equalIgnoringCase(getAttribute(aria_pressedAttr).string(), "true")) + return true; + return false; + } + + return node->active(); +} + +bool AccessibilityRenderObject::isIndeterminate() const +{ + ASSERT(m_renderer); + return m_renderer->node() && m_renderer->node()->isIndeterminate(); +} + +bool AccessibilityRenderObject::isChecked() const +{ + ASSERT(m_renderer); + return m_renderer->node() && m_renderer->node()->isChecked(); +} + +bool AccessibilityRenderObject::isHovered() const +{ + ASSERT(m_renderer); + return m_renderer->node() && m_renderer->node()->hovered(); +} + +bool AccessibilityRenderObject::isMultiSelect() const +{ + ASSERT(m_renderer); + if (!m_renderer->isListBox()) + return false; + return m_renderer->element() && static_cast<HTMLSelectElement*>(m_renderer->element())->multiple(); +} + +bool AccessibilityRenderObject::isReadOnly() const +{ + ASSERT(m_renderer); + + if (isWebArea()) { + Document* document = m_renderer->document(); + if (!document) + return true; + + Frame* frame = document->frame(); + if (!frame) + return true; + + return !frame->isContentEditable(); + } + + return !m_renderer->node() || !m_renderer->node()->isContentEditable(); +} + +bool AccessibilityRenderObject::isOffScreen() const +{ + ASSERT(m_renderer); + IntRect contentRect = m_renderer->absoluteClippedOverflowRect(); + FrameView* view = m_renderer->document()->frame()->view(); + FloatRect viewRect = view->visibleContentRect(); + viewRect.intersect(contentRect); + return viewRect.isEmpty(); +} + +int AccessibilityRenderObject::headingLevel(Node* node) +{ + // headings can be in block flow and non-block flow + if (!node) + return 0; + + if (RenderObject* renderer = node->renderer()) { + AccessibilityObject* axObjectForNode = node->document()->axObjectCache()->get(renderer); + if (axObjectForNode->ariaRoleAttribute() == HeadingRole) { + if (!node->isElementNode()) + return 0; + Element* element = static_cast<Element*>(node); + return element->getAttribute(aria_levelAttr).toInt(); + } + } + + + if (node->hasTagName(h1Tag)) + return 1; + + if (node->hasTagName(h2Tag)) + return 2; + + if (node->hasTagName(h3Tag)) + return 3; + + if (node->hasTagName(h4Tag)) + return 4; + + if (node->hasTagName(h5Tag)) + return 5; + + if (node->hasTagName(h6Tag)) + return 6; + + return 0; +} + +bool AccessibilityRenderObject::isHeading() const +{ + return roleValue() == HeadingRole; +} + +bool AccessibilityRenderObject::isLink() const +{ + return roleValue() == WebCoreLinkRole; +} + +bool AccessibilityRenderObject::isControl() const +{ + if (!m_renderer) + return false; + + Node* node = m_renderer->element(); + return node && (node->isControl() || AccessibilityObject::isARIAControl(ariaRoleAttribute())); +} + +bool AccessibilityRenderObject::isFieldset() const +{ + if (!m_renderer) + return false; + + return m_renderer->isFieldset(); +} + +const AtomicString& AccessibilityRenderObject::getAttribute(const QualifiedName& attribute) const +{ + Node* node = m_renderer->element(); + if (!node) + return nullAtom; + + if (!node->isElementNode()) + return nullAtom; + + Element* element = static_cast<Element*>(node); + return element->getAttribute(attribute); +} + +Element* AccessibilityRenderObject::anchorElement() const +{ + if (!m_renderer) + return 0; + + AXObjectCache* cache = axObjectCache(); + RenderObject* currRenderer; + + // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though. + for (currRenderer = m_renderer; currRenderer && !currRenderer->element(); currRenderer = currRenderer->parent()) { + if (currRenderer->continuation()) + return cache->get(currRenderer->continuation())->anchorElement(); + } + + // bail if none found + if (!currRenderer) + return 0; + + // search up the DOM tree for an anchor element + // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement + Node* node = currRenderer->node(); + for ( ; node; node = node->parentNode()) { + if (node->hasTagName(aTag) || (node->renderer() && cache->get(node->renderer())->isAnchor())) + return static_cast<Element*>(node); + } + + return 0; +} + +Element* AccessibilityRenderObject::actionElement() const +{ + if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); + if (!input->disabled() && (isCheckboxOrRadio() || input->isTextButton())) + return input; + } + + if (isFileUploadButton()) + return static_cast<Element*>(m_renderer->element()); + + if (AccessibilityObject::isARIAInput(ariaRoleAttribute())) + return static_cast<Element*>(m_renderer->element()); + + if (isImageButton()) + return static_cast<Element*>(m_renderer->element()); + + if (m_renderer->isMenuList()) + return static_cast<RenderMenuList*>(m_renderer)->selectElement(); + + Element* elt = anchorElement(); + if (!elt) + elt = mouseButtonListener(); + return elt; +} + +Element* AccessibilityRenderObject::mouseButtonListener() const +{ + Node* node = m_renderer->element(); + if (!node) + return 0; + if (!node->isEventTargetNode()) + return 0; + + // FIXME: Do the continuation search like anchorElement does + for (EventTargetNode* elt = static_cast<EventTargetNode*>(node); elt; elt = static_cast<EventTargetNode*>(elt->parentNode())) { + if (elt->inlineEventListenerForType(eventNames().clickEvent) || elt->inlineEventListenerForType(eventNames().mousedownEvent) || elt->inlineEventListenerForType(eventNames().mouseupEvent)) + return static_cast<Element*>(elt); + } + + return 0; +} + +static Element* siblingWithAriaRole(String role, Node* node) +{ + Node* sibling = node->parent()->firstChild(); + while (sibling) { + if (sibling->isElementNode()) { + String siblingAriaRole = static_cast<Element*>(sibling)->getAttribute(roleAttr).string(); + if (equalIgnoringCase(siblingAriaRole, role)) + return static_cast<Element*>(sibling); + } + sibling = sibling->nextSibling(); + } + + return 0; +} + +Element* AccessibilityRenderObject::menuElementForMenuButton() const +{ + if (ariaRoleAttribute() != MenuButtonRole) + return 0; + + return siblingWithAriaRole("menu", renderer()->node()); +} + +AccessibilityObject* AccessibilityRenderObject::menuForMenuButton() const +{ + Element* menu = menuElementForMenuButton(); + if (menu && menu->renderer()) + return m_renderer->document()->axObjectCache()->get(menu->renderer()); + return 0; +} + +Element* AccessibilityRenderObject::menuItemElementForMenu() const +{ + if (ariaRoleAttribute() != MenuRole) + return 0; + + return siblingWithAriaRole("menuitem", renderer()->node()); +} + +AccessibilityObject* AccessibilityRenderObject::menuButtonForMenu() const +{ + Element* menuItem = menuItemElementForMenu(); + + if (menuItem && menuItem->renderer()) { + // ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem + AccessibilityObject* menuItemAX = m_renderer->document()->axObjectCache()->get(menuItem->renderer()); + if (menuItemAX->isMenuButton()) + return menuItemAX; + } + return 0; +} + +String AccessibilityRenderObject::helpText() const +{ + if (!m_renderer) + return String(); + + for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) { + if (curr->element() && curr->element()->isHTMLElement()) { + const AtomicString& summary = static_cast<Element*>(curr->element())->getAttribute(summaryAttr); + if (!summary.isEmpty()) + return summary; + const AtomicString& title = static_cast<Element*>(curr->element())->getAttribute(titleAttr); + if (!title.isEmpty()) + return title; + } + } + + return String(); +} + +String AccessibilityRenderObject::textUnderElement() const +{ + if (!m_renderer) + return String(); + + if (isFileUploadButton()) { + RenderFileUploadControl* uploadControl = static_cast<RenderFileUploadControl*>(m_renderer); + return uploadControl->buttonValue(); + } + + Node* node = m_renderer->element(); + if (node) { + if (Frame* frame = node->document()->frame()) { + // catch stale WebCoreAXObject (see <rdar://problem/3960196>) + if (frame->document() != node->document()) + return String(); + return plainText(rangeOfContents(node).get()); + } + } + + // return the null string for anonymous text because it is non-trivial to get + // the actual text and, so far, that is not needed + return String(); +} + +bool AccessibilityRenderObject::hasIntValue() const +{ + if (isHeading()) + return true; + + if (m_renderer->element() && isCheckboxOrRadio()) + return true; + + return false; +} + +int AccessibilityRenderObject::intValue() const +{ + if (!m_renderer || isPasswordField()) + return 0; + + if (isHeading()) + return headingLevel(m_renderer->element()); + + Node* node = m_renderer->element(); + if (!node || !isCheckboxOrRadio()) + return 0; + + // If this is an ARIA checkbox or radio, check the aria-checked attribute rather than node()->checked() + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) { + if (equalIgnoringCase(getAttribute(aria_checkedAttr).string(), "true")) + return true; + return false; + } + + return static_cast<HTMLInputElement*>(node)->checked(); +} + +float AccessibilityRenderObject::valueForRange() const +{ + if (!isProgressIndicator() && !isSlider()) + return 0.0f; + + return getAttribute(aria_valuenowAttr).toFloat(); +} + +float AccessibilityRenderObject::maxValueForRange() const +{ + if (!isProgressIndicator() && !isSlider()) + return 0.0f; + + return getAttribute(aria_valuemaxAttr).toFloat(); +} + +float AccessibilityRenderObject::minValueForRange() const +{ + if (!isProgressIndicator() && !isSlider()) + return 0.0f; + + return getAttribute(aria_valueminAttr).toFloat(); +} + +String AccessibilityRenderObject::stringValue() const +{ + if (!m_renderer || isPasswordField()) + return String(); + + if (m_renderer->isText()) + return textUnderElement(); + + if (m_renderer->isMenuList()) + return static_cast<RenderMenuList*>(m_renderer)->text(); + + if (m_renderer->isListMarker()) + return static_cast<RenderListMarker*>(m_renderer)->text(); + + if (isWebArea()) { + if (m_renderer->document()->frame()) + return String(); + + // FIXME: should use startOfDocument and endOfDocument (or rangeForDocument?) here + VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates(0, 0); + VisiblePosition endVisiblePosition = m_renderer->positionForCoordinates(INT_MAX, INT_MAX); + if (startVisiblePosition.isNull() || endVisiblePosition.isNull()) + return String(); + + return plainText(makeRange(startVisiblePosition, endVisiblePosition).get()); + } + + if (isTextControl()) + return text(); + + if (isFileUploadButton()) { + RenderFileUploadControl* uploadControl = static_cast<RenderFileUploadControl*>(m_renderer); + return uploadControl->fileTextValue(); + } + + // FIXME: We might need to implement a value here for more types + // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one; + // this would require subclassing or making accessibilityAttributeNames do something other than return a + // single static array. + return String(); +} + +// This function implements the ARIA accessible name as described by the Mozilla +// ARIA Implementer's Guide. +static String accessibleNameForNode(Node* node) +{ + if (node->isTextNode()) + return static_cast<Text*>(node)->data(); + + if (node->hasTagName(inputTag)) + return static_cast<HTMLInputElement*>(node)->value(); + + if (node->isHTMLElement()) { + const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr); + if (!alt.isEmpty()) + return alt; + } + + return String(); +} + +String AccessibilityRenderObject::ariaAccessiblityName(const String& s) const +{ + Document* document = m_renderer->document(); + if (!document) + return String(); + + String idList = s; + idList.replace('\n', ' '); + Vector<String> idVector; + idList.split(' ', idVector); + + Vector<UChar> ariaLabel; + unsigned size = idVector.size(); + for (unsigned i = 0; i < size; ++i) { + String idName = idVector[i]; + Element* idElement = document->getElementById(idName); + if (idElement) { + String nameFragment = accessibleNameForNode(idElement); + ariaLabel.append(nameFragment.characters(), nameFragment.length()); + for (Node* n = idElement->firstChild(); n; n = n->traverseNextNode(idElement->nextSibling())) { + nameFragment = accessibleNameForNode(n); + ariaLabel.append(nameFragment.characters(), nameFragment.length()); + } + ariaLabel.append(' '); + } + } + return String::adopt(ariaLabel); +} + +String AccessibilityRenderObject::ariaLabeledByAttribute() const +{ + Node* node = m_renderer->node(); + if (!node) + return String(); + + if (!node->isElementNode()) + return String(); + + // The ARIA spec uses the British spelling: "labelled." It seems prudent to support the American + // spelling ("labeled") as well. + String idList = getAttribute(aria_labeledbyAttr).string(); + if (idList.isEmpty()) { + idList = getAttribute(aria_labelledbyAttr).string(); + if (idList.isEmpty()) + return String(); + } + + return ariaAccessiblityName(idList); +} + +static HTMLLabelElement* labelForElement(Element* element) +{ + RefPtr<NodeList> list = element->document()->getElementsByTagName("label"); + unsigned len = list->length(); + for (unsigned i = 0; i < len; i++) { + if (list->item(i)->hasTagName(labelTag)) { + HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i)); + if (label->correspondingControl() == element) + return label; + } + } + + return 0; +} + +HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const +{ + if (!m_renderer) + return false; + + // the control element should not be considered part of the label + if (isControl()) + return false; + + // find if this has a parent that is a label + for (Node* parentNode = m_renderer->element(); parentNode; parentNode = parentNode->parentNode()) { + if (parentNode->hasTagName(labelTag)) + return static_cast<HTMLLabelElement*>(parentNode); + } + + return 0; +} + +String AccessibilityRenderObject::title() const +{ + AccessibilityRole ariaRole = ariaRoleAttribute(); + + if (!m_renderer) + return String(); + + Node* node = m_renderer->element(); + if (!node) + return String(); + + String ariaLabel = ariaLabeledByAttribute(); + if (!ariaLabel.isEmpty()) + return ariaLabel; + + const AtomicString& title = getAttribute(titleAttr); + if (!title.isEmpty()) + return title; + + bool isInputTag = node->hasTagName(inputTag); + if (isInputTag) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(node); + if (input->isTextButton()) + return input->value(); + } + + if (isInputTag || AccessibilityObject::isARIAInput(ariaRole) || isControl()) { + HTMLLabelElement* label = labelForElement(static_cast<Element*>(node)); + if (label) + return label->innerText(); + } + + if (roleValue() == ButtonRole + || ariaRole == ListBoxOptionRole + || ariaRole == MenuItemRole + || ariaRole == MenuButtonRole + || isHeading()) + return textUnderElement(); + + if (isLink()) + return textUnderElement(); + + return String(); +} + +String AccessibilityRenderObject::ariaDescribedByAttribute() const +{ + String idList = getAttribute(aria_describedbyAttr).string(); + if (idList.isEmpty()) + return String(); + + return ariaAccessiblityName(idList); +} + +String AccessibilityRenderObject::accessibilityDescription() const +{ + if (!m_renderer) + return String(); + + String ariaDescription = ariaDescribedByAttribute(); + if (!ariaDescription.isEmpty()) + return ariaDescription; + + if (isImage()) { + if (m_renderer->element() && m_renderer->element()->isHTMLElement()) { + const AtomicString& alt = static_cast<HTMLElement*>(m_renderer->element())->getAttribute(altAttr); + if (alt.isEmpty()) + return String(); + return alt; + } + } + + if (isWebArea()) { + Document *document = m_renderer->document(); + Node* owner = document->ownerElement(); + if (owner) { + if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) { + const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr); + if (!title.isEmpty()) + return title; + return static_cast<HTMLFrameElementBase*>(owner)->name(); + } + if (owner->isHTMLElement()) + return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr); + } + owner = document->body(); + if (owner && owner->isHTMLElement()) + return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr); + } + + if (roleValue() == DefinitionListTermRole) + return AXDefinitionListTermText(); + if (roleValue() == DefinitionListDefinitionRole) + return AXDefinitionListDefinitionText(); + + return String(); +} + +IntRect AccessibilityRenderObject::boundingBoxRect() const +{ + IntRect rect; + RenderObject* obj = m_renderer; + + if (!obj) + return IntRect(); + + if (obj->isInlineContinuation()) + obj = obj->element()->renderer(); + + // FIXME: This doesn't work correctly with transforms. + Vector<IntRect> rects; + int x, y; + obj->absolutePosition(x, y); + obj->absoluteRects(rects, x, y); + const size_t n = rects.size(); + for (size_t i = 0; i < n; ++i) { + IntRect r = rects[i]; + if (!r.isEmpty()) { + if (obj->style()->hasAppearance()) + theme()->adjustRepaintRect(obj, r); + rect.unite(r); + } + } + return rect; +} + +IntRect AccessibilityRenderObject::checkboxOrRadioRect() const +{ + if (!m_renderer) + return IntRect(); + + HTMLLabelElement* label = labelForElement(static_cast<Element*>(m_renderer->element())); + if (!label || !label->renderer()) + return boundingBoxRect(); + + IntRect labelRect = axObjectCache()->get(label->renderer())->elementRect(); + labelRect.unite(boundingBoxRect()); + return labelRect; +} + +IntRect AccessibilityRenderObject::elementRect() const +{ + // a checkbox or radio button should encompass its label + if (isCheckboxOrRadio()) + return checkboxOrRadioRect(); + + return boundingBoxRect(); +} + +IntSize AccessibilityRenderObject::size() const +{ + IntRect rect = elementRect(); + return rect.size(); +} + +AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const +{ + Element* element = anchorElement(); + if (!element) + return 0; + + // Right now, we do not support ARIA links as internal link elements + if (!element->hasTagName(aTag)) + return 0; + HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(element); + + KURL linkURL = anchor->href(); + String ref = linkURL.ref(); + if (ref.isEmpty()) + return 0; + + // check if URL is the same as current URL + linkURL.removeRef(); + if (m_renderer->document()->url() != linkURL) + return 0; + + Node* linkedNode = m_renderer->document()->getElementById(ref); + if (!linkedNode) { + linkedNode = m_renderer->document()->anchors()->namedItem(ref, !m_renderer->document()->inCompatMode()); + if (!linkedNode) + return 0; + } + + // the element we find may not be accessible, keep searching until we find a good one + AccessibilityObject* linkedAXElement = m_renderer->document()->axObjectCache()->get(linkedNode->renderer()); + while (linkedAXElement && linkedAXElement->accessibilityIsIgnored()) { + linkedNode = linkedNode->traverseNextNode(); + + while (linkedNode && !linkedNode->renderer()) + linkedNode = linkedNode->traverseNextSibling(); + + if (!linkedNode) + return 0; + linkedAXElement = m_renderer->document()->axObjectCache()->get(linkedNode->renderer()); + } + + return linkedAXElement; +} + +void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const +{ + if (!m_renderer || roleValue() != RadioButtonRole) + return; + + Node* node = m_renderer->node(); + if (!node || !node->hasTagName(inputTag)) + return; + + HTMLInputElement* input = static_cast<HTMLInputElement*>(node); + // if there's a form, then this is easy + if (input->form()) { + Vector<RefPtr<Node> > formElements; + input->form()->getNamedElements(input->name(), formElements); + + unsigned len = formElements.size(); + for (unsigned i = 0; i < len; ++i) { + Node* associateElement = formElements[i].get(); + if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->get(associateElement->renderer())) + linkedUIElements.append(object); + } + } else { + RefPtr<NodeList> list = node->document()->getElementsByTagName("input"); + unsigned len = list->length(); + for (unsigned i = 0; i < len; ++i) { + if (list->item(i)->hasTagName(inputTag)) { + HTMLInputElement* associateElement = static_cast<HTMLInputElement*>(list->item(i)); + if (associateElement->isRadioButton() && associateElement->name() == input->name()) { + if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->get(associateElement->renderer())) + linkedUIElements.append(object); + } + } + } + } +} + +// linked ui elements could be all the related radio buttons in a group +// or an internal anchor connection +void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const +{ + if (isAnchor()) { + AccessibilityObject* linkedAXElement = internalLinkElement(); + if (linkedAXElement) + linkedUIElements.append(linkedAXElement); + } + + if (roleValue() == RadioButtonRole) + addRadioButtonGroupMembers(linkedUIElements); +} + +AccessibilityObject* AccessibilityRenderObject::titleUIElement() const +{ + if (!m_renderer) + return 0; + + // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset + if (isFieldset()) + return axObjectCache()->get(static_cast<RenderFieldset*>(m_renderer)->findLegend()); + + // checkbox and radio hide their labels. Only controls get titleUIElements for now + if (isCheckboxOrRadio() || !isControl()) + return 0; + + Node* element = m_renderer->element(); + HTMLLabelElement* label = labelForElement(static_cast<Element*>(element)); + if (label && label->renderer()) + return axObjectCache()->get(label->renderer()); + + return 0; +} + +bool AccessibilityRenderObject::accessibilityIsIgnored() const +{ + // ignore invisible element + if (!m_renderer || m_renderer->style()->visibility() != VISIBLE) + return true; + + if (isPresentationalChildOfAriaRole()) + return true; + + // ignore popup menu items because AppKit does + for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) { + if (parent->isMenuList()) + return true; + } + + // find out if this element is inside of a label element. + // if so, it may be ignored because it's the label for a checkbox or radio button + HTMLLabelElement* labelElement = labelElementContainer(); + if (labelElement) { + HTMLElement* correspondingControl = labelElement->correspondingControl(); + if (correspondingControl && correspondingControl->renderer()) { + AccessibilityObject* controlObject = axObjectCache()->get(correspondingControl->renderer()); + if (controlObject->isCheckboxOrRadio()) + return true; + } + } + + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole == TextAreaRole || ariaRole == StaticTextRole) { + String ariaText = text(); + return ariaText.isNull() || ariaText.isEmpty(); + } + + // NOTE: BRs always have text boxes now, so the text box check here can be removed + if (m_renderer->isText()) { + // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level + if (parentObjectUnignored()->ariaRoleAttribute() == MenuItemRole || + parentObjectUnignored()->ariaRoleAttribute() == MenuButtonRole) + return true; + return m_renderer->isBR() || !static_cast<RenderText*>(m_renderer)->firstTextBox(); + } + + if (isHeading()) + return false; + + if (isLink()) + return false; + + // all controls are accessible + if (isControl()) + return false; + + // don't ignore labels, because they serve as TitleUIElements + Node* node = m_renderer->element(); + if (node && node->hasTagName(labelTag)) + return false; + + if (m_renderer->isBlockFlow() && m_renderer->childrenInline()) + return !static_cast<RenderBlock*>(m_renderer)->firstLineBox() && !mouseButtonListener(); + + // ignore images seemingly used as spacers + if (isImage()) { + if (node && node->isElementNode()) { + Element* elt = static_cast<Element*>(node); + const AtomicString& alt = elt->getAttribute(altAttr); + // don't ignore an image that has an alt tag + if (!alt.isEmpty()) + return false; + // informal standard is to ignore images with zero-length alt strings + if (!alt.isNull()) + return true; + } + + // check for one-dimensional image + if (m_renderer->height() <= 1 || m_renderer->width() <= 1) + return true; + + // check whether rendered image was stretched from one-dimensional file image + if (isNativeImage()) { + RenderImage* image = static_cast<RenderImage*>(m_renderer); + if (image->cachedImage()) { + IntSize imageSize = image->cachedImage()->imageSize(image->view()->zoomFactor()); + return imageSize.height() <= 1 || imageSize.width() <= 1; + } + } + return false; + } + + if (ariaRole != UnknownRole) + return false; + + // make a platform-specific decision + if (isAttachment()) + return accessibilityIgnoreAttachment(); + + return !m_renderer->isListMarker() && !isWebArea(); +} + +bool AccessibilityRenderObject::isLoaded() const +{ + return !m_renderer->document()->tokenizer(); +} + +int AccessibilityRenderObject::layoutCount() const +{ + if (!m_renderer->isRenderView()) + return 0; + return static_cast<RenderView*>(m_renderer)->frameView()->layoutCount(); +} + +String AccessibilityRenderObject::text() const +{ + if (!isTextControl() || isPasswordField()) + return String(); + + if (isNativeTextControl()) + return static_cast<RenderTextControl*>(m_renderer)->text(); + + Node* node = m_renderer->element(); + if (!node) + return String(); + if (!node->isElementNode()) + return String(); + + return static_cast<Element*>(node)->innerText(); +} + +int AccessibilityRenderObject::textLength() const +{ + ASSERT(isTextControl()); + + if (isPasswordField()) + return -1; // need to return something distinct from 0 + + return text().length(); +} + +PassRefPtr<Range> AccessibilityRenderObject::ariaSelectedTextDOMRange() const +{ + Node* node = m_renderer->element(); + if (!node) + return 0; + + RefPtr<Range> currentSelectionRange = selection().toRange(); + if (!currentSelectionRange) + return 0; + + ExceptionCode ec = 0; + if (!currentSelectionRange->intersectsNode(node, ec)) + return Range::create(currentSelectionRange->ownerDocument()); + + RefPtr<Range> ariaRange = rangeOfContents(node); + Position startPosition, endPosition; + + // Find intersection of currentSelectionRange and ariaRange + if (ariaRange->startOffset() > currentSelectionRange->startOffset()) + startPosition = ariaRange->startPosition(); + else + startPosition = currentSelectionRange->startPosition(); + + if (ariaRange->endOffset() < currentSelectionRange->endOffset()) + endPosition = ariaRange->endPosition(); + else + endPosition = currentSelectionRange->endPosition(); + + return Range::create(ariaRange->ownerDocument(), startPosition, endPosition); +} + +String AccessibilityRenderObject::selectedText() const +{ + ASSERT(isTextControl()); + + if (isPasswordField()) + return String(); // need to return something distinct from empty string + + if (isNativeTextControl()) { + RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); + return textControl->text().substring(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart()); + } + + if (ariaRoleAttribute() == UnknownRole) + return String(); + + RefPtr<Range> ariaRange = ariaSelectedTextDOMRange(); + if (!ariaRange) + return String(); + return ariaRange->text(); +} + +const AtomicString& AccessibilityRenderObject::accessKey() const +{ + Node* node = m_renderer->element(); + if (!node) + return nullAtom; + if (!node->isElementNode()) + return nullAtom; + return static_cast<Element*>(node)->getAttribute(accesskeyAttr); +} + +Selection AccessibilityRenderObject::selection() const +{ + return m_renderer->document()->frame()->selection()->selection(); +} + +PlainTextRange AccessibilityRenderObject::selectedTextRange() const +{ + ASSERT(isTextControl()); + + if (isPasswordField()) + return PlainTextRange(); + + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (isNativeTextControl() && ariaRole == UnknownRole) { + RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); + return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart()); + } + + if (ariaRole == UnknownRole) + return PlainTextRange(); + + RefPtr<Range> ariaRange = ariaSelectedTextDOMRange(); + if (!ariaRange) + return PlainTextRange(); + return PlainTextRange(ariaRange->startOffset(), ariaRange->endOffset()); +} + +void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range) +{ + if (isNativeTextControl()) { + RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer); + textControl->setSelectionRange(range.start, range.start + range.length); + return; + } + + Document* document = m_renderer->document(); + if (!document) + return; + Frame* frame = document->frame(); + if (!frame) + return; + Node* node = m_renderer->element(); + frame->selection()->setSelection(Selection(Position(node, range.start), + Position(node, range.start + range.length), DOWNSTREAM)); +} + +KURL AccessibilityRenderObject::url() const +{ + if (isAnchor() && m_renderer->element()->hasTagName(aTag)) { + if (HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(anchorElement())) + return anchor->href(); + } + + if (isWebArea()) + return m_renderer->document()->url(); + + if (isImage() && m_renderer->element() && m_renderer->element()->hasTagName(imgTag)) + return static_cast<HTMLImageElement*>(m_renderer->element())->src(); + + return KURL(); +} + +bool AccessibilityRenderObject::isVisited() const +{ + return m_renderer->style()->pseudoState() == PseudoVisited; +} + +bool AccessibilityRenderObject::isSelected() const +{ + if (!m_renderer) + return false; + + Node* node = m_renderer->node(); + if (!node) + return false; + + return false; +} + +bool AccessibilityRenderObject::isFocused() const +{ + if (!m_renderer) + return false; + + Document* document = m_renderer->document(); + if (!document) + return false; + + Node* focusedNode = document->focusedNode(); + if (!focusedNode) + return false; + + // A web area is represented by the Document node in the DOM tree, which isn't focusable. + // Check instead if the frame's selection controller is focused + if (focusedNode == m_renderer->element() || + (roleValue() == WebAreaRole && document->frame()->selection()->isFocusedAndActive())) + return true; + + return false; +} + +void AccessibilityRenderObject::setFocused(bool on) +{ + if (!canSetFocusAttribute()) + return; + + if (!on) + m_renderer->document()->setFocusedNode(0); + else { + if (m_renderer->element()->isElementNode()) + static_cast<Element*>(m_renderer->element())->focus(); + else + m_renderer->document()->setFocusedNode(m_renderer->element()); + } +} + +void AccessibilityRenderObject::setValue(const String& string) +{ + // FIXME: Do we want to do anything here for ARIA textboxes? + if (m_renderer->isTextField()) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element()); + input->setValue(string); + } else if (m_renderer->isTextArea()) { + HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->element()); + textArea->setValue(string); + } +} + +bool AccessibilityRenderObject::isEnabled() const +{ + return m_renderer->element() ? m_renderer->element()->isEnabled() : true; +} + +RenderObject* AccessibilityRenderObject::topRenderer() const +{ + return m_renderer->document()->topDocument()->renderer(); +} + +Document* AccessibilityRenderObject::document() const +{ + return m_renderer->document(); +} + +FrameView* AccessibilityRenderObject::topDocumentFrameView() const +{ + return topRenderer()->view()->frameView(); +} + +Widget* AccessibilityRenderObject::widget() const +{ + if (!m_renderer->isWidget()) + return 0; + + return static_cast<RenderWidget*>(m_renderer)->widget(); +} + +AXObjectCache* AccessibilityRenderObject::axObjectCache() const +{ + return m_renderer->document()->axObjectCache(); +} + +AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const +{ + // find an image that is using this map + if (!m_renderer || !map) + return 0; + + RefPtr<HTMLCollection> coll = m_renderer->document()->images(); + for (Node* curr = coll->firstItem(); curr; curr = coll->nextItem()) { + RenderObject* obj = curr->renderer(); + if (!obj || !curr->hasTagName(imgTag)) + continue; + + // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning, + // which has to be stripped off + if (static_cast<HTMLImageElement*>(curr)->useMap().substring(1) == map->getName()) + return axObjectCache()->get(obj); + } + + return 0; +} + +void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result) +{ + Document* document = m_renderer->document(); + RefPtr<HTMLCollection> coll = document->links(); + Node* curr = coll->firstItem(); + while (curr) { + RenderObject* obj = curr->renderer(); + if (obj) { + RefPtr<AccessibilityObject> axobj = document->axObjectCache()->get(obj); + ASSERT(axobj); + ASSERT(axobj->roleValue() == WebCoreLinkRole); + if (!axobj->accessibilityIsIgnored()) + result.append(axobj); + } else { + Node* parent = curr->parent(); + if (parent && curr->hasTagName(areaTag) && parent->hasTagName(mapTag)) { + AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->get(ImageMapLinkRole)); + areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(curr)); + areaObject->setHTMLMapElement(static_cast<HTMLMapElement*>(parent)); + areaObject->setParent(accessibilityParentForImageMap(static_cast<HTMLMapElement*>(parent))); + + result.append(areaObject); + } + } + curr = coll->nextItem(); + } +} + +FrameView* AccessibilityRenderObject::documentFrameView() const +{ + if (!m_renderer || !m_renderer->document()) + return 0; + + // this is the RenderObject's Document's Frame's FrameView + return m_renderer->document()->view(); +} + +Widget* AccessibilityRenderObject::widgetForAttachmentView() const +{ + if (!isAttachment()) + return 0; + return static_cast<RenderWidget*>(m_renderer)->widget(); +} + +FrameView* AccessibilityRenderObject::frameViewIfRenderView() const +{ + if (!m_renderer->isRenderView()) + return 0; + // this is the RenderObject's Document's renderer's FrameView + return m_renderer->view()->frameView(); +} + +// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns +// a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file +VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const +{ + if (!m_renderer) + return VisiblePositionRange(); + + // construct VisiblePositions for start and end + Node* node = m_renderer->element(); + if (!node) + return VisiblePositionRange(); + + VisiblePosition startPos = VisiblePosition(node, 0, VP_DEFAULT_AFFINITY); + VisiblePosition endPos = VisiblePosition(node, maxDeepOffset(node), VP_DEFAULT_AFFINITY); + + // the VisiblePositions are equal for nodes like buttons, so adjust for that + if (startPos == endPos) { + endPos = endPos.next(); + if (endPos.isNull()) + endPos = startPos; + } + + return VisiblePositionRange(startPos, endPos); +} + +VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const +{ + if (lineCount == 0 || !m_renderer) + return VisiblePositionRange(); + + // iterate over the lines + // FIXME: this is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the + // last offset of the last line + VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForCoordinates(0, 0); + VisiblePosition savedVisiblePos; + while (--lineCount != 0) { + savedVisiblePos = visiblePos; + visiblePos = nextLinePosition(visiblePos, 0); + if (visiblePos.isNull() || visiblePos == savedVisiblePos) + return VisiblePositionRange(); + } + + // make a caret selection for the marker position, then extend it to the line + // NOTE: ignores results of sel.modify because it returns false when + // starting at an empty line. The resulting selection in that case + // will be a caret at visiblePos. + SelectionController selection; + selection.setSelection(Selection(visiblePos)); + selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary); + + return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd()); +} + +VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const +{ + if (!m_renderer) + return VisiblePosition(); + + if (isNativeTextControl()) + return static_cast<RenderTextControl*>(m_renderer)->visiblePositionForIndex(index); + + if (!isTextControl() && !m_renderer->isText()) + return VisiblePosition(); + + Node* node = m_renderer->node(); + if (!node) + return VisiblePosition(); + + if (index <= 0) + return VisiblePosition(node, 0, DOWNSTREAM); + + ExceptionCode ec = 0; + RefPtr<Range> range = Range::create(m_renderer->document()); + range->selectNodeContents(node, ec); + CharacterIterator it(range.get()); + it.advance(index - 1); + return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM); +} + +int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const +{ + if (isNativeTextControl()) + return static_cast<RenderTextControl*>(m_renderer)->indexForVisiblePosition(pos); + + if (!isTextControl()) + return 0; + + Node* node = m_renderer->node(); + if (!node) + return 0; + + Position indexPosition = pos.deepEquivalent(); + if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != node) + return 0; + + ExceptionCode ec = 0; + RefPtr<Range> range = Range::create(m_renderer->document()); + range->setStart(node, 0, ec); + range->setEnd(indexPosition.node(), indexPosition.offset(), ec); + return TextIterator::rangeLength(range.get()); +} + +IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const +{ + if (visiblePositionRange.isNull()) + return IntRect(); + + // Create a mutable VisiblePositionRange. + VisiblePositionRange range(visiblePositionRange); + IntRect rect1 = range.start.caretRect(); + IntRect rect2 = range.end.caretRect(); + + // readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds + if (rect2.y() != rect1.y()) { + VisiblePosition endOfFirstLine = endOfLine(range.start); + if (range.start == endOfFirstLine) { + range.start.setAffinity(DOWNSTREAM); + rect1 = range.start.caretRect(); + } + if (range.end == endOfFirstLine) { + range.end.setAffinity(UPSTREAM); + rect2 = range.end.caretRect(); + } + } + + IntRect ourrect = rect1; + ourrect.unite(rect2); + + // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead + if (rect1.bottom() != rect2.bottom()) { + RefPtr<Range> dataRange = makeRange(range.start, range.end); + IntRect boundingBox = dataRange->boundingBox(); + String rangeString = plainText(dataRange.get()); + if (rangeString.length() > 1 && !boundingBox.isEmpty()) + ourrect = boundingBox; + } + +#if PLATFORM(MAC) + return m_renderer->document()->view()->contentsToScreen(ourrect); +#else + return ourrect; +#endif +} + +void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const +{ + if (range.start.isNull() || range.end.isNull()) + return; + + // make selection and tell the document to use it. if it's zero length, then move to that position + if (range.start == range.end) { + m_renderer->document()->frame()->selection()->moveTo(range.start, true); + } + else { + Selection newSelection = Selection(range.start, range.end); + m_renderer->document()->frame()->selection()->setSelection(newSelection); + } +} + +VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const +{ + // convert absolute point to view coordinates + FrameView* frameView = m_renderer->document()->topDocument()->renderer()->view()->frameView(); + RenderObject* renderer = topRenderer(); + Node* innerNode = 0; + + // locate the node containing the point + IntPoint pointResult; + while (1) { + IntPoint ourpoint; +#if PLATFORM(MAC) + ourpoint = frameView->screenToContents(point); +#else + ourpoint = point; +#endif + HitTestRequest request(true, true); + HitTestResult result(ourpoint); + renderer->layer()->hitTest(request, result); + innerNode = result.innerNode(); + if (!innerNode || !innerNode->renderer()) + return VisiblePosition(); + + pointResult = result.localPoint(); + + // done if hit something other than a widget + renderer = innerNode->renderer(); + if (!renderer->isWidget()) + break; + + // descend into widget (FRAME, IFRAME, OBJECT...) + Widget* widget = static_cast<RenderWidget*>(renderer)->widget(); + if (!widget || !widget->isFrameView()) + break; + Frame* frame = static_cast<FrameView*>(widget)->frame(); + if (!frame) + break; + Document* document = frame->document(); + if (!document) + break; + renderer = document->renderer(); + frameView = static_cast<FrameView*>(widget); + } + + return innerNode->renderer()->positionForPoint(pointResult); +} + +// NOTE: Consider providing this utility method as AX API +VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const +{ + if (!isTextControl()) + return VisiblePosition(); + + // lastIndexOK specifies whether the position after the last character is acceptable + if (indexValue >= text().length()) { + if (!lastIndexOK || indexValue > text().length()) + return VisiblePosition(); + } + VisiblePosition position = visiblePositionForIndex(indexValue); + position.setAffinity(DOWNSTREAM); + return position; +} + +// NOTE: Consider providing this utility method as AX API +int AccessibilityRenderObject::index(const VisiblePosition& position) const +{ + if (!isTextControl()) + return -1; + + Node* node = position.deepEquivalent().node(); + if (!node) + return -1; + + for (RenderObject* renderer = node->renderer(); renderer && renderer->element(); renderer = renderer->parent()) { + if (renderer == m_renderer) + return indexForVisiblePosition(position); + } + + return -1; +} + +// Given a line number, the range of characters of the text associated with this accessibility +// object that contains the line number. +PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const +{ + if (!isTextControl()) + return PlainTextRange(); + + // iterate to the specified line + VisiblePosition visiblePos = visiblePositionForIndex(0); + VisiblePosition savedVisiblePos; + for (unsigned lineCount = lineNumber; lineCount != 0; lineCount -= 1) { + savedVisiblePos = visiblePos; + visiblePos = nextLinePosition(visiblePos, 0); + if (visiblePos.isNull() || visiblePos == savedVisiblePos) + return PlainTextRange(); + } + + // make a caret selection for the marker position, then extend it to the line + // NOTE: ignores results of selection.modify because it returns false when + // starting at an empty line. The resulting selection in that case + // will be a caret at visiblePos. + SelectionController selection; + selection.setSelection(Selection(visiblePos)); + selection.modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary); + selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary); + + // calculate the indices for the selection start and end + VisiblePosition startPosition = selection.selection().visibleStart(); + VisiblePosition endPosition = selection.selection().visibleEnd(); + int index1 = indexForVisiblePosition(startPosition); + int index2 = indexForVisiblePosition(endPosition); + + // add one to the end index for a line break not caused by soft line wrap (to match AppKit) + if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull()) + index2 += 1; + + // return nil rather than an zero-length range (to match AppKit) + if (index1 == index2) + return PlainTextRange(); + + return PlainTextRange(index1, index2 - index1); +} + +// The composed character range in the text associated with this accessibility object that +// is specified by the given index value. This parameterized attribute returns the complete +// range of characters (including surrogate pairs of multi-byte glyphs) at the given index. +PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const +{ + if (!isTextControl()) + return PlainTextRange(); + + String elementText = text(); + if (!elementText.length() || index > elementText.length() - 1) + return PlainTextRange(); + + return PlainTextRange(index, 1); +} + +// A substring of the text associated with this accessibility object that is +// specified by the given character range. +String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const +{ + if (isPasswordField()) + return String(); + + if (range.length == 0) + return ""; + + if (!isTextControl()) + return String(); + + String elementText = text(); + if (range.start + range.length > elementText.length()) + return String(); + + return elementText.substring(range.start, range.length); +} + +// The bounding rectangle of the text associated with this accessibility object that is +// specified by the given range. This is the bounding rectangle a sighted user would see +// on the display screen, in pixels. +IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const +{ + if (isTextControl()) + return boundsForVisiblePositionRange(visiblePositionRangeForRange(range)); + return IntRect(); +} + +AccessibilityObject* AccessibilityRenderObject::doAccessibilityHitTest(const IntPoint& point) const +{ + if (!m_renderer) + return 0; + + RenderLayer* layer = m_renderer->layer(); + if (!layer) + return 0; + + HitTestRequest request(true, true); + HitTestResult hitTestResult = HitTestResult(point); + layer->hitTest(request, hitTestResult); + if (!hitTestResult.innerNode()) + return 0; + Node* node = hitTestResult.innerNode()->shadowAncestorNode(); + RenderObject* obj = node->renderer(); + if (!obj) + return 0; + + AccessibilityObject *result = obj->document()->axObjectCache()->get(obj); + + if (obj->isListBox()) + return static_cast<AccessibilityListBox*>(result)->doAccessibilityHitTest(point); + + if (result->accessibilityIsIgnored()) + result = result->parentObjectUnignored(); + + return result; +} + +AccessibilityObject* AccessibilityRenderObject::focusedUIElement() const +{ + // get the focused node in the page + Page* page = m_renderer->document()->page(); + if (!page) + return 0; + + Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document(); + Node* focusedNode = focusedDocument->focusedNode(); + if (!focusedNode) + focusedNode = focusedDocument; + + RenderObject* focusedNodeRenderer = focusedNode->renderer(); + if (!focusedNodeRenderer) + return 0; + + AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->get(focusedNodeRenderer); + + if (obj->shouldFocusActiveDescendant()) { + if (AccessibilityObject* descendant = obj->activeDescendant()) + obj = descendant; + } + + // the HTML element, for example, is focusable but has an AX object that is ignored + if (obj->accessibilityIsIgnored()) + obj = obj->parentObjectUnignored(); + + return obj; +} + +bool AccessibilityRenderObject::shouldFocusActiveDescendant() const +{ + switch (ariaRoleAttribute()) { + case GroupRole: + case ComboBoxRole: + case ListBoxRole: + case MenuRole: + case MenuBarRole: + case RadioGroupRole: + case RowRole: + case PopUpButtonRole: + case ProgressIndicatorRole: + case ToolbarRole: + case OutlineRole: + /* FIXME: replace these with actual roles when they are added to AccessibilityRole + composite + alert + alertdialog + grid + status + timer + tree + */ + return true; + default: + return false; + } +} + +AccessibilityObject* AccessibilityRenderObject::activeDescendant() const +{ + if (renderer()->element() && !renderer()->element()->isElementNode()) + return 0; + Element* element = static_cast<Element*>(renderer()->element()); + + String activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr).string(); + if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty()) + return 0; + + Element* target = renderer()->document()->getElementById(activeDescendantAttrStr); + AccessibilityObject* obj = renderer()->document()->axObjectCache()->get(target->renderer()); + if (obj->isAccessibilityRenderObject()) + // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification + return obj; + return 0; +} + + +void AccessibilityRenderObject::handleActiveDescendantChanged() +{ + Element* element = static_cast<Element*>(renderer()->element()); + if (!element) + return; + Document* doc = renderer()->document(); + if (!doc->frame()->selection()->isFocusedAndActive() || doc->focusedNode() != element) + return; + AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant()); + + if (activedescendant && shouldFocusActiveDescendant()) + doc->axObjectCache()->postNotificationToElement(activedescendant->renderer(), "AXFocusedUIElementChanged"); +} + + +AccessibilityObject* AccessibilityRenderObject::observableObject() const +{ + for (RenderObject* renderer = m_renderer; renderer && renderer->element(); renderer = renderer->parent()) { + if (renderer->isTextField() || renderer->isTextArea()) + return renderer->document()->axObjectCache()->get(renderer); + } + + return 0; +} + +typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap; + +static const ARIARoleMap& createARIARoleMap() +{ + struct RoleEntry { + String ariaRole; + AccessibilityRole webcoreRole; + }; + + static const RoleEntry roles[] = { + { "button", ButtonRole }, + { "checkbox", CheckBoxRole }, + { "group", GroupRole }, + { "heading", HeadingRole }, + { "img", ImageRole }, + { "link", WebCoreLinkRole }, + { "listbox", ListBoxRole }, + // "option" isn't here because it may map to different roles depending on the parent element's role + { "menu", MenuRole }, + { "menubar", GroupRole }, + // "menuitem" isn't here because it may map to different roles depending on the parent element's role + { "menuitemcheckbox", MenuItemRole }, + { "menuitemradio", MenuItemRole }, + { "progressbar", ProgressIndicatorRole }, + { "radio", RadioButtonRole }, + { "range", SliderRole }, + { "slider", SliderRole }, + { "spinbutton", ProgressIndicatorRole }, + { "textbox", TextAreaRole } + }; + ARIARoleMap& roleMap = *new ARIARoleMap; + + const unsigned numRoles = sizeof(roles) / sizeof(roles[0]); + for (unsigned i = 0; i < numRoles; ++i) + roleMap.set(roles[i].ariaRole, roles[i].webcoreRole); + return roleMap; +} + +static AccessibilityRole ariaRoleToWebCoreRole(String value) +{ + ASSERT(!value.isEmpty() && !value.isNull()); + static const ARIARoleMap& roleMap = createARIARoleMap(); + return roleMap.get(value); +} + +AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const +{ + String ariaRole = getAttribute(roleAttr).string(); + if (ariaRole.isNull() || ariaRole.isEmpty()) + return UnknownRole; + + AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole); + if (role) + return role; + // selects and listboxes both have options as child roles, but they map to different roles within WebCore + if (equalIgnoringCase(ariaRole,"option")) { + if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole) + return MenuItemRole; + if (parentObjectUnignored()->ariaRoleAttribute() == ListBoxRole) + return ListBoxOptionRole; + } + // an aria "menuitem" may map to MenuButton or MenuItem depending on its parent + if (equalIgnoringCase(ariaRole,"menuitem")) { + if (parentObjectUnignored()->ariaRoleAttribute() == GroupRole) + return MenuButtonRole; + if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole) + return MenuItemRole; + } + + return UnknownRole; +} + +void AccessibilityRenderObject::setAriaRole() +{ + m_ariaRole = determineAriaRoleAttribute(); +} + +AccessibilityRole AccessibilityRenderObject::ariaRoleAttribute() const +{ + return m_ariaRole; +} + +AccessibilityRole AccessibilityRenderObject::roleValue() const +{ + if (!m_renderer) + return UnknownRole; + + Node* node = m_renderer->element(); + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole != UnknownRole) + return ariaRole; + + if (node && node->isLink()) { + if (m_renderer->isImage()) + return ImageMapRole; + return WebCoreLinkRole; + } + if (m_renderer->isListMarker()) + return ListMarkerRole; + if (node && node->hasTagName(buttonTag)) + return ButtonRole; + if (m_renderer->isText()) + return StaticTextRole; + if (m_renderer->isImage()) { + if (node && node->hasTagName(inputTag)) + return ButtonRole; + return ImageRole; + } + if (m_renderer->isRenderView()) + return WebAreaRole; + + if (m_renderer->isTextField()) + return TextFieldRole; + + if (m_renderer->isTextArea()) + return TextAreaRole; + + if (node && node->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(node); + if (input->inputType() == HTMLInputElement::CHECKBOX) + return CheckBoxRole; + if (input->inputType() == HTMLInputElement::RADIO) + return RadioButtonRole; + if (input->isTextButton()) + return ButtonRole; + } + + if (node && node->hasTagName(buttonTag)) + return ButtonRole; + + if (isFileUploadButton()) + return ButtonRole; + + if (m_renderer->isMenuList()) + return PopUpButtonRole; + + if (headingLevel(m_renderer->element()) != 0) + return HeadingRole; + + if (node && node->hasTagName(ddTag)) + return DefinitionListDefinitionRole; + + if (node && node->hasTagName(dtTag)) + return DefinitionListTermRole; + + if (m_renderer->isBlockFlow() || (node && node->hasTagName(labelTag))) + return GroupRole; + + return UnknownRole; +} + +bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const +{ + // Walk the parent chain looking for a parent that has presentational children + AccessibilityObject* parent; + for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject()) + ; + return parent; +} + +bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const +{ + switch (m_ariaRole) { + case ButtonRole: + case SliderRole: + case ImageRole: + case ProgressIndicatorRole: + //case SeparatorRole: + return true; + default: + return false; + } +} + +bool AccessibilityRenderObject::canSetFocusAttribute() const +{ + // NOTE: It would be more accurate to ask the document whether setFocusedNode() would + // do anything. For example, it setFocusedNode() will do nothing if the current focused + // node will not relinquish the focus. + if (!m_renderer->element() || !m_renderer->element()->isEnabled()) + return false; + + switch (roleValue()) { + case WebCoreLinkRole: + case ImageMapLinkRole: + case TextFieldRole: + case TextAreaRole: + case ButtonRole: + case PopUpButtonRole: + case CheckBoxRole: + case RadioButtonRole: + return true; + default: + return false; + } +} + +bool AccessibilityRenderObject::canSetValueAttribute() const +{ + if (isWebArea()) + return !isReadOnly(); + + return isTextControl() || isProgressIndicator() || isSlider(); +} + +bool AccessibilityRenderObject::canSetTextRangeAttributes() const +{ + return isTextControl(); +} + +void AccessibilityRenderObject::childrenChanged() +{ + clearChildren(); + + if (accessibilityIsIgnored()) { + AccessibilityObject* parent = parentObject(); + if (parent) + parent->childrenChanged(); + } +} + +bool AccessibilityRenderObject::canHaveChildren() const +{ + if (!m_renderer) + return false; + + // Elements that should not have children + switch (roleValue()) { + case ImageRole: + case ButtonRole: + case PopUpButtonRole: + case CheckBoxRole: + case RadioButtonRole: + return false; + default: + return true; + } +} + +const AccessibilityObject::AccessibilityChildrenVector& AccessibilityRenderObject::children() +{ + if (!m_haveChildren) + addChildren(); + return m_children; +} + +void AccessibilityRenderObject::addChildren() +{ + // If the need to add more children in addition to existing children arises, + // childrenChanged should have been called, leaving the object with no children. + ASSERT(!m_haveChildren); + + // nothing to add if there is no RenderObject + if (!m_renderer) + return; + + m_haveChildren = true; + + if (!canHaveChildren()) + return; + + // add all unignored acc children + for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) { + if (obj->accessibilityIsIgnored()) { + if (!obj->hasChildren()) + obj->addChildren(); + AccessibilityChildrenVector children = obj->children(); + unsigned length = children.size(); + for (unsigned i = 0; i < length; ++i) + m_children.append(children[i]); + } else + m_children.append(obj); + } + + // for a RenderImage, add the <area> elements as individual accessibility objects + if (m_renderer->isRenderImage()) { + HTMLMapElement* map = static_cast<RenderImage*>(m_renderer)->imageMap(); + if (map) { + for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) { + + // add an <area> element for this child if it has a link + if (current->isLink()) { + AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(m_renderer->document()->axObjectCache()->get(ImageMapLinkRole)); + areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(current)); + areaObject->setHTMLMapElement(map); + areaObject->setParent(this); + + m_children.append(areaObject); + } + } + } + } +} + +void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result) +{ + AccessibilityObject* child = firstChild(); + bool isMultiselectable = false; + + Element* element = static_cast<Element*>(renderer()->element()); + if (!element || !element->isElementNode()) // do this check to ensure safety of static_cast above + return; + + String multiselectablePropertyStr = element->getAttribute("aria-multiselectable").string(); + isMultiselectable = equalIgnoringCase(multiselectablePropertyStr, "true"); + + while (child) { + // every child should have aria-role option, and if so, check for selected attribute/state + AccessibilityRole ariaRole = child->ariaRoleAttribute(); + RenderObject* childRenderer = 0; + if (child->isAccessibilityRenderObject()) + childRenderer = static_cast<AccessibilityRenderObject*>(child)->renderer(); + if (childRenderer && ariaRole == ListBoxOptionRole) { + Element* childElement = static_cast<Element*>(childRenderer->element()); + if (childElement && childElement->isElementNode()) { // do this check to ensure safety of static_cast above + String selectedAttrString = childElement->getAttribute("aria-selected").string(); + if (equalIgnoringCase(selectedAttrString, "true")) { + result.append(child); + if (isMultiselectable) + return; + } + } + } + child = child->nextSibling(); + } +} + +void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result) +{ + ASSERT(result.isEmpty()); + + // only listboxes should be asked for their selected children. + if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes + ASSERT_NOT_REACHED(); + return; + } + return ariaListboxSelectedChildren(result); +} + +void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result) +{ + if (!hasChildren()) + addChildren(); + + unsigned length = m_children.size(); + for (unsigned i = 0; i < length; i++) { + if (!m_children[i]->isOffScreen()) + result.append(m_children[i]); + } +} + +void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result) +{ + ASSERT(result.isEmpty()); + + // only listboxes are asked for their visible children. + if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes + ASSERT_NOT_REACHED(); + return; + } + return ariaListboxVisibleChildren(result); +} + +void AccessibilityRenderObject::removeAXObjectID() +{ + if (!m_id) + return; +#if PLATFORM(MAC) + m_renderer->document()->axObjectCache()->removeAXID(this); +#endif +} + +const String& AccessibilityRenderObject::actionVerb() const +{ + // FIXME: Need to add verbs for select elements. + static const String buttonAction = AXButtonActionVerb(); + static const String textFieldAction = AXTextFieldActionVerb(); + static const String radioButtonAction = AXRadioButtonActionVerb(); + static const String checkedCheckBoxAction = AXCheckedCheckBoxActionVerb(); + static const String uncheckedCheckBoxAction = AXUncheckedCheckBoxActionVerb(); + static const String linkAction = AXLinkActionVerb(); + static const String noAction; + + switch (roleValue()) { + case ButtonRole: + return buttonAction; + case TextFieldRole: + case TextAreaRole: + return textFieldAction; + case RadioButtonRole: + return radioButtonAction; + case CheckBoxRole: + return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction; + case LinkRole: + case WebCoreLinkRole: + return linkAction; + default: + return noAction; + } +} + + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityRenderObject.h b/WebCore/page/AccessibilityRenderObject.h new file mode 100644 index 0000000..c859b5a --- /dev/null +++ b/WebCore/page/AccessibilityRenderObject.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityRenderObject_h +#define AccessibilityRenderObject_h + +#include "AccessibilityObject.h" + +namespace WebCore { + +class AXObjectCache; +class Element; +class Frame; +class FrameView; +class HitTestResult; +class HTMLAnchorElement; +class HTMLAreaElement; +class HTMLElement; +class HTMLLabelElement; +class HTMLMapElement; +class HTMLSelectElement; +class IntPoint; +class IntSize; +class Node; +class RenderObject; +class RenderListBox; +class RenderTextControl; +class Selection; +class String; +class Widget; + +class AccessibilityRenderObject : public AccessibilityObject { +protected: + AccessibilityRenderObject(RenderObject*); +public: + static PassRefPtr<AccessibilityRenderObject> create(RenderObject*); + virtual ~AccessibilityRenderObject(); + + bool isAccessibilityRenderObject() const { return true; }; + + virtual bool isAnchor() const; + virtual bool isAttachment() const; + virtual bool isHeading() const; + virtual bool isLink() const; + virtual bool isImageButton() const; + virtual bool isImage() const; + virtual bool isNativeImage() const; + virtual bool isPasswordField() const; + virtual bool isTextControl() const; + virtual bool isNativeTextControl() const; + virtual bool isWebArea() const; + virtual bool isCheckboxOrRadio() const; + virtual bool isFileUploadButton() const; + virtual bool isProgressIndicator() const; + virtual bool isSlider() const; + virtual bool isMenuRelated() const; + virtual bool isMenu() const; + virtual bool isMenuBar() const; + virtual bool isMenuButton() const; + virtual bool isMenuItem() const; + virtual bool isControl() const; + virtual bool isFieldset() const; + + virtual bool isEnabled() const; + virtual bool isSelected() const; + virtual bool isFocused() const; + virtual bool isChecked() const; + virtual bool isHovered() const; + virtual bool isIndeterminate() const; + virtual bool isLoaded() const; + virtual bool isMultiSelect() const; + virtual bool isOffScreen() const; + virtual bool isPressed() const; + virtual bool isReadOnly() const; + virtual bool isVisited() const; + + const AtomicString& getAttribute(const QualifiedName&) const; + virtual bool canSetFocusAttribute() const; + virtual bool canSetTextRangeAttributes() const; + virtual bool canSetValueAttribute() const; + + virtual bool hasIntValue() const; + + virtual bool accessibilityIsIgnored() const; + + static int headingLevel(Node*); + virtual int intValue() const; + virtual float valueForRange() const; + virtual float maxValueForRange() const; + virtual float minValueForRange() const; + virtual int layoutCount() const; + + virtual AccessibilityObject* doAccessibilityHitTest(const IntPoint&) const; + virtual AccessibilityObject* focusedUIElement() const; + virtual AccessibilityObject* firstChild() const; + virtual AccessibilityObject* lastChild() const; + virtual AccessibilityObject* previousSibling() const; + virtual AccessibilityObject* nextSibling() const; + virtual AccessibilityObject* parentObject() const; + virtual AccessibilityObject* observableObject() const; + virtual void linkedUIElements(AccessibilityChildrenVector&) const; + virtual AccessibilityObject* titleUIElement() const; + virtual AccessibilityRole ariaRoleAttribute() const; + virtual bool isPresentationalChildOfAriaRole() const; + virtual bool ariaRoleHasPresentationalChildren() const; + void setAriaRole(); + virtual AccessibilityRole roleValue() const; + virtual AXObjectCache* axObjectCache() const; + + virtual Element* actionElement() const; + Element* mouseButtonListener() const; + FrameView* frameViewIfRenderView() const; + virtual Element* anchorElement() const; + AccessibilityObject* menuForMenuButton() const; + AccessibilityObject* menuButtonForMenu() const; + + virtual IntRect boundingBoxRect() const; + virtual IntRect elementRect() const; + virtual IntSize size() const; + + void setRenderer(RenderObject* renderer) { m_renderer = renderer; } + RenderObject* renderer() const { return m_renderer; } + RenderObject* topRenderer() const; + RenderTextControl* textControl() const; + Document* document() const; + FrameView* topDocumentFrameView() const; + HTMLLabelElement* labelElementContainer() const; + + virtual KURL url() const; + virtual PlainTextRange selectedTextRange() const; + virtual Selection selection() const; + virtual String stringValue() const; + virtual String ariaAccessiblityName(const String&) const; + virtual String ariaLabeledByAttribute() const; + virtual String title() const; + virtual String ariaDescribedByAttribute() const; + virtual String accessibilityDescription() const; + virtual String helpText() const; + virtual String textUnderElement() const; + virtual String text() const; + virtual int textLength() const; + virtual PassRefPtr<Range> ariaSelectedTextDOMRange() const; + virtual String selectedText() const; + virtual const AtomicString& accessKey() const; + virtual const String& actionVerb() const; + virtual Widget* widget() const; + virtual Widget* widgetForAttachmentView() const; + virtual void getDocumentLinks(AccessibilityChildrenVector&); + virtual FrameView* documentFrameView() const; + + virtual const AccessibilityChildrenVector& children(); + + virtual void setFocused(bool); + virtual void setSelectedTextRange(const PlainTextRange&); + virtual void setValue(const String&); + + virtual void detach(); + virtual void childrenChanged(); + virtual void addChildren(); + virtual bool canHaveChildren() const; + virtual void selectedChildren(AccessibilityChildrenVector&); + virtual void visibleChildren(AccessibilityChildrenVector&); + virtual bool shouldFocusActiveDescendant() const; + virtual AccessibilityObject* activeDescendant() const; + virtual void handleActiveDescendantChanged(); + + virtual VisiblePositionRange visiblePositionRange() const; + virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const; + virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const; + virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const; + + virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const; + virtual VisiblePosition visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const; + virtual int index(const VisiblePosition&) const; + + virtual VisiblePosition visiblePositionForIndex(int) const; + virtual int indexForVisiblePosition(const VisiblePosition&) const; + + virtual PlainTextRange doAXRangeForLine(unsigned) const; + virtual PlainTextRange doAXRangeForIndex(unsigned) const; + + virtual String doAXStringForRange(const PlainTextRange&) const; + virtual IntRect doAXBoundsForRange(const PlainTextRange&) const; + +protected: + RenderObject* m_renderer; + AccessibilityRole m_ariaRole; + + void setRenderObject(RenderObject* renderer) { m_renderer = renderer; } + virtual void removeAXObjectID(); + + virtual bool isDetached() const { return !m_renderer; } + +private: + void ariaListboxSelectedChildren(AccessibilityChildrenVector&); + void ariaListboxVisibleChildren(AccessibilityChildrenVector&); + + Element* menuElementForMenuButton() const; + Element* menuItemElementForMenu() const; + AccessibilityRole determineAriaRoleAttribute() const; + + IntRect checkboxOrRadioRect() const; + void addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const; + AccessibilityObject* internalLinkElement() const; + AccessibilityObject* accessibilityParentForImageMap(HTMLMapElement* map) const; + +}; + +} // namespace WebCore + +#endif // AccessibilityRenderObject_h diff --git a/WebCore/page/AccessibilityTable.cpp b/WebCore/page/AccessibilityTable.cpp new file mode 100644 index 0000000..11c36ef --- /dev/null +++ b/WebCore/page/AccessibilityTable.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityTable.h" + +#include "AccessibilityTableCell.h" +#include "AccessibilityTableColumn.h" +#include "AccessibilityTableHeaderContainer.h" +#include "AccessibilityTableRow.h" +#include "AXObjectCache.h" +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "HTMLTableCaptionElement.h" +#include "HTMLTableCellElement.h" +#include "RenderObject.h" +#include "RenderTable.h" +#include "RenderTableCell.h" +#include "RenderTableSection.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityTable::AccessibilityTable(RenderObject* renderer) + : AccessibilityRenderObject(renderer), + m_headerContainer(0) +{ + // AXTables should not appear in tiger or leopard, on the mac +#if PLATFORM(MAC) && (defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)) + m_isAccessibilityTable = false; +#else + m_isAccessibilityTable = isTableExposableThroughAccessibility(); +#endif + +} + +AccessibilityTable::~AccessibilityTable() +{ +} + +PassRefPtr<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityTable(renderer)); +} + +bool AccessibilityTable::isTableExposableThroughAccessibility() +{ + // the following is a heuristic used to determine if a + // <table> should be exposed as an AXTable. The goal + // is to only show "data" tables + + if (!m_renderer || !m_renderer->isTable()) + return false; + + // if the developer assigned an aria role to this, then we shouldn't + // expose it as a table, unless, of course, the aria role is a table + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole == TableRole) + return true; + if (ariaRole != UnknownRole) + return false; + + RenderTable* table = static_cast<RenderTable*>(m_renderer); + + // this employs a heuristic to determine if this table should appear. + // Only "data" tables should be exposed as tables. + // Unfortunately, there is no good way to determine the difference + // between a "layout" table and a "data" table + + Node* tableNode = table->element(); + if (!tableNode || !tableNode->hasTagName(tableTag)) + return false; + + // if there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table + HTMLTableElement* tableElement = static_cast<HTMLTableElement*>(tableNode); + if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption()) + return true; + + // if someone used "rules" attribute than the table should appear + if (!tableElement->rules().isEmpty()) + return true; + + // go through the cell's and check for tell-tale signs of "data" table status + // cells have borders, or use attributes like headers, abbr, scope or axis + RenderTableSection* firstBody = table->firstBody(); + if (!firstBody) + return false; + + int numCols = firstBody->numColumns(); + int numRows = firstBody->numRows(); + + // if there's only one cell, it's not a good AXTable candidate + if (numRows == 1 && numCols == 1) + return false; + + // store the background color of the table to check against cell's background colors + RenderStyle* tableStyle = table->style(); + if (!tableStyle) + return false; + Color tableBGColor = tableStyle->backgroundColor(); + + // check enough of the cells to find if the table matches our criteria + // Criteria: + // 1) must have at least one valid cell (and) + // 2) at least half of cells have borders (or) + // 3) at least half of cells have different bg colors than the table, and there is cell spacing + unsigned validCellCount = 0; + unsigned borderedCellCount = 0; + unsigned backgroundDifferenceCellCount = 0; + + for (int row = 0; row < numRows; ++row) { + for (int col = 0; col < numCols; ++col) { + RenderTableCell* cell = firstBody->cellAt(row, col).cell; + if (!cell) + continue; + Node* cellNode = cell->element(); + if (!cellNode) + continue; + + if (cell->width() < 1 || cell->height() < 1) + continue; + + validCellCount++; + + HTMLTableCellElement* cellElement = static_cast<HTMLTableCellElement*>(cellNode); + + // in this case, the developer explicitly assigned a "data" table attribute + if (!cellElement->headers().isEmpty() || !cellElement->abbr().isEmpty() || + !cellElement->axis().isEmpty() || !cellElement->scope().isEmpty()) + return true; + + RenderStyle* renderStyle = cell->style(); + if (!renderStyle) + continue; + + // a cell needs to have matching bordered sides, before it can be considered a bordered cell. + if ((cell->borderTop() > 0 && cell->borderBottom() > 0) || + (cell->borderLeft() > 0 && cell->borderRight() > 0)) + borderedCellCount++; + + // if the cell has a different color from the table and there is cell spacing, + // then it is probably a data table cell (spacing and colors take the place of borders) + Color cellColor = renderStyle->backgroundColor(); + if (table->hBorderSpacing() > 0 && table->vBorderSpacing() > 0 && + tableBGColor != cellColor && cellColor.alpha() != 1) + backgroundDifferenceCellCount++; + + // if we've found 10 "good" cells, we don't need to keep searching + if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10) + return true; + } + } + + // if there is less than two valid cells, it's not a data table + if (validCellCount <= 1) + return false; + + // half of the cells had borders, it's a data table + unsigned neededCellCount = validCellCount / 2; + if (borderedCellCount >= neededCellCount) + return true; + + // half had different background colors, it's a data table + if (backgroundDifferenceCellCount >= neededCellCount) + return true; + + return false; +} + +void AccessibilityTable::clearChildren() +{ + m_children.clear(); + m_rows.clear(); + m_columns.clear(); + m_haveChildren = false; +} + +void AccessibilityTable::addChildren() +{ + if (!isDataTable()) { + AccessibilityRenderObject::addChildren(); + return; + } + + ASSERT(!m_haveChildren); + + m_haveChildren = true; + if (!m_renderer) + return; + + RenderTable* table = static_cast<RenderTable*>(m_renderer); + AXObjectCache* axCache = m_renderer->document()->axObjectCache(); + + // go through all the available sections to pull out the rows + // and add them as children + RenderTableSection* tableSection = table->header(); + if (!tableSection) + tableSection = table->firstBody(); + + if (!tableSection) + return; + + RenderTableSection* initialTableSection = tableSection; + + while (tableSection) { + + HashSet<AccessibilityObject*> appendedRows; + + unsigned numRows = tableSection->numRows(); + unsigned numCols = tableSection->numColumns(); + for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) { + for (unsigned colIndex = 0; colIndex < numCols; ++colIndex) { + + RenderTableCell* cell = tableSection->cellAt(rowIndex, colIndex).cell; + if (!cell) + continue; + + AccessibilityObject* rowObject = axCache->get(cell->parent()); + if (!rowObject->isTableRow()) + continue; + + AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(rowObject); + // we need to check every cell for a new row, because cell spans + // can cause us to mess rows if we just check the first column + if (appendedRows.contains(row)) + continue; + + row->setRowIndex((int)m_rows.size()); + m_rows.append(row); + m_children.append(row); + appendedRows.add(row); + } + } + + tableSection = table->sectionBelow(tableSection, true); + } + + // make the columns based on the number of columns in the first body + unsigned length = initialTableSection->numColumns(); + for (unsigned i = 0; i < length; ++i) { + AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->get(ColumnRole)); + column->setColumnIndex((int)i); + column->setParentTable(this); + m_columns.append(column); + m_children.append(column); + } + + AccessibilityObject* headerContainerObject = headerContainer(); + if (headerContainerObject) + m_children.append(headerContainerObject); +} + +AccessibilityObject* AccessibilityTable::headerContainer() +{ + if (m_headerContainer) + return m_headerContainer; + + m_headerContainer = static_cast<AccessibilityTableHeaderContainer*>(axObjectCache()->get(TableHeaderContainerRole)); + m_headerContainer->setParentTable(this); + + return m_headerContainer; +} + +AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::columns() +{ + if (!hasChildren()) + addChildren(); + + return m_columns; +} + +AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::rows() +{ + if (!hasChildren()) + addChildren(); + + return m_rows; +} + +void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers) +{ + if (!m_renderer) + return; + + if (!hasChildren()) + addChildren(); + + unsigned rowCount = m_rows.size(); + for (unsigned k = 0; k < rowCount; ++k) { + AccessibilityObject* header = static_cast<AccessibilityTableRow*>(m_rows[k].get())->headerObject(); + if (!header) + continue; + headers.append(header); + } +} + +void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers) +{ + if (!m_renderer) + return; + + if (!hasChildren()) + addChildren(); + + unsigned colCount = m_columns.size(); + for (unsigned k = 0; k < colCount; ++k) { + AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_columns[k].get())->headerObject(); + if (!header) + continue; + headers.append(header); + } +} + +void AccessibilityTable::cells(AccessibilityObject::AccessibilityChildrenVector& cells) +{ + if (!m_renderer) + return; + + if (!hasChildren()) + addChildren(); + + int numRows = m_rows.size(); + for (int row = 0; row < numRows; ++row) { + AccessibilityChildrenVector rowChildren = m_rows[row]->children(); + cells.append(rowChildren); + } +} + +const unsigned AccessibilityTable::columnCount() +{ + if (!hasChildren()) + addChildren(); + + return m_columns.size(); +} + +const unsigned AccessibilityTable::rowCount() +{ + if (!hasChildren()) + addChildren(); + + return m_rows.size(); +} + +AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column, unsigned row) +{ + if (!m_renderer) + return 0; + + if (!hasChildren()) + addChildren(); + + RenderTable* table = static_cast<RenderTable*>(m_renderer); + RenderTableSection* tableSection = table->header(); + if (!tableSection) + tableSection = table->firstBody(); + + RenderTableCell* cell = 0; + unsigned rowCount = 0; + unsigned rowOffset = 0; + while (tableSection) { + + rowCount += tableSection->numRows(); + unsigned numCols = tableSection->numColumns(); + + if (row < rowCount && column < numCols) { + int sectionSpecificRow = row - rowOffset; + cell = tableSection->cellAt(sectionSpecificRow, column).cell; + + // we didn't find the cell, which means there's spanning happening + // search backwards to find the spanning cell + if (!cell) { + + // first try rows + for (int testRow = sectionSpecificRow-1; testRow >= 0; --testRow) { + cell = tableSection->cellAt(testRow, column).cell; + // cell overlapped. use this one + if (cell && ((cell->row() + (cell->rowSpan()-1)) >= (int)sectionSpecificRow)) + break; + cell = 0; + } + + if (!cell) { + // try cols + for (int testCol = column-1; testCol >= 0; --testCol) { + cell = tableSection->cellAt(sectionSpecificRow, testCol).cell; + // cell overlapped. use this one + if (cell && ((cell->col() + (cell->colSpan()-1)) >= (int)column)) + break; + cell = 0; + } + } + } + } + + if (cell) + break; + + rowOffset += rowCount; + // we didn't find anything between the rows we should have + if (row < rowOffset) + break; + tableSection = table->sectionBelow(tableSection, true); + } + + if (!cell) + return 0; + + AccessibilityObject* cellObject = axObjectCache()->get(cell); + ASSERT(cellObject->isTableCell()); + + return static_cast<AccessibilityTableCell*>(cellObject); +} + +AccessibilityRole AccessibilityTable::roleValue() const +{ + if (!isDataTable()) + return AccessibilityRenderObject::roleValue(); + + return TableRole; +} + +bool AccessibilityTable::accessibilityIsIgnored() const +{ + if (!isDataTable()) + return AccessibilityRenderObject::accessibilityIsIgnored(); + + return false; +} + +String AccessibilityTable::title() const +{ + if (!isDataTable()) + return AccessibilityRenderObject::title(); + + String title; + if (!m_renderer) + return title; + + // see if there is a caption + Node *tableElement = m_renderer->element(); + if (tableElement) { + HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(tableElement)->caption(); + if (caption) + title = caption->innerText(); + } + + // try the standard + if (title.isEmpty()) + title = AccessibilityRenderObject::title(); + + return title; +} + +bool AccessibilityTable::isDataTable() const +{ + if (!m_renderer) + return false; + + return m_isAccessibilityTable; +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityTable.h b/WebCore/page/AccessibilityTable.h new file mode 100644 index 0000000..b98b6b7 --- /dev/null +++ b/WebCore/page/AccessibilityTable.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityTable_h +#define AccessibilityTable_h + +#include "AccessibilityRenderObject.h" + +namespace WebCore { + +class String; +class AccessibilityTableCell; +class AccessibilityTableHeaderContainer; + +class AccessibilityTable : public AccessibilityRenderObject { + +private: + AccessibilityTable(RenderObject*); +public: + static PassRefPtr<AccessibilityTable> create(RenderObject*); + virtual ~AccessibilityTable(); + + virtual bool isDataTable() const; + virtual AccessibilityRole roleValue() const; + + virtual bool accessibilityIsIgnored() const; + + virtual void addChildren(); + virtual void clearChildren(); + + AccessibilityChildrenVector& columns(); + AccessibilityChildrenVector& rows(); + + const unsigned columnCount(); + const unsigned rowCount(); + + virtual String title() const; + + // all the cells in the table + void cells(AccessibilityChildrenVector&); + AccessibilityTableCell* cellForColumnAndRow(unsigned column, unsigned row); + + void columnHeaders(AccessibilityChildrenVector&); + void rowHeaders(AccessibilityChildrenVector&); + + // an object that contains, as children, all the objects that act as headers + AccessibilityObject* headerContainer(); + +private: + AccessibilityChildrenVector m_rows; + AccessibilityChildrenVector m_columns; + + AccessibilityTableHeaderContainer* m_headerContainer; + mutable bool m_isAccessibilityTable; + + bool isTableExposableThroughAccessibility(); +}; + +} // namespace WebCore + +#endif // AccessibilityTable_h diff --git a/WebCore/page/AccessibilityTableCell.cpp b/WebCore/page/AccessibilityTableCell.cpp new file mode 100644 index 0000000..2062a88 --- /dev/null +++ b/WebCore/page/AccessibilityTableCell.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityTableCell.h" + +#include "AXObjectCache.h" +#include "RenderObject.h" +#include "RenderTableCell.h" + +using namespace std; + +namespace WebCore { + +AccessibilityTableCell::AccessibilityTableCell(RenderObject* renderer) + : AccessibilityRenderObject(renderer) +{ +} + +AccessibilityTableCell::~AccessibilityTableCell() +{ +} + +PassRefPtr<AccessibilityTableCell> AccessibilityTableCell::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityTableCell(renderer)); +} + +bool AccessibilityTableCell::accessibilityIsIgnored() const +{ + if (!isTableCell()) + return AccessibilityRenderObject::accessibilityIsIgnored(); + + return false; +} + +bool AccessibilityTableCell::isTableCell() const +{ + if (!m_renderer) + return false; + + AccessibilityObject* renderTable = axObjectCache()->get(static_cast<RenderTableCell*>(m_renderer)->table()); + if (!renderTable->isDataTable()) + return false; + + return true; +} + +AccessibilityRole AccessibilityTableCell::roleValue() const +{ + if (!isTableCell()) + return AccessibilityRenderObject::roleValue(); + + return CellRole; +} + +void AccessibilityTableCell::rowIndexRange(pair<int, int>& rowRange) +{ + if (!m_renderer) + return; + + RenderTableCell *renderCell = static_cast<RenderTableCell*>(m_renderer); + rowRange.first = renderCell->row(); + rowRange.second = renderCell->rowSpan(); + + // since our table might have multiple sections, we have to offset our row appropriately + RenderTableSection* section = renderCell->section(); + RenderTable* table = renderCell->table(); + if (!table || !section) + return; + + RenderTableSection* tableSection = table->header(); + if (!tableSection) + tableSection = table->firstBody(); + + unsigned rowOffset = 0; + while (tableSection) { + if (tableSection == section) + break; + rowOffset += tableSection->numRows(); + tableSection = table->sectionBelow(tableSection, true); + } + + rowRange.first += rowOffset; +} + +void AccessibilityTableCell::columnIndexRange(pair<int, int>& columnRange) +{ + if (!m_renderer) + return; + + RenderTableCell *renderCell = static_cast<RenderTableCell*>(m_renderer); + columnRange.first = renderCell->col(); + columnRange.second = renderCell->colSpan(); +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityTableCell.h b/WebCore/page/AccessibilityTableCell.h new file mode 100644 index 0000000..e77dfb2 --- /dev/null +++ b/WebCore/page/AccessibilityTableCell.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityTableCell_h +#define AccessibilityTableCell_h + +#include "AccessibilityRenderObject.h" + +namespace WebCore { + +class AccessibilityTableCell : public AccessibilityRenderObject { + +private: + AccessibilityTableCell(RenderObject*); +public: + static PassRefPtr<AccessibilityTableCell> create(RenderObject*); + virtual ~AccessibilityTableCell(); + + virtual bool isTableCell() const; + virtual AccessibilityRole roleValue() const; + + virtual bool accessibilityIsIgnored() const; + + // fills in the start location and row span of cell + void rowIndexRange(pair<int, int>& rowRange); + // fills in the start location and column span of cell + void columnIndexRange(pair<int, int>& columnRange); + +private: + int m_rowIndex; + +}; + +} // namespace WebCore + +#endif // AccessibilityTableCell_h diff --git a/WebCore/page/AccessibilityTableColumn.cpp b/WebCore/page/AccessibilityTableColumn.cpp new file mode 100644 index 0000000..6e03af9 --- /dev/null +++ b/WebCore/page/AccessibilityTableColumn.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityTableColumn.h" + +#include "AccessibilityTableCell.h" +#include "AXObjectCache.h" +#include "HTMLNames.h" +#include "RenderTable.h" +#include "RenderTableSection.h" +#include "RenderTableCell.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityTableColumn::AccessibilityTableColumn() + : m_parentTable(0) +{ +} + +AccessibilityTableColumn::~AccessibilityTableColumn() +{ +} + +PassRefPtr<AccessibilityTableColumn> AccessibilityTableColumn::create() +{ + return adoptRef(new AccessibilityTableColumn()); +} + +void AccessibilityTableColumn::setParentTable(AccessibilityTable* table) +{ + m_parentTable = table; + + clearChildren(); + addChildren(); +} + +IntRect AccessibilityTableColumn::elementRect() const +{ + // this will be filled in when addChildren is called + return m_columnRect; +} + +IntSize AccessibilityTableColumn::size() const +{ + return elementRect().size(); +} + +const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTableColumn::children() +{ + if (!m_haveChildren) + addChildren(); + return m_children; +} + +AccessibilityObject* AccessibilityTableColumn::headerObject() +{ + if (!m_parentTable) + return 0; + + RenderTable* table = static_cast<RenderTable*>(m_parentTable->renderer()); + + AccessibilityObject* headerObject = 0; + + // try the <thead> section first. this doesn't require th tags + headerObject = headerObjectForSection(table->header(), false); + + if (headerObject) + return headerObject; + + // now try for <th> tags in the first body + headerObject = headerObjectForSection(table->firstBody(), true); + + return headerObject; +} + +AccessibilityObject* AccessibilityTableColumn::headerObjectForSection(RenderTableSection* section, bool thTagRequired) +{ + if (!section) + return 0; + + int numCols = section->numColumns(); + if (m_columnIndex >= numCols) + return 0; + + RenderTableCell* cell = 0; + // also account for cells that have a span + for (int testCol = m_columnIndex; testCol >= 0; --testCol) { + RenderTableCell* testCell = section->cellAt(0, testCol).cell; + if (!testCell) + continue; + + // we've reached a cell that doesn't even overlap our column + // it can't be our header + if ((testCell->col() + (testCell->colSpan()-1)) < m_columnIndex) + break; + + Node* node = testCell->element(); + if (!node) + continue; + + if (thTagRequired && !node->hasTagName(thTag)) + continue; + + cell = testCell; + } + + if (!cell) + return 0; + + return m_parentTable->axObjectCache()->get(cell); +} + +void AccessibilityTableColumn::addChildren() +{ + ASSERT(!m_haveChildren); + + m_haveChildren = true; + if (!m_parentTable) + return; + + int numRows = m_parentTable->rowCount(); + + for (int i = 0; i < numRows; i++) { + AccessibilityTableCell* cell = m_parentTable->cellForColumnAndRow(m_columnIndex, i); + if (!cell) + continue; + + // make sure the last one isn't the same as this one (rowspan cells) + if (m_children.size() > 0 && m_children.last() == cell) + continue; + + m_children.append(cell); + m_columnRect.unite(cell->elementRect()); + } +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityTableColumn.h b/WebCore/page/AccessibilityTableColumn.h new file mode 100644 index 0000000..6270398 --- /dev/null +++ b/WebCore/page/AccessibilityTableColumn.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityTableColumn_h +#define AccessibilityTableColumn_h + +#include "AccessibilityObject.h" +#include "AccessibilityTable.h" +#include "IntRect.h" + +namespace WebCore { + +class RenderTableSection; + +class AccessibilityTableColumn : public AccessibilityObject { + +private: + AccessibilityTableColumn(); +public: + static PassRefPtr<AccessibilityTableColumn> create(); + virtual ~AccessibilityTableColumn(); + + void setParentTable(AccessibilityTable*); + virtual AccessibilityObject* parentObject() const { return m_parentTable; } + AccessibilityObject* headerObject(); + + virtual AccessibilityRole roleValue() const { return ColumnRole; } + virtual bool accessibilityIsIgnored() const { return false; } + virtual bool isTableColumn() const { return true; } + + void setColumnIndex(int columnIndex) { m_columnIndex = columnIndex; } + int columnIndex() const { return m_columnIndex; } + + virtual const AccessibilityChildrenVector& children(); + virtual void addChildren(); + + virtual IntSize size() const; + virtual IntRect elementRect() const; + +private: + AccessibilityTable* m_parentTable; + int m_columnIndex; + IntRect m_columnRect; + + AccessibilityObject* headerObjectForSection(RenderTableSection*, bool thTagRequired); +}; + +} // namespace WebCore + +#endif // AccessibilityTableColumn_h diff --git a/WebCore/page/AccessibilityTableHeaderContainer.cpp b/WebCore/page/AccessibilityTableHeaderContainer.cpp new file mode 100644 index 0000000..af9de39 --- /dev/null +++ b/WebCore/page/AccessibilityTableHeaderContainer.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityTableHeaderContainer.h" + +#include "AccessibilityTable.h" +#include "AXObjectCache.h" + +using namespace std; + +namespace WebCore { + +AccessibilityTableHeaderContainer::AccessibilityTableHeaderContainer() + : m_parentTable(0) +{ +} + +AccessibilityTableHeaderContainer::~AccessibilityTableHeaderContainer() +{ +} + +PassRefPtr<AccessibilityTableHeaderContainer> AccessibilityTableHeaderContainer::create() +{ + return adoptRef(new AccessibilityTableHeaderContainer()); +} + +const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTableHeaderContainer::children() +{ + if (!m_haveChildren) + addChildren(); + return m_children; +} + +IntRect AccessibilityTableHeaderContainer::elementRect() const +{ + // this will be filled in when addChildren is called + return m_headerRect; +} + +IntSize AccessibilityTableHeaderContainer::size() const +{ + return elementRect().size(); +} + +void AccessibilityTableHeaderContainer::addChildren() +{ + ASSERT(!m_haveChildren); + + m_haveChildren = true; + if (!m_parentTable || !m_parentTable->isDataTable()) + return; + + static_cast<AccessibilityTable*>(m_parentTable)->columnHeaders(m_children); + + unsigned length = m_children.size(); + for (unsigned k = 0; k < length; ++k) { + m_headerRect.unite(m_children[k]->elementRect()); + } +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityTableHeaderContainer.h b/WebCore/page/AccessibilityTableHeaderContainer.h new file mode 100644 index 0000000..8a9448a --- /dev/null +++ b/WebCore/page/AccessibilityTableHeaderContainer.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityTableHeaderContainer_h +#define AccessibilityTableHeaderContainer_h + +#include "AccessibilityObject.h" +#include "AccessibilityTable.h" +#include "IntRect.h" + +namespace WebCore { + +class AccessibilityTableHeaderContainer : public AccessibilityObject { + +private: + AccessibilityTableHeaderContainer(); +public: + static PassRefPtr<AccessibilityTableHeaderContainer> create(); + virtual ~AccessibilityTableHeaderContainer(); + + virtual AccessibilityRole roleValue() const { return TableHeaderContainerRole; } + + void setParentTable(AccessibilityTable* table) { m_parentTable = table; } + virtual AccessibilityObject* parentObject() const { return m_parentTable; } + + virtual bool accessibilityIsIgnored() const { return false; } + + virtual const AccessibilityChildrenVector& children(); + virtual void addChildren(); + + virtual IntSize size() const; + virtual IntRect elementRect() const; + +private: + AccessibilityTable* m_parentTable; + IntRect m_headerRect; + +}; + +} // namespace WebCore + +#endif // AccessibilityTableHeaderContainer_h diff --git a/WebCore/page/AccessibilityTableRow.cpp b/WebCore/page/AccessibilityTableRow.cpp new file mode 100644 index 0000000..caccff5 --- /dev/null +++ b/WebCore/page/AccessibilityTableRow.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityTableRow.h" + +#include "AccessibilityTableCell.h" +#include "AXObjectCache.h" +#include "HTMLNames.h" +#include "HTMLTableRowElement.h" +#include "RenderObject.h" +#include "RenderTableCell.h" +#include "RenderTableRow.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +AccessibilityTableRow::AccessibilityTableRow(RenderObject* renderer) + : AccessibilityRenderObject(renderer) +{ +} + +AccessibilityTableRow::~AccessibilityTableRow() +{ +} + +PassRefPtr<AccessibilityTableRow> AccessibilityTableRow::create(RenderObject* renderer) +{ + return adoptRef(new AccessibilityTableRow(renderer)); +} + +AccessibilityRole AccessibilityTableRow::roleValue() const +{ + if (!isTableRow()) + return AccessibilityRenderObject::roleValue(); + + return RowRole; +} + +bool AccessibilityTableRow::isTableRow() const +{ + if (!m_renderer) + return true; + + AccessibilityObject* renderTable = axObjectCache()->get(static_cast<RenderTableRow*>(m_renderer)->table()); + if (!renderTable->isDataTable()) + return false; + + return true; +} + +bool AccessibilityTableRow::accessibilityIsIgnored() const +{ + if (!isTableRow()) + return AccessibilityRenderObject::accessibilityIsIgnored(); + + return false; +} + +AccessibilityObject* AccessibilityTableRow::headerObject() +{ + AccessibilityChildrenVector rowChildren = children(); + if (!rowChildren.size()) + return 0; + + // check the first element in the row to see if it is a TH element + AccessibilityObject* cell = rowChildren[0].get(); + if (!cell->isTableCell()) + return 0; + + RenderObject* cellRenderer = static_cast<AccessibilityTableCell*>(cell)->renderer(); + if (!cellRenderer) + return 0; + + Node* cellNode = cellRenderer->element(); + if (!cellNode || !cellNode->hasTagName(thTag)) + return 0; + + return cell; +} + +} // namespace WebCore diff --git a/WebCore/page/AccessibilityTableRow.h b/WebCore/page/AccessibilityTableRow.h new file mode 100644 index 0000000..0ec7f04 --- /dev/null +++ b/WebCore/page/AccessibilityTableRow.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AccessibilityTableRow_h +#define AccessibilityTableRow_h + +#include "AccessibilityRenderObject.h" + +namespace WebCore { + +class AccessibilityTableRow : public AccessibilityRenderObject { + +private: + AccessibilityTableRow(RenderObject*); +public: + static PassRefPtr<AccessibilityTableRow> create(RenderObject*); + virtual ~AccessibilityTableRow(); + + virtual bool isTableRow() const; + virtual AccessibilityRole roleValue() const; + + // retrieves the "row" header (a th tag in the rightmost column) + AccessibilityObject* headerObject(); + + virtual bool accessibilityIsIgnored() const; + + void setRowIndex(int rowIndex) { m_rowIndex = rowIndex; } + int rowIndex() const { return m_rowIndex; } + + // allows the table to add other children that may not originate + // in the row, but their col/row spans overlap into it + void appendChild(AccessibilityObject*); + +private: + int m_rowIndex; +}; + +} // namespace WebCore + +#endif // AccessibilityTableRow_h diff --git a/WebCore/page/AnimationController.cpp b/WebCore/page/AnimationController.cpp deleted file mode 100644 index 0c78364..0000000 --- a/WebCore/page/AnimationController.cpp +++ /dev/null @@ -1,606 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AnimationController.h" - -#include "CSSPropertyNames.h" -#include "Document.h" -#include "FloatConversion.h" -#include "Frame.h" -#include "RenderObject.h" -#include "RenderStyle.h" -#include "SystemTime.h" -#include "Timer.h" - -namespace WebCore { - -static const double cAnimationTimerDelay = 0.025; - -struct CurveData { - CurveData(double p1x, double p1y, double p2x, double p2y) - { - // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). - cx = 3.0 * p1x; - bx = 3.0 * (p2x - p1x) - cx; - ax = 1.0 - cx -bx; - - cy = 3.0 * p1y; - by = 3.0 * (p2y - p1y) - cy; - ay = 1.0 - cy - by; - } - - double sampleCurveX(double t) - { - // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. - return ((ax * t + bx) * t + cx) * t; - } - - double sampleCurveY(double t) - { - return ((ay * t + by) * t + cy) * t; - } - - double sampleCurveDerivativeX(double t) - { - return (3.0 * ax * t + 2.0 * bx) * t + cx; - } - - // Given an x value, find a parametric value it came from. - double solveCurveX(double x, double epsilon) - { - double t0; - double t1; - double t2; - double x2; - double d2; - int i; - - // First try a few iterations of Newton's method -- normally very fast. - for (t2 = x, i = 0; i < 8; i++) { - x2 = sampleCurveX(t2) - x; - if (fabs (x2) < epsilon) - return t2; - d2 = sampleCurveDerivativeX(t2); - if (fabs(d2) < 1e-6) - break; - t2 = t2 - x2 / d2; - } - - // Fall back to the bisection method for reliability. - t0 = 0.0; - t1 = 1.0; - t2 = x; - - if (t2 < t0) - return t0; - if (t2 > t1) - return t1; - - while (t0 < t1) { - x2 = sampleCurveX(t2); - if (fabs(x2 - x) < epsilon) - return t2; - if (x > x2) - t0 = t2; - else - t1 = t2; - t2 = (t1 - t0) * .5 + t0; - } - - // Failure. - return t2; - } - -private: - double ax; - double bx; - double cx; - - double ay; - double by; - double cy; -}; - -// The epsilon value we pass to solveCurveX given that the animation is going to run over |dur| seconds. The longer the -// animation, the more precision we need in the timing function result to avoid ugly discontinuities. -static inline double solveEpsilon(double duration) { return 1. / (200. * duration); } - -static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration) -{ - // Convert from input time to parametric value in curve, then from - // that to output time. - CurveData c(p1x, p1y, p2x, p2y); - t = c.solveCurveX(t, solveEpsilon(duration)); - t = c.sampleCurveY(t); - return t; -} - -class CompositeImplicitAnimation; - -class ImplicitAnimation : public Noncopyable { -public: - ImplicitAnimation(const Transition*); - ~ImplicitAnimation(); - - void animate(CompositeImplicitAnimation*, RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle); - - void reset(RenderObject*, RenderStyle* from, RenderStyle* to); - - double progress() const; - - bool finished() const { return m_finished; } - -private: - // The two styles that we are blending. - RenderStyle* m_fromStyle; - RenderStyle* m_toStyle; - - int m_property; - TimingFunction m_function; - double m_duration; - - int m_repeatCount; - int m_iteration; - - bool m_finished; - double m_startTime; - bool m_paused; - double m_pauseTime; -}; - -class CompositeImplicitAnimation : public Noncopyable { -public: - ~CompositeImplicitAnimation() { deleteAllValues(m_animations); } - - RenderStyle* animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); - - bool animating() const; - - bool hasAnimationForProperty(int prop) const { return m_animations.contains(prop); } - - void reset(RenderObject*); - -private: - HashMap<int, ImplicitAnimation*> m_animations; -}; - -ImplicitAnimation::ImplicitAnimation(const Transition* transition) -: m_fromStyle(0) -, m_toStyle(0) -, m_property(transition->transitionProperty()) -, m_function(transition->transitionTimingFunction()) -, m_duration(transition->transitionDuration() / 1000.0) -, m_repeatCount(transition->transitionRepeatCount()) -, m_iteration(0) -, m_finished(false) -, m_startTime(currentTime()) -, m_paused(false) -, m_pauseTime(m_startTime) -{ -} - -ImplicitAnimation::~ImplicitAnimation() -{ - ASSERT(!m_fromStyle && !m_toStyle); -} - -void ImplicitAnimation::reset(RenderObject* renderer, RenderStyle* from, RenderStyle* to) -{ - if (m_fromStyle) - m_fromStyle->deref(renderer->renderArena()); - if (m_toStyle) - m_toStyle->deref(renderer->renderArena()); - m_fromStyle = from; - if (m_fromStyle) - m_fromStyle->ref(); - m_toStyle = to; - if (m_toStyle) - m_toStyle->ref(); - m_finished = false; - if (from || to) - m_startTime = currentTime(); -} - -double ImplicitAnimation::progress() const -{ - double elapsedTime = currentTime() - m_startTime; - - if (m_finished || !m_duration || elapsedTime >= m_duration) - return 1.0; - - if (m_function.type() == LinearTimingFunction) - return elapsedTime / m_duration; - - // Cubic bezier. - return solveCubicBezierFunction(m_function.x1(), m_function.y1(), - m_function.x2(), m_function.y2(), - elapsedTime / m_duration, m_duration); -} - -static inline int blendFunc(int from, int to, double progress) -{ - return int(from + (to - from) * progress); -} - -static inline double blendFunc(double from, double to, double progress) -{ - return from + (to - from) * progress; -} - -static inline float blendFunc(float from, float to, double progress) -{ - return narrowPrecisionToFloat(from + (to - from) * progress); -} - -static inline Color blendFunc(const Color& from, const Color& to, double progress) -{ - return Color(blendFunc(from.red(), to.red(), progress), - blendFunc(from.green(), to.green(), progress), - blendFunc(from.blue(), to.blue(), progress), - blendFunc(from.alpha(), to.alpha(), progress)); -} - -static inline Length blendFunc(const Length& from, const Length& to, double progress) -{ - return to.blend(from, progress); -} - -static inline IntSize blendFunc(const IntSize& from, const IntSize& to, double progress) -{ - return IntSize(blendFunc(from.width(), to.width(), progress), - blendFunc(from.height(), to.height(), progress)); -} - -static inline ShadowData* blendFunc(const ShadowData* from, const ShadowData* to, double progress) -{ - ASSERT(from && to); - return new ShadowData(blendFunc(from->x, to->x, progress), blendFunc(from->y, to->y, progress), blendFunc(from->blur, to->blur, progress), blendFunc(from->color, to->color, progress)); -} - -static inline TransformOperations blendFunc(const TransformOperations& from, const TransformOperations& to, double progress) -{ - // Blend any operations whose types actually match up. Otherwise don't bother. - unsigned fromSize = from.size(); - unsigned toSize = to.size(); - unsigned size = max(fromSize, toSize); - TransformOperations result; - for (unsigned i = 0; i < size; i++) { - TransformOperation* fromOp = i < fromSize ? from[i].get() : 0; - TransformOperation* toOp = i < toSize ? to[i].get() : 0; - TransformOperation* blendedOp = toOp ? toOp->blend(fromOp, progress) : fromOp->blend(0, progress, true); - result.append(blendedOp); - } - return result; -} - -static inline EVisibility blendFunc(EVisibility from, EVisibility to, double progress) -{ - // Any non-zero result means we consider the object to be visible. Only at 0 do we consider the object to be - // invisible. The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values. - double fromVal = from == VISIBLE ? 1. : 0.; - double toVal = to == VISIBLE ? 1. : 0.; - if (fromVal == toVal) - return to; - double result = blendFunc(fromVal, toVal, progress); - return result > 0. ? VISIBLE : (to != VISIBLE ? to : from); -} - -#define BLEND(prop, getter, setter) \ - if (m_property == prop && m_toStyle->getter() != targetStyle->getter()) \ - reset(renderer, currentStyle, targetStyle); \ - \ - if ((m_property == cAnimateAll && !animation->hasAnimationForProperty(prop)) || m_property == prop) { \ - if (m_fromStyle->getter() != m_toStyle->getter()) {\ - m_finished = false; \ - if (!animatedStyle) \ - animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \ - animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\ - if (m_property == prop) \ - return; \ - }\ - }\ - -#define BLEND_MAYBE_INVALID_COLOR(prop, getter, setter) \ - if (m_property == prop && m_toStyle->getter() != targetStyle->getter()) \ - reset(renderer, currentStyle, targetStyle); \ - \ - if ((m_property == cAnimateAll && !animation->hasAnimationForProperty(prop)) || m_property == prop) { \ - Color fromColor = m_fromStyle->getter(); \ - Color toColor = m_toStyle->getter(); \ - if (!fromColor.isValid()) \ - fromColor = m_fromStyle->color(); \ - if (!toColor.isValid()) \ - toColor = m_toStyle->color(); \ - if (fromColor != toColor) {\ - m_finished = false; \ - if (!animatedStyle) \ - animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \ - animatedStyle->setter(blendFunc(fromColor, toColor, progress()));\ - if (m_property == prop) \ - return; \ - }\ - }\ - -#define BLEND_SHADOW(prop, getter, setter) \ - if (m_property == prop && (!m_toStyle->getter() || !targetStyle->getter() || *m_toStyle->getter() != *targetStyle->getter())) \ - reset(renderer, currentStyle, targetStyle); \ - \ - if ((m_property == cAnimateAll && !animation->hasAnimationForProperty(prop)) || m_property == prop) { \ - if (m_fromStyle->getter() && m_toStyle->getter() && *m_fromStyle->getter() != *m_toStyle->getter()) {\ - m_finished = false; \ - if (!animatedStyle) \ - animatedStyle = new (renderer->renderArena()) RenderStyle(*targetStyle); \ - animatedStyle->setter(blendFunc(m_fromStyle->getter(), m_toStyle->getter(), progress()));\ - if (m_property == prop) \ - return; \ - }\ - } - -void ImplicitAnimation::animate(CompositeImplicitAnimation* animation, RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle, RenderStyle*& animatedStyle) -{ - // FIXME: If we have no transition-property, then the only way to tell if our goal state changed is to check - // every single animatable property. For now we'll just diff the styles to ask that question, - // but we should really exclude non-animatable properties. - if (!m_toStyle || (m_property == cAnimateAll && targetStyle->diff(m_toStyle))) - reset(renderer, currentStyle, targetStyle); - - // FIXME: Blow up shorthands so that they can be honored. - m_finished = true; - BLEND(CSS_PROP_LEFT, left, setLeft); - BLEND(CSS_PROP_RIGHT, right, setRight); - BLEND(CSS_PROP_TOP, top, setTop); - BLEND(CSS_PROP_BOTTOM, bottom, setBottom); - BLEND(CSS_PROP_WIDTH, width, setWidth); - BLEND(CSS_PROP_HEIGHT, height, setHeight); - BLEND(CSS_PROP_BORDER_LEFT_WIDTH, borderLeftWidth, setBorderLeftWidth); - BLEND(CSS_PROP_BORDER_RIGHT_WIDTH, borderRightWidth, setBorderRightWidth); - BLEND(CSS_PROP_BORDER_TOP_WIDTH, borderTopWidth, setBorderTopWidth); - BLEND(CSS_PROP_BORDER_BOTTOM_WIDTH, borderBottomWidth, setBorderBottomWidth); - BLEND(CSS_PROP_MARGIN_LEFT, marginLeft, setMarginLeft); - BLEND(CSS_PROP_MARGIN_RIGHT, marginRight, setMarginRight); - BLEND(CSS_PROP_MARGIN_TOP, marginTop, setMarginTop); - BLEND(CSS_PROP_MARGIN_BOTTOM, marginBottom, setMarginBottom); - BLEND(CSS_PROP_PADDING_LEFT, paddingLeft, setPaddingLeft); - BLEND(CSS_PROP_PADDING_RIGHT, paddingRight, setPaddingRight); - BLEND(CSS_PROP_PADDING_TOP, paddingTop, setPaddingTop); - BLEND(CSS_PROP_PADDING_BOTTOM, paddingBottom, setPaddingBottom); - BLEND(CSS_PROP_OPACITY, opacity, setOpacity); - BLEND(CSS_PROP_COLOR, color, setColor); - BLEND(CSS_PROP_BACKGROUND_COLOR, backgroundColor, setBackgroundColor); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP__WEBKIT_COLUMN_RULE_COLOR, columnRuleColor, setColumnRuleColor); - BLEND(CSS_PROP__WEBKIT_COLUMN_RULE_WIDTH, columnRuleWidth, setColumnRuleWidth); - BLEND(CSS_PROP__WEBKIT_COLUMN_GAP, columnGap, setColumnGap); - BLEND(CSS_PROP__WEBKIT_COLUMN_COUNT, columnCount, setColumnCount); - BLEND(CSS_PROP__WEBKIT_COLUMN_WIDTH, columnWidth, setColumnWidth); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP__WEBKIT_TEXT_STROKE_COLOR, textStrokeColor, setTextStrokeColor); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP__WEBKIT_TEXT_FILL_COLOR, textFillColor, setTextFillColor); - BLEND(CSS_PROP__WEBKIT_BORDER_HORIZONTAL_SPACING, horizontalBorderSpacing, setHorizontalBorderSpacing); - BLEND(CSS_PROP__WEBKIT_BORDER_VERTICAL_SPACING, verticalBorderSpacing, setVerticalBorderSpacing); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP_BORDER_LEFT_COLOR, borderLeftColor, setBorderLeftColor); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP_BORDER_RIGHT_COLOR, borderRightColor, setBorderRightColor); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP_BORDER_TOP_COLOR, borderTopColor, setBorderTopColor); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP_BORDER_BOTTOM_COLOR, borderBottomColor, setBorderBottomColor); - BLEND(CSS_PROP_Z_INDEX, zIndex, setZIndex); - BLEND(CSS_PROP_LINE_HEIGHT, lineHeight, setLineHeight); - BLEND_MAYBE_INVALID_COLOR(CSS_PROP_OUTLINE_COLOR, outlineColor, setOutlineColor); - BLEND(CSS_PROP_OUTLINE_OFFSET, outlineOffset, setOutlineOffset); - BLEND(CSS_PROP_OUTLINE_WIDTH, outlineWidth, setOutlineWidth); - BLEND(CSS_PROP_LETTER_SPACING, letterSpacing, setLetterSpacing); - BLEND(CSS_PROP_WORD_SPACING, wordSpacing, setWordSpacing); - BLEND_SHADOW(CSS_PROP__WEBKIT_BOX_SHADOW, boxShadow, setBoxShadow); - BLEND_SHADOW(CSS_PROP_TEXT_SHADOW, textShadow, setTextShadow); - BLEND(CSS_PROP__WEBKIT_TRANSFORM, transform, setTransform); - BLEND(CSS_PROP__WEBKIT_TRANSFORM_ORIGIN_X, transformOriginX, setTransformOriginX); - BLEND(CSS_PROP__WEBKIT_TRANSFORM_ORIGIN_Y, transformOriginY, setTransformOriginY); - BLEND(CSS_PROP__WEBKIT_BORDER_TOP_LEFT_RADIUS, borderTopLeftRadius, setBorderTopLeftRadius); - BLEND(CSS_PROP__WEBKIT_BORDER_TOP_RIGHT_RADIUS, borderTopRightRadius, setBorderTopRightRadius); - BLEND(CSS_PROP__WEBKIT_BORDER_BOTTOM_LEFT_RADIUS, borderBottomLeftRadius, setBorderBottomLeftRadius); - BLEND(CSS_PROP__WEBKIT_BORDER_BOTTOM_RIGHT_RADIUS, borderBottomRightRadius, setBorderBottomRightRadius); - BLEND(CSS_PROP_VISIBILITY, visibility, setVisibility); -} - -RenderStyle* CompositeImplicitAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) -{ - // Get the animation layers from the target style. - // For each one, we need to create a new animation unless one exists already (later occurrences of duplicate - // triggers in the layer list get ignored). - if (m_animations.isEmpty()) { - for (const Transition* transition = targetStyle->transitions(); transition; transition = transition->next()) { - int property = transition->transitionProperty(); - int duration = transition->transitionDuration(); - int repeatCount = transition->transitionRepeatCount(); - if (property && duration && repeatCount && !m_animations.contains(property)) { - ImplicitAnimation* animation = new ImplicitAnimation(transition); - m_animations.set(property, animation); - } - } - } - - // Now that we have animation objects ready, let them know about the new goal state. We want them - // to fill in a RenderStyle*& only if needed. - RenderStyle* result = 0; - HashMap<int, ImplicitAnimation*>::iterator end = m_animations.end(); - for (HashMap<int, ImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) - it->second->animate(this, renderer, currentStyle, targetStyle, result); - - if (result) - return result; - - return targetStyle; -} - -bool CompositeImplicitAnimation::animating() const -{ - HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end(); - for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it) - if (!it->second->finished()) - return true; - return false; -} - -void CompositeImplicitAnimation::reset(RenderObject* renderer) -{ - HashMap<int, ImplicitAnimation*>::const_iterator end = m_animations.end(); - for (HashMap<int, ImplicitAnimation*>::const_iterator it = m_animations.begin(); it != end; ++it) - it->second->reset(renderer, 0, 0); -} - -class AnimationControllerPrivate { -public: - AnimationControllerPrivate(Frame*); - ~AnimationControllerPrivate(); - - CompositeImplicitAnimation* get(RenderObject*); - bool clear(RenderObject*); - - void timerFired(Timer<AnimationControllerPrivate>*); - void updateTimer(); - - bool hasImplicitAnimations() const { return !m_animations.isEmpty(); } - -private: - HashMap<RenderObject*, CompositeImplicitAnimation*> m_animations; - Timer<AnimationControllerPrivate> m_timer; - Frame* m_frame; -}; - -AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame) - : m_timer(this, &AnimationControllerPrivate::timerFired) - , m_frame(frame) -{ -} - -AnimationControllerPrivate::~AnimationControllerPrivate() -{ - deleteAllValues(m_animations); -} - -CompositeImplicitAnimation* AnimationControllerPrivate::get(RenderObject* renderer) -{ - CompositeImplicitAnimation* animation = m_animations.get(renderer); - if (!animation) { - animation = new CompositeImplicitAnimation(); - m_animations.set(renderer, animation); - } - return animation; -} - -bool AnimationControllerPrivate::clear(RenderObject* renderer) -{ - CompositeImplicitAnimation* animation = m_animations.take(renderer); - if (!animation) - return false; - animation->reset(renderer); - delete animation; - return true; -} - -void AnimationControllerPrivate::updateTimer() -{ - bool animating = false; - HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end(); - for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) { - if (it->second->animating()) { - animating = true; - break; - } - } - - if (animating) { - if (!m_timer.isActive()) - m_timer.startRepeating(cAnimationTimerDelay); - } else if (m_timer.isActive()) - m_timer.stop(); -} - -void AnimationControllerPrivate::timerFired(Timer<AnimationControllerPrivate>* timer) -{ - // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate - // updateRendering. It will then call back to us with new information. - bool animating = false; - HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator end = m_animations.end(); - for (HashMap<RenderObject*, CompositeImplicitAnimation*>::iterator it = m_animations.begin(); it != end; ++it) { - if (it->second->animating()) { - animating = true; - it->first->element()->setChanged(); - } - } - - m_frame->document()->updateRendering(); - - updateTimer(); -} - -AnimationController::AnimationController(Frame* frame) -:m_data(new AnimationControllerPrivate(frame)) -{ - -} - -AnimationController::~AnimationController() -{ - delete m_data; -} - -void AnimationController::cancelImplicitAnimations(RenderObject* renderer) -{ - if (!m_data->hasImplicitAnimations()) - return; - - if (m_data->clear(renderer)) - renderer->element()->setChanged(); -} - -RenderStyle* AnimationController::updateImplicitAnimations(RenderObject* renderer, RenderStyle* newStyle) -{ - // Fetch our current set of implicit animations from a hashtable. We then compare them - // against the animations in the style and make sure we're in sync. If destination values - // have changed, we reset the animation. We then do a blend to get new values and we return - // a new style. - ASSERT(renderer->element()); // FIXME: We do not animate generated content yet. - - CompositeImplicitAnimation* animation = m_data->get(renderer); - RenderStyle* result = animation->animate(renderer, renderer->style(), newStyle); - m_data->updateTimer(); - return result; -} - -void AnimationController::suspendAnimations() -{ - // FIXME: Walk the whole hashtable and call pause on each animation. - // Kill our timer. -} - -void AnimationController::resumeAnimations() -{ - // FIXME: Walk the whole hashtable and call resume on each animation. - // Start our timer. -} - -} diff --git a/WebCore/page/BarInfo.h b/WebCore/page/BarInfo.h index 261d3dd..4cbbcfc 100644 --- a/WebCore/page/BarInfo.h +++ b/WebCore/page/BarInfo.h @@ -29,6 +29,7 @@ #ifndef BarInfo_h #define BarInfo_h +#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -38,13 +39,15 @@ namespace WebCore { class BarInfo : public RefCounted<BarInfo> { public: enum Type { Locationbar, Menubar, Personalbar, Scrollbars, Statusbar, Toolbar }; - - BarInfo(Frame*, Type); + + static PassRefPtr<BarInfo> create(Frame* frame, Type type) { return adoptRef(new BarInfo(frame, type)); } + void disconnectFrame(); bool visible() const; private: + BarInfo(Frame*, Type); Frame* m_frame; Type m_type; }; diff --git a/WebCore/page/Chrome.cpp b/WebCore/page/Chrome.cpp index fecbe79..c9b57f2 100644 --- a/WebCore/page/Chrome.cpp +++ b/WebCore/page/Chrome.cpp @@ -1,7 +1,6 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2007 Trolltech ASA + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,6 +22,9 @@ #include "Chrome.h" #include "ChromeClient.h" +#include "DNS.h" +#include "Document.h" +#include "FileList.h" #include "FloatRect.h" #include "Frame.h" #include "FrameTree.h" @@ -32,16 +34,21 @@ #include "HitTestResult.h" #include "InspectorController.h" #include "Page.h" +#include "PageGroup.h" +#include "PausedTimeouts.h" #include "ResourceHandle.h" +#include "ScriptController.h" +#include "SecurityOrigin.h" #include "Settings.h" #include "WindowFeatures.h" -#include "kjs_window.h" -#include "PausedTimeouts.h" -#include "SecurityOrigin.h" #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Vector.h> +#if ENABLE(DOM_STORAGE) +#include "SessionStorage.h" +#endif + namespace WebCore { using namespace HTMLNames; @@ -70,6 +77,31 @@ Chrome::~Chrome() m_client->chromeDestroyed(); } +void Chrome::repaint(const IntRect& windowRect, bool contentChanged, bool immediate, bool repaintContentOnly) +{ + m_client->repaint(windowRect, contentChanged, immediate, repaintContentOnly); +} + +void Chrome::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) +{ + m_client->scroll(scrollDelta, rectToScroll, clipRect); +} + +IntPoint Chrome::screenToWindow(const IntPoint& point) const +{ + return m_client->screenToWindow(point); +} + +IntRect Chrome::windowToScreen(const IntRect& rect) const +{ + return m_client->windowToScreen(rect); +} + +PlatformWidget Chrome::platformWindow() const +{ + return m_client->platformWindow(); +} + void Chrome::setWindowRect(const FloatRect& rect) const { m_client->setWindowRect(rect); @@ -112,7 +144,15 @@ void Chrome::takeFocus(FocusDirection direction) const Page* Chrome::createWindow(Frame* frame, const FrameLoadRequest& request, const WindowFeatures& features) const { - return m_client->createWindow(frame, request, features); + Page* newPage = m_client->createWindow(frame, request, features); +#if ENABLE(DOM_STORAGE) + + if (newPage) { + if (SessionStorage* oldSessionStorage = m_page->sessionStorage(false)) + newPage->setSessionStorage(oldSessionStorage->copy(newPage)); + } +#endif + return newPage; } void Chrome::show() const @@ -134,11 +174,6 @@ bool Chrome::canRunModalNow() const void Chrome::runModal() const { - if (m_page->defersLoading()) { - LOG_ERROR("Tried to run modal in a page when it was deferring loading -- should never happen."); - return; - } - // Defer callbacks in all the other pages in this group, so we don't try to run JavaScript // in a way that could interact with this view. PageGroupLoadDeferrer deferrer(m_page, false); @@ -192,14 +227,6 @@ void Chrome::setResizable(bool b) const m_client->setResizable(b); } -void Chrome::addMessageToConsole(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID) -{ - if (source == JSMessageSource) - m_client->addMessageToConsole(message, lineNumber, sourceID); - - m_page->inspectorController()->addMessageToConsole(source, level, message, lineNumber, sourceID); -} - bool Chrome::canRunBeforeUnloadConfirmPanel() { return m_client->canRunBeforeUnloadConfirmPanel(); @@ -288,24 +315,17 @@ IntRect Chrome::windowResizerRect() const return m_client->windowResizerRect(); } -void Chrome::addToDirtyRegion(const IntRect& rect) -{ - m_client->addToDirtyRegion(rect); -} - -void Chrome::scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect) -{ - m_client->scrollBackingStore(dx, dy, scrollViewRect, clipRect); -} - -void Chrome::updateBackingStore() -{ - m_client->updateBackingStore(); -} - void Chrome::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags) { + if (result.innerNode()) { + Document* document = result.innerNode()->document(); + if (document && document->isDNSPrefetchEnabled()) + prefetchDNS(result.absoluteLinkURL().host()); + } m_client->mouseDidMoveOverElement(result, modifierFlags); + + if (InspectorController* inspector = m_page->inspectorController()) + inspector->mouseDidMoveOverElement(result, modifierFlags); } void Chrome::setToolTip(const HitTestResult& result) @@ -331,10 +351,32 @@ void Chrome::setToolTip(const HitTestResult& result) toolTip = result.absoluteLinkURL().string(); } - // Lastly we'll consider a tooltip for element with "title" attribute + // Next we'll consider a tooltip for element with "title" attribute if (toolTip.isEmpty()) toolTip = result.title(); + // Lastly, for <input type="file"> that allow multiple files, we'll consider a tooltip for the selected filenames + if (toolTip.isEmpty()) { + if (Node* node = result.innerNonSharedNode()) { + if (node->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(node); + if (input->inputType() == HTMLInputElement::FILE) { + FileList* files = input->files(); + unsigned listSize = files->length(); + if (files && listSize > 1) { + Vector<UChar> names; + for (size_t i = 0; i < listSize; ++i) { + append(names, files->item(i)->fileName()); + if (i != listSize - 1) + names.append('\n'); + } + toolTip = String::adopt(names); + } + } + } + } + } + m_client->setToolTip(toolTip); } @@ -343,15 +385,80 @@ void Chrome::print(Frame* frame) m_client->print(frame); } -PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page* page, bool deferSelf) +void Chrome::disableSuddenTermination() +{ + m_client->disableSuddenTermination(); +} + +void Chrome::enableSuddenTermination() +{ + m_client->enableSuddenTermination(); +} + +void Chrome::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> fileChooser) +{ + m_client->runOpenPanel(frame, fileChooser); +} +// -------- + +#if ENABLE(DASHBOARD_SUPPORT) +void ChromeClient::dashboardRegionsChanged() +{ +} +#endif + +void ChromeClient::populateVisitedLinks() +{ +} + +FloatRect ChromeClient::customHighlightRect(Node*, const AtomicString&, const FloatRect&) +{ + return FloatRect(); +} + +void ChromeClient::paintCustomHighlight(Node*, const AtomicString&, const FloatRect&, const FloatRect&, bool, bool) +{ +} + +bool ChromeClient::shouldReplaceWithGeneratedFileForUpload(const String&, String&) { - const HashSet<Page*>* group = page->frameNamespace(); + return false; +} + +String ChromeClient::generateReplacementFile(const String&) +{ + ASSERT_NOT_REACHED(); + return String(); +} + +void ChromeClient::disableSuddenTermination() +{ +} + +void ChromeClient::enableSuddenTermination() +{ +} + +bool ChromeClient::paintCustomScrollbar(GraphicsContext*, const FloatRect&, ScrollbarControlSize, + ScrollbarControlState, ScrollbarPart, bool vertical, + float value, float proportion, ScrollbarControlPartMask) +{ + return false; +} + +bool ChromeClient::paintCustomScrollCorner(GraphicsContext*, const FloatRect&) +{ + return false; +} + +// -------- - if (!group) - return; +PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page* page, bool deferSelf) +{ + const HashSet<Page*>& pages = page->group().pages(); - HashSet<Page*>::const_iterator end = group->end(); - for (HashSet<Page*>::const_iterator it = group->begin(); it != end; ++it) { + HashSet<Page*>::const_iterator end = pages.end(); + for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) { Page* otherPage = *it; if ((deferSelf || otherPage != page)) { if (!otherPage->defersLoading()) @@ -359,11 +466,10 @@ PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page* page, bool deferSelf) #if !PLATFORM(MAC) for (Frame* frame = otherPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) { - if (KJS::Window* window = KJS::Window::retrieveWindow(frame)) { - PausedTimeouts* timeouts = window->pauseTimeouts(); - - m_pausedTimeouts.append(make_pair(frame, timeouts)); - } + OwnPtr<PausedTimeouts> timeouts; + frame->script()->pauseTimeouts(timeouts); + if (timeouts) + m_pausedTimeouts.append(make_pair(RefPtr<Frame>(frame), timeouts.release())); } #endif } @@ -377,19 +483,15 @@ PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page* page, bool deferSelf) PageGroupLoadDeferrer::~PageGroupLoadDeferrer() { - size_t count = m_deferredFrames.size(); - for (size_t i = 0; i < count; ++i) + for (size_t i = 0; i < m_deferredFrames.size(); ++i) if (Page* page = m_deferredFrames[i]->page()) page->setDefersLoading(false); #if !PLATFORM(MAC) - count = m_pausedTimeouts.size(); - - for (size_t i = 0; i < count; i++) { - KJS::Window* window = KJS::Window::retrieveWindow(m_pausedTimeouts[i].first.get()); - if (window) - window->resumeTimeouts(m_pausedTimeouts[i].second); - delete m_pausedTimeouts[i].second; + for (size_t i = 0; i < m_pausedTimeouts.size(); i++) { + Frame* frame = m_pausedTimeouts[i].first.get(); + OwnPtr<PausedTimeouts> timeouts(m_pausedTimeouts[i].second); + frame->script()->resumeTimeouts(timeouts); } #endif } diff --git a/WebCore/page/Chrome.h b/WebCore/page/Chrome.h index 9bc42e0..0dd4013 100644 --- a/WebCore/page/Chrome.h +++ b/WebCore/page/Chrome.h @@ -1,4 +1,3 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * @@ -21,9 +20,10 @@ #ifndef Chrome_h #define Chrome_h +#include "FileChooser.h" #include "FocusDirection.h" +#include "HostWindow.h" #include <wtf/Forward.h> -#include <wtf/Noncopyable.h> #include <wtf/RefPtr.h> #if PLATFORM(MAC) @@ -42,32 +42,24 @@ namespace WebCore { class IntRect; class Page; class String; - + struct FrameLoadRequest; struct WindowFeatures; - enum MessageSource { - HTMLMessageSource, - XMLMessageSource, - JSMessageSource, - CSSMessageSource, - OtherMessageSource - }; - - enum MessageLevel { - TipMessageLevel, - LogMessageLevel, - WarningMessageLevel, - ErrorMessageLevel - }; - - class Chrome : Noncopyable { + class Chrome : public HostWindow { public: Chrome(Page*, ChromeClient*); ~Chrome(); ChromeClient* client() { return m_client; } + // HostWindow methods. + virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false); + virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect); + virtual IntPoint screenToWindow(const IntPoint&) const; + virtual IntRect windowToScreen(const IntRect&) const; + virtual PlatformWidget platformWindow() const; + void setWindowRect(const FloatRect&) const; FloatRect windowRect() const; @@ -102,8 +94,6 @@ namespace WebCore { void setResizable(bool) const; - void addMessageToConsole(MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String& sourceID); - bool canRunBeforeUnloadConfirmPanel(); bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame); @@ -111,21 +101,23 @@ namespace WebCore { void runJavaScriptAlert(Frame*, const String&); bool runJavaScriptConfirm(Frame*, const String&); - bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result); + bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result); void setStatusbarText(Frame*, const String&); bool shouldInterruptJavaScript(); IntRect windowResizerRect() const; - void addToDirtyRegion(const IntRect&); - void scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect); - void updateBackingStore(); void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags); void setToolTip(const HitTestResult&); void print(Frame*); - + + void enableSuddenTermination(); + void disableSuddenTermination(); + + void runOpenPanel(Frame*, PassRefPtr<FileChooser>); + #if PLATFORM(MAC) void focusNSView(NSView*); #endif diff --git a/WebCore/page/ChromeClient.h b/WebCore/page/ChromeClient.h index 5bce1af..7678dc2 100644 --- a/WebCore/page/ChromeClient.h +++ b/WebCore/page/ChromeClient.h @@ -1,4 +1,3 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* * Copyright (C) 2006, 2007, 2008 Apple, Inc. All rights reserved. * @@ -21,20 +20,38 @@ #ifndef ChromeClient_h #define ChromeClient_h +#include "GraphicsContext.h" #include "FocusDirection.h" +#include "ScrollTypes.h" +#include "HostWindow.h" +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +#if PLATFORM(MAC) +#include "WebCoreKeyboardUIMode.h" +#endif + +#ifndef __OBJC__ +class NSMenu; +class NSResponder; +#endif namespace WebCore { + class AtomicString; + class FileChooser; class FloatRect; class Frame; class HitTestResult; class IntRect; + class Node; class Page; class String; + class Widget; struct FrameLoadRequest; struct WindowFeatures; - + class ChromeClient { public: virtual void chromeDestroyed() = 0; @@ -86,15 +103,19 @@ namespace WebCore { virtual void runJavaScriptAlert(Frame*, const String&) = 0; virtual bool runJavaScriptConfirm(Frame*, const String&) = 0; virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result) = 0; - virtual void setStatusbarText(const String&) = 0; virtual bool shouldInterruptJavaScript() = 0; virtual bool tabsToLinks() const = 0; virtual IntRect windowResizerRect() const = 0; - virtual void addToDirtyRegion(const IntRect&) = 0; - virtual void scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect) = 0; - virtual void updateBackingStore() = 0; + + // Methods used by HostWindow. + virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false) = 0; + virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) = 0; + virtual IntPoint screenToWindow(const IntPoint&) const = 0; + virtual IntRect windowToScreen(const IntRect&) const = 0; + virtual PlatformWidget platformWindow() const = 0; + // End methods used by HostWindow. virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags) = 0; @@ -104,6 +125,38 @@ namespace WebCore { virtual void exceededDatabaseQuota(Frame*, const String& databaseName) = 0; +#if ENABLE(DASHBOARD_SUPPORT) + virtual void dashboardRegionsChanged(); +#endif + + virtual void populateVisitedLinks(); + + virtual FloatRect customHighlightRect(Node*, const AtomicString& type, const FloatRect& lineRect); + virtual void paintCustomHighlight(Node*, const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, + bool behindText, bool entireLine); + + virtual bool shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename); + virtual String generateReplacementFile(const String& path); + + virtual void enableSuddenTermination(); + virtual void disableSuddenTermination(); + + virtual bool paintCustomScrollbar(GraphicsContext*, const FloatRect&, ScrollbarControlSize, + ScrollbarControlState, ScrollbarPart pressedPart, bool vertical, + float value, float proportion, ScrollbarControlPartMask); + virtual bool paintCustomScrollCorner(GraphicsContext*, const FloatRect&); + + virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) = 0; + +#if PLATFORM(MAC) + virtual KeyboardUIMode keyboardUIMode() { return KeyboardAccessDefault; } + + virtual NSResponder *firstResponder() { return 0; } + virtual void makeFirstResponder(NSResponder *) { } + + virtual void willPopUpMenu(NSMenu *) { } +#endif + protected: virtual ~ChromeClient() { } }; diff --git a/WebCore/page/Console.cpp b/WebCore/page/Console.cpp index d45af62..bfcc478 100644 --- a/WebCore/page/Console.cpp +++ b/WebCore/page/Console.cpp @@ -29,11 +29,24 @@ #include "config.h" #include "Console.h" -#include "Chrome.h" +#include "ChromeClient.h" +#include "CString.h" #include "Frame.h" #include "FrameLoader.h" +#include "FrameTree.h" +#include "InspectorController.h" +#include "JSDOMBinding.h" #include "Page.h" +#include "PageGroup.h" #include "PlatformString.h" +#include <runtime/ArgList.h> +#include <kjs/interpreter.h> +#include <runtime/JSObject.h> +#include <VM/Machine.h> +#include <profiler/Profiler.h> +#include <stdio.h> + +using namespace JSC; namespace WebCore { @@ -47,52 +60,422 @@ void Console::disconnectFrame() m_frame = 0; } -void Console::error(const String& message) +static void printSourceURLAndLine(const String& sourceURL, unsigned lineNumber) { - if (!m_frame) + if (!sourceURL.isEmpty()) { + if (lineNumber > 0) + printf("%s:%d: ", sourceURL.utf8().data(), lineNumber); + else + printf("%s: ", sourceURL.utf8().data()); + } +} + +static void printMessageSourceAndLevelPrefix(MessageSource source, MessageLevel level) +{ + const char* sourceString; + switch (source) { + case HTMLMessageSource: + sourceString = "HTML"; + break; + case XMLMessageSource: + sourceString = "XML"; + break; + case JSMessageSource: + sourceString = "JS"; + break; + case CSSMessageSource: + sourceString = "CSS"; + break; + default: + ASSERT_NOT_REACHED(); + // Fall thru. + case OtherMessageSource: + sourceString = "OTHER"; + break; + } + + const char* levelString; + switch (level) { + case TipMessageLevel: + levelString = "TIP"; + break; + default: + ASSERT_NOT_REACHED(); + // Fall thru. + case LogMessageLevel: + levelString = "LOG"; + break; + case WarningMessageLevel: + levelString = "WARN"; + break; + case ErrorMessageLevel: + levelString = "ERROR"; + break; + } + + printf("%s %s:", sourceString, levelString); +} + +static void printToStandardOut(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber) +{ + if (!Console::shouldPrintExceptions()) return; - Page* page = m_frame->page(); + printSourceURLAndLine(sourceURL, lineNumber); + printMessageSourceAndLevelPrefix(source, level); + + printf(" %s\n", message.utf8().data()); +} + +static void printToStandardOut(MessageLevel level, ExecState* exec, const ArgList& args, const KURL& url) +{ + if (!Console::shouldPrintExceptions()) + return; + + printSourceURLAndLine(url.prettyURL(), 0); + printMessageSourceAndLevelPrefix(JSMessageSource, level); + + for (size_t i = 0; i < args.size(); ++i) { + UString argAsString = args.at(exec, i)->toString(exec); + printf(" %s", argAsString.UTF8String().c_str()); + } + + printf("\n"); +} + +static inline void retrieveLastCaller(ExecState* exec, KURL& url, unsigned& lineNumber) +{ + int signedLineNumber; + intptr_t sourceID; + UString urlString; + JSValue* function; + + exec->machine()->retrieveLastCaller(exec, signedLineNumber, sourceID, urlString, function); + + url = KURL(urlString); + lineNumber = (signedLineNumber >= 0 ? signedLineNumber : 0); +} + +void Console::addMessage(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL) +{ + Page* page = this->page(); if (!page) return; - page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 0, m_frame->loader()->url().prettyURL()); + if (source == JSMessageSource) + page->chrome()->client()->addMessageToConsole(message, lineNumber, sourceURL); + + page->inspectorController()->addMessageToConsole(source, level, message, lineNumber, sourceURL); + + printToStandardOut(source, level, message, sourceURL, lineNumber); } -void Console::info(const String& message) +void Console::debug(ExecState* exec, const ArgList& args) { - if (!m_frame) + // In Firebug, console.debug has the same behavior as console.log. So we'll do the same. + log(exec, args); +} + +void Console::error(ExecState* exec, const ArgList& args) +{ + if (args.isEmpty()) return; - Page* page = m_frame->page(); + Page* page = this->page(); if (!page) return; - page->chrome()->addMessageToConsole(JSMessageSource, LogMessageLevel, message, 0, m_frame->loader()->url().prettyURL()); + String message = args.at(exec, 0)->toString(exec); + + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + page->chrome()->client()->addMessageToConsole(message, lineNumber, url.prettyURL()); + page->inspectorController()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, exec, args, lineNumber, url.string()); + + printToStandardOut(ErrorMessageLevel, exec, args, url); } -void Console::log(const String& message) +void Console::info(ExecState* exec, const ArgList& args) { - if (!m_frame) + if (args.isEmpty()) return; - Page* page = m_frame->page(); + Page* page = this->page(); if (!page) return; - page->chrome()->addMessageToConsole(JSMessageSource, LogMessageLevel, message, 0, m_frame->loader()->url().prettyURL()); + String message = args.at(exec, 0)->toString(exec); + + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + page->chrome()->client()->addMessageToConsole(message, lineNumber, url.prettyURL()); + page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageLevel, exec, args, lineNumber, url.string()); + + printToStandardOut(LogMessageLevel, exec, args, url); } -void Console::warn(const String& message) +void Console::log(ExecState* exec, const ArgList& args) { - if (!m_frame) + if (args.isEmpty()) return; - Page* page = m_frame->page(); + Page* page = this->page(); if (!page) return; - page->chrome()->addMessageToConsole(JSMessageSource, WarningMessageLevel, message, 0, m_frame->loader()->url().prettyURL()); + String message = args.at(exec, 0)->toString(exec); + + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + page->chrome()->client()->addMessageToConsole(message, lineNumber, url.prettyURL()); + page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageLevel, exec, args, lineNumber, url.string()); + + printToStandardOut(LogMessageLevel, exec, args, url); +} + +void Console::dir(ExecState* exec, const ArgList& args) +{ + if (args.isEmpty()) + return; + + Page* page = this->page(); + if (!page) + return; + + page->inspectorController()->addMessageToConsole(JSMessageSource, ObjectMessageLevel, exec, args, 0, String()); +} + +void Console::dirxml(ExecState* exec, const ArgList& args) +{ + if (args.isEmpty()) + return; + + Page* page = this->page(); + if (!page) + return; + + page->inspectorController()->addMessageToConsole(JSMessageSource, NodeMessageLevel, exec, args, 0, String()); +} + +void Console::trace(ExecState* exec) +{ + Page* page = this->page(); + if (!page) + return; + + int signedLineNumber; + intptr_t sourceID; + UString urlString; + JSValue* func; + + exec->machine()->retrieveLastCaller(exec, signedLineNumber, sourceID, urlString, func); + + ArgList args; + while (!func->isNull()) { + args.append(func); + func = exec->machine()->retrieveCaller(exec, asInternalFunction(func)); + } + + page->inspectorController()->addMessageToConsole(JSMessageSource, TraceMessageLevel, exec, args, 0, String()); +} + +void Console::assertCondition(bool condition, ExecState* exec, const ArgList& args) +{ + if (condition) + return; + + Page* page = this->page(); + if (!page) + return; + + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + // FIXME: <https://bugs.webkit.org/show_bug.cgi?id=19135> It would be nice to prefix assertion failures with a message like "Assertion failed: ". + // FIXME: <https://bugs.webkit.org/show_bug.cgi?id=19136> We should print a message even when args.isEmpty() is true. + + page->inspectorController()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, exec, args, lineNumber, url.string()); + + printToStandardOut(ErrorMessageLevel, exec, args, url); +} + +void Console::count(ExecState* exec, const ArgList& args) +{ + Page* page = this->page(); + if (!page) + return; + + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + UString title; + if (args.size() >= 1) + title = valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)); + + page->inspectorController()->count(title, lineNumber, url.string()); +} + +void Console::profile(ExecState* exec, const ArgList& args) +{ + Page* page = this->page(); + if (!page) + return; + + // FIXME: log a console message when profiling is disabled. + if (!page->inspectorController()->profilerEnabled()) + return; + + UString title = args.at(exec, 0)->toString(exec); + Profiler::profiler()->startProfiling(exec, title); +} + +void Console::profileEnd(ExecState* exec, const ArgList& args) +{ + Page* page = this->page(); + if (!page) + return; + + if (!page->inspectorController()->profilerEnabled()) + return; + + UString title; + if (args.size() >= 1) + title = valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)); + + RefPtr<Profile> profile = Profiler::profiler()->stopProfiling(exec, title); + if (!profile) + return; + + m_profiles.append(profile); + + if (Page* page = this->page()) { + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + page->inspectorController()->addProfile(profile, lineNumber, url); + } +} + +void Console::time(const UString& title) +{ + if (title.isNull()) + return; + + Page* page = this->page(); + if (!page) + return; + + page->inspectorController()->startTiming(title); +} + +void Console::timeEnd(ExecState* exec, const ArgList& args) +{ + UString title; + if (args.size() >= 1) + title = valueToStringWithUndefinedOrNullCheck(exec, args.at(exec, 0)); + if (title.isNull()) + return; + + Page* page = this->page(); + if (!page) + return; + + double elapsed; + if (!page->inspectorController()->stopTiming(title, elapsed)) + return; + + String message = String(title) + String::format(": %.0fms", elapsed); + + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageLevel, message, lineNumber, url.string()); +} + +void Console::group(ExecState* exec, const ArgList& arguments) +{ + Page* page = this->page(); + if (!page) + return; + + page->inspectorController()->startGroup(JSMessageSource, exec, arguments, 0, String()); +} + +void Console::groupEnd() +{ + Page* page = this->page(); + if (!page) + return; + + page->inspectorController()->endGroup(JSMessageSource, 0, String()); +} + +void Console::warn(ExecState* exec, const ArgList& args) +{ + if (args.isEmpty()) + return; + + Page* page = this->page(); + if (!page) + return; + + String message = args.at(exec, 0)->toString(exec); + + KURL url; + unsigned lineNumber; + retrieveLastCaller(exec, url, lineNumber); + + page->chrome()->client()->addMessageToConsole(message, lineNumber, url.prettyURL()); + page->inspectorController()->addMessageToConsole(JSMessageSource, WarningMessageLevel, exec, args, lineNumber, url.string()); + + printToStandardOut(WarningMessageLevel, exec, args, url); +} + +void Console::reportException(ExecState* exec, JSValue* exception) +{ + UString errorMessage = exception->toString(exec); + JSObject* exceptionObject = exception->toObject(exec); + int lineNumber = exceptionObject->get(exec, Identifier(exec, "line"))->toInt32(exec); + UString exceptionSourceURL = exceptionObject->get(exec, Identifier(exec, "sourceURL"))->toString(exec); + addMessage(JSMessageSource, ErrorMessageLevel, errorMessage, lineNumber, exceptionSourceURL); + if (exec->hadException()) + exec->clearException(); +} + +void Console::reportCurrentException(ExecState* exec) +{ + JSValue* exception = exec->exception(); + exec->clearException(); + reportException(exec, exception); +} + +static bool printExceptions = false; + +bool Console::shouldPrintExceptions() +{ + return printExceptions; +} + +void Console::setShouldPrintExceptions(bool print) +{ + printExceptions = print; +} + +Page* Console::page() const +{ + if (!m_frame) + return 0; + return m_frame->page(); } } // namespace WebCore diff --git a/WebCore/page/Console.h b/WebCore/page/Console.h index 81e39be..8f33e96 100644 --- a/WebCore/page/Console.h +++ b/WebCore/page/Console.h @@ -29,25 +29,86 @@ #ifndef Console_h #define Console_h -#include <wtf/RefCounted.h> #include "PlatformString.h" +#include <profiler/Profile.h> +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace JSC { + class ExecState; + class ArgList; +} namespace WebCore { + typedef Vector<RefPtr<JSC::Profile> > ProfilesArray; + class Frame; + class Page; + class String; + + enum MessageSource { + HTMLMessageSource, + XMLMessageSource, + JSMessageSource, + CSSMessageSource, + OtherMessageSource + }; + + enum MessageLevel { + TipMessageLevel, + LogMessageLevel, + WarningMessageLevel, + ErrorMessageLevel, + ObjectMessageLevel, + NodeMessageLevel, + TraceMessageLevel, + StartGroupMessageLevel, + EndGroupMessageLevel + }; class Console : public RefCounted<Console> { public: - Console(Frame*); + static PassRefPtr<Console> create(Frame* frame) { return adoptRef(new Console(frame)); } + void disconnectFrame(); - void error(const String& message); - void info(const String& message); - void log(const String& message); - void warn(const String& message); + void addMessage(MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String& sourceURL); + +#if USE(JSC) + void debug(JSC::ExecState*, const JSC::ArgList&); + void error(JSC::ExecState*, const JSC::ArgList&); + void info(JSC::ExecState*, const JSC::ArgList&); + void log(JSC::ExecState*, const JSC::ArgList&); + void warn(JSC::ExecState*, const JSC::ArgList&); + void dir(JSC::ExecState*, const JSC::ArgList&); + void dirxml(JSC::ExecState*, const JSC::ArgList& arguments); + void trace(JSC::ExecState*); + void assertCondition(bool condition, JSC::ExecState*, const JSC::ArgList&); + void count(JSC::ExecState*, const JSC::ArgList&); + void profile(JSC::ExecState*, const JSC::ArgList&); + void profileEnd(JSC::ExecState*, const JSC::ArgList&); + void time(const JSC::UString& title); + void timeEnd(JSC::ExecState*, const JSC::ArgList&); + void group(JSC::ExecState*, const JSC::ArgList&); + void groupEnd(); + + void reportException(JSC::ExecState*, JSC::JSValue*); + void reportCurrentException(JSC::ExecState*); + + static bool shouldPrintExceptions(); + static void setShouldPrintExceptions(bool); + + const ProfilesArray& profiles() const { return m_profiles; } +#endif private: + inline Page* page() const; + + Console(Frame*); + Frame* m_frame; + ProfilesArray m_profiles; }; } // namespace WebCore diff --git a/WebCore/page/Console.idl b/WebCore/page/Console.idl index 3356c0e..9075963 100644 --- a/WebCore/page/Console.idl +++ b/WebCore/page/Console.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,10 +29,25 @@ module window { interface Console { - void error(in DOMString message); - void info(in DOMString message); - void log(in DOMString message); - void warn(in DOMString message); + readonly attribute [CustomGetter] Array profiles; + + [Custom] void debug(); + [Custom] void error(); + [Custom] void info(); + [Custom] void log(); + [Custom] void warn(); + [Custom] void dir(); + [Custom] void dirxml(); + [Custom] void trace(); + [Custom, ImplementationFunction=assertCondition] void assert(in boolean condition); + [Custom] void count(); + + [Custom] void profile(in DOMString title); + [Custom] void profileEnd(); + void time(in [ConvertUndefinedOrNullToNullString] DOMString title); + [Custom] void timeEnd(); + [Custom] void group(); + void groupEnd(); }; } diff --git a/WebCore/page/ContextMenuController.cpp b/WebCore/page/ContextMenuController.cpp index f2c3c75..813f8e2 100644 --- a/WebCore/page/ContextMenuController.cpp +++ b/WebCore/page/ContextMenuController.cpp @@ -43,7 +43,6 @@ #include "HitTestRequest.h" #include "HitTestResult.h" #include "InspectorController.h" -#include "KURL.h" #include "MouseEvent.h" #include "Node.h" #include "Page.h" @@ -59,8 +58,6 @@ namespace WebCore { -using namespace EventNames; - ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client) : m_page(page) , m_client(client) @@ -82,7 +79,7 @@ void ContextMenuController::clearContextMenu() void ContextMenuController::handleContextMenuEvent(Event* event) { - ASSERT(event->type() == contextmenuEvent); + ASSERT(event->type() == eventNames().contextmenuEvent); if (!event->isMouseEvent()) return; MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); @@ -154,10 +151,11 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) frame->editor()->copyImage(result); break; case ContextMenuItemTagOpenFrameInNewWindow: { - KURL url = frame->loader()->documentLoader()->unreachableURL(); - if (frame && url.isEmpty()) - url = frame->loader()->documentLoader()->url(); - openNewWindow(url, frame); + DocumentLoader* loader = frame->loader()->documentLoader(); + if (!loader->unreachableURL().isEmpty()) + openNewWindow(loader->unreachableURL(), frame); + else + openNewWindow(loader->url(), frame); break; } case ContextMenuItemTagCopy: @@ -191,11 +189,11 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) #endif case ContextMenuItemTagSpellingGuess: ASSERT(frame->selectedText().length()); - if (frame->editor()->shouldInsertText(item->title(), frame->selectionController()->toRange().get(), + if (frame->editor()->shouldInsertText(item->title(), frame->selection()->toRange().get(), EditorInsertActionPasted)) { Document* document = frame->document(); RefPtr<ReplaceSelectionCommand> command = - new ReplaceSelectionCommand(document, createFragmentFromMarkup(document, item->title(), ""), + ReplaceSelectionCommand::create(document, createFragmentFromMarkup(document, item->title(), ""), true, false, true); applyCommand(command); frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); @@ -216,8 +214,8 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) break; case ContextMenuItemTagOpenLink: if (Frame* targetFrame = result.targetFrame()) - targetFrame->loader()->load(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), - frame->loader()->outgoingReferrer())), false, true, 0, 0, HashMap<String, String>()); + targetFrame->loader()->loadFrameRequestWithFormAndValues(FrameLoadRequest(ResourceRequest(result.absoluteLinkURL(), + frame->loader()->outgoingReferrer())), false, 0, 0, HashMap<String, String>()); else openNewWindow(result.absoluteLinkURL(), frame); break; @@ -236,7 +234,7 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) break; case ContextMenuItemTagStartSpeaking: { ExceptionCode ec; - RefPtr<Range> selectedRange = frame->selectionController()->toRange(); + RefPtr<Range> selectedRange = frame->selection()->toRange(); if (!selectedRange || selectedRange->collapsed(ec)) { Document* document = result.innerNonSharedNode()->document(); selectedRange = document->createRange(); @@ -249,13 +247,13 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) m_client->stopSpeaking(); break; case ContextMenuItemTagDefaultDirection: - frame->editor()->setBaseWritingDirection("inherit"); + frame->editor()->setBaseWritingDirection(NaturalWritingDirection); break; case ContextMenuItemTagLeftToRight: - frame->editor()->setBaseWritingDirection("ltr"); + frame->editor()->setBaseWritingDirection(LeftToRightWritingDirection); break; case ContextMenuItemTagRightToLeft: - frame->editor()->setBaseWritingDirection("rtl"); + frame->editor()->setBaseWritingDirection(RightToLeftWritingDirection); break; #if PLATFORM(MAC) case ContextMenuItemTagSearchInSpotlight: diff --git a/WebCore/page/DOMSelection.cpp b/WebCore/page/DOMSelection.cpp index 66b567a..5dab325 100644 --- a/WebCore/page/DOMSelection.cpp +++ b/WebCore/page/DOMSelection.cpp @@ -61,7 +61,7 @@ Node* DOMSelection::anchorNode() const if (!m_frame) return 0; - const Selection& selection = m_frame->selectionController()->selection(); + const Selection& selection = m_frame->selection()->selection(); Position anchor = selection.isBaseFirst() ? selection.start() : selection.end(); anchor = rangeCompliantEquivalent(anchor); return anchor.node(); @@ -71,7 +71,7 @@ Node* DOMSelection::baseNode() const { if (!m_frame) return 0; - return rangeCompliantEquivalent(m_frame->selectionController()->selection().base()).node(); + return rangeCompliantEquivalent(m_frame->selection()->selection().base()).node(); } int DOMSelection::anchorOffset() const @@ -79,7 +79,7 @@ int DOMSelection::anchorOffset() const if (!m_frame) return 0; - const Selection& selection = m_frame->selectionController()->selection(); + const Selection& selection = m_frame->selection()->selection(); Position anchor = selection.isBaseFirst() ? selection.start() : selection.end(); anchor = rangeCompliantEquivalent(anchor); return anchor.offset(); @@ -89,7 +89,7 @@ int DOMSelection::baseOffset() const { if (!m_frame) return 0; - return rangeCompliantEquivalent(m_frame->selectionController()->selection().base()).offset(); + return rangeCompliantEquivalent(m_frame->selection()->selection().base()).offset(); } Node* DOMSelection::focusNode() const @@ -97,7 +97,7 @@ Node* DOMSelection::focusNode() const if (!m_frame) return 0; - const Selection& selection = m_frame->selectionController()->selection(); + const Selection& selection = m_frame->selection()->selection(); Position focus = selection.isBaseFirst() ? selection.end() : selection.start(); focus = rangeCompliantEquivalent(focus); return focus.node(); @@ -107,7 +107,7 @@ Node* DOMSelection::extentNode() const { if (!m_frame) return 0; - return rangeCompliantEquivalent(m_frame->selectionController()->selection().extent()).node(); + return rangeCompliantEquivalent(m_frame->selection()->selection().extent()).node(); } int DOMSelection::focusOffset() const @@ -115,7 +115,7 @@ int DOMSelection::focusOffset() const if (!m_frame) return 0; - const Selection& selection = m_frame->selectionController()->selection(); + const Selection& selection = m_frame->selection()->selection(); Position focus = selection.isBaseFirst() ? selection.end() : selection.start(); focus = rangeCompliantEquivalent(focus); return focus.offset(); @@ -125,14 +125,14 @@ int DOMSelection::extentOffset() const { if (!m_frame) return 0; - return rangeCompliantEquivalent(m_frame->selectionController()->selection().extent()).offset(); + return rangeCompliantEquivalent(m_frame->selection()->selection().extent()).offset(); } bool DOMSelection::isCollapsed() const { if (!m_frame) return false; - return !m_frame->selectionController()->isRange(); + return !m_frame->selection()->isRange(); } String DOMSelection::type() const @@ -140,11 +140,11 @@ String DOMSelection::type() const if (!m_frame) return String(); - SelectionController* selectionController = m_frame->selectionController(); + SelectionController* selection = m_frame->selection(); - if (selectionController->isNone()) + if (selection->isNone()) return "None"; - if (selectionController->isCaret()) + if (selection->isCaret()) return "Caret"; return "Range"; } @@ -153,7 +153,7 @@ int DOMSelection::rangeCount() const { if (!m_frame) return 0; - return m_frame->selectionController()->isNone() ? 0 : 1; + return m_frame->selection()->isNone() ? 0 : 1; } void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec) @@ -165,7 +165,7 @@ void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec) ec = INDEX_SIZE_ERR; return; } - m_frame->selectionController()->moveTo(VisiblePosition(node, offset, DOWNSTREAM)); + m_frame->selection()->moveTo(VisiblePosition(node, offset, DOWNSTREAM)); } void DOMSelection::collapseToEnd() @@ -173,8 +173,8 @@ void DOMSelection::collapseToEnd() if (!m_frame) return; - const Selection& selection = m_frame->selectionController()->selection(); - m_frame->selectionController()->moveTo(VisiblePosition(selection.end(), DOWNSTREAM)); + const Selection& selection = m_frame->selection()->selection(); + m_frame->selection()->moveTo(VisiblePosition(selection.end(), DOWNSTREAM)); } void DOMSelection::collapseToStart() @@ -182,15 +182,15 @@ void DOMSelection::collapseToStart() if (!m_frame) return; - const Selection& selection = m_frame->selectionController()->selection(); - m_frame->selectionController()->moveTo(VisiblePosition(selection.start(), DOWNSTREAM)); + const Selection& selection = m_frame->selection()->selection(); + m_frame->selection()->moveTo(VisiblePosition(selection.start(), DOWNSTREAM)); } void DOMSelection::empty() { if (!m_frame) return; - m_frame->selectionController()->moveTo(VisiblePosition()); + m_frame->selection()->moveTo(VisiblePosition()); } void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec) @@ -205,7 +205,7 @@ void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extent VisiblePosition visibleBase = VisiblePosition(baseNode, baseOffset, DOWNSTREAM); VisiblePosition visibleExtent = VisiblePosition(extentNode, extentOffset, DOWNSTREAM); - m_frame->selectionController()->moveTo(visibleBase, visibleExtent); + m_frame->selection()->moveTo(visibleBase, visibleExtent); } void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec) @@ -216,7 +216,7 @@ void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec) ec = INDEX_SIZE_ERR; return; } - m_frame->selectionController()->moveTo(VisiblePosition(node, offset, DOWNSTREAM)); + m_frame->selection()->moveTo(VisiblePosition(node, offset, DOWNSTREAM)); } void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString) @@ -224,52 +224,49 @@ void DOMSelection::modify(const String& alterString, const String& directionStri if (!m_frame) return; - String alterStringLower = alterString.lower(); SelectionController::EAlteration alter; - if (alterStringLower == "extend") + if (equalIgnoringCase(alterString, "extend")) alter = SelectionController::EXTEND; - else if (alterStringLower == "move") + else if (equalIgnoringCase(alterString, "move")) alter = SelectionController::MOVE; else return; - String directionStringLower = directionString.lower(); SelectionController::EDirection direction; - if (directionStringLower == "forward") + if (equalIgnoringCase(directionString, "forward")) direction = SelectionController::FORWARD; - else if (directionStringLower == "backward") + else if (equalIgnoringCase(directionString, "backward")) direction = SelectionController::BACKWARD; - else if (directionStringLower == "left") + else if (equalIgnoringCase(directionString, "left")) direction = SelectionController::LEFT; - else if (directionStringLower == "right") + else if (equalIgnoringCase(directionString, "right")) direction = SelectionController::RIGHT; else return; - String granularityStringLower = granularityString.lower(); TextGranularity granularity; - if (granularityStringLower == "character") + if (equalIgnoringCase(granularityString, "character")) granularity = CharacterGranularity; - else if (granularityStringLower == "word") + else if (equalIgnoringCase(granularityString, "word")) granularity = WordGranularity; - else if (granularityStringLower == "sentence") + else if (equalIgnoringCase(granularityString, "sentence")) granularity = SentenceGranularity; - else if (granularityStringLower == "line") + else if (equalIgnoringCase(granularityString, "line")) granularity = LineGranularity; - else if (granularityStringLower == "paragraph") + else if (equalIgnoringCase(granularityString, "paragraph")) granularity = ParagraphGranularity; - else if (granularityStringLower == "lineboundary") + else if (equalIgnoringCase(granularityString, "lineboundary")) granularity = LineBoundary; - else if (granularityStringLower == "sentenceboundary") + else if (equalIgnoringCase(granularityString, "sentenceboundary")) granularity = SentenceBoundary; - else if (granularityStringLower == "paragraphboundary") + else if (equalIgnoringCase(granularityString, "paragraphboundary")) granularity = ParagraphBoundary; - else if (granularityStringLower == "documentboundary") + else if (equalIgnoringCase(granularityString, "documentboundary")) granularity = DocumentBoundary; else return; - m_frame->selectionController()->modify(alter, direction, granularity, false); + m_frame->selection()->modify(alter, direction, granularity, false); } void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec) @@ -286,9 +283,9 @@ void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec) return; } - SelectionController* selectionController = m_frame->selectionController(); - selectionController->expandUsingGranularity(CharacterGranularity); - selectionController->setExtent(VisiblePosition(node, offset, DOWNSTREAM)); + SelectionController* selection = m_frame->selection(); + selection->expandUsingGranularity(CharacterGranularity); + selection->setExtent(VisiblePosition(node, offset, DOWNSTREAM)); } PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec) @@ -301,7 +298,7 @@ PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec) return 0; } - const Selection& selection = m_frame->selectionController()->selection(); + const Selection& selection = m_frame->selection()->selection(); return selection.toRange(); } @@ -309,7 +306,7 @@ void DOMSelection::removeAllRanges() { if (!m_frame) return; - m_frame->selectionController()->clear(); + m_frame->selection()->clear(); } void DOMSelection::addRange(Range* r) @@ -319,34 +316,34 @@ void DOMSelection::addRange(Range* r) if (!r) return; - SelectionController* selectionController = m_frame->selectionController(); + SelectionController* selection = m_frame->selection(); - if (selectionController->isNone()) { - selectionController->setSelection(Selection(r)); + if (selection->isNone()) { + selection->setSelection(Selection(r)); return; } - RefPtr<Range> range = selectionController->selection().toRange(); + RefPtr<Range> range = selection->selection().toRange(); ExceptionCode ec = 0; if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), ec) == -1) { // We don't support discontiguous selection. We don't do anything if r and range don't intersect. - if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) > -1) { + if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), ec) > -1) { if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1) // The original range and r intersect. - selectionController->setSelection(Selection(r->startPosition(), range->endPosition(), DOWNSTREAM)); + selection->setSelection(Selection(r->startPosition(), range->endPosition(), DOWNSTREAM)); else // r contains the original range. - selectionController->setSelection(Selection(r)); + selection->setSelection(Selection(r)); } } else { // We don't support discontiguous selection. We don't do anything if r and range don't intersect. - if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), ec) < 1) { + if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) < 1) { if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1) // The original range contains r. - selectionController->setSelection(Selection(range.get())); + selection->setSelection(Selection(range.get())); else // The original range and r intersect. - selectionController->setSelection(Selection(range->startPosition(), r->endPosition(), DOWNSTREAM)); + selection->setSelection(Selection(range->startPosition(), r->endPosition(), DOWNSTREAM)); } } } @@ -356,15 +353,15 @@ void DOMSelection::deleteFromDocument() if (!m_frame) return; - SelectionController* selectionController = m_frame->selectionController(); + SelectionController* selection = m_frame->selection(); - if (selectionController->isNone()) + if (selection->isNone()) return; if (isCollapsed()) - selectionController->modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity); + selection->modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity); - RefPtr<Range> selectedRange = selectionController->selection().toRange(); + RefPtr<Range> selectedRange = selection->selection().toRange(); ExceptionCode ec = 0; selectedRange->deleteContents(ec); @@ -379,14 +376,14 @@ bool DOMSelection::containsNode(const Node* n, bool allowPartial) const if (!m_frame) return false; - SelectionController* selectionController = m_frame->selectionController(); + SelectionController* selection = m_frame->selection(); - if (!n || selectionController->isNone()) + if (!n || selection->isNone()) return false; Node* parentNode = n->parentNode(); unsigned nodeIndex = n->nodeIndex(); - RefPtr<Range> selectedRange = selectionController->selection().toRange(); + RefPtr<Range> selectedRange = selection->selection().toRange(); if (!parentNode) return false; @@ -421,7 +418,7 @@ String DOMSelection::toString() if (!m_frame) return String(); - return plainText(m_frame->selectionController()->selection().toRange().get()); + return plainText(m_frame->selection()->selection().toRange().get()); } } // namespace WebCore diff --git a/WebCore/page/DOMSelection.h b/WebCore/page/DOMSelection.h index d4a579f..fd8d1fc 100644 --- a/WebCore/page/DOMSelection.h +++ b/WebCore/page/DOMSelection.h @@ -45,7 +45,7 @@ namespace WebCore { class DOMSelection : public RefCounted<DOMSelection> { public: - DOMSelection(Frame*); + static PassRefPtr<DOMSelection> create(Frame* frame) { return adoptRef(new DOMSelection(frame)); } Frame* frame() const; void disconnectFrame(); @@ -92,6 +92,8 @@ namespace WebCore { //TextRange *createRange(); private: + DOMSelection(Frame*); + Frame* m_frame; }; diff --git a/WebCore/page/DOMWindow.cpp b/WebCore/page/DOMWindow.cpp index f091c3d..4ab69ca 100644 --- a/WebCore/page/DOMWindow.cpp +++ b/WebCore/page/DOMWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,22 +30,31 @@ #include "CSSComputedStyleDeclaration.h" #include "CSSRuleList.h" #include "CSSStyleSelector.h" +#include "CString.h" #include "Chrome.h" #include "Console.h" #include "DOMSelection.h" #include "Document.h" #include "Element.h" +#include "EventListener.h" +#include "EventNames.h" +#include "ExceptionCode.h" #include "FloatRect.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameTree.h" #include "FrameView.h" +#include "HTMLFrameOwnerElement.h" #include "History.h" +#include "Location.h" #include "MessageEvent.h" +#include "Navigator.h" #include "Page.h" +#include "PageGroup.h" #include "PlatformScreen.h" #include "PlatformString.h" #include "Screen.h" +#include "SecurityOrigin.h" #include <algorithm> #include <wtf/MathExtras.h> @@ -53,11 +62,45 @@ #include "Database.h" #endif +#if ENABLE(DOM_STORAGE) +#include "LocalStorage.h" +#include "SessionStorage.h" +#include "Storage.h" +#include "StorageArea.h" +#endif + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "DOMApplicationCache.h" +#endif + using std::min; using std::max; namespace WebCore { +class PostMessageTimer : public TimerBase { +public: + PostMessageTimer(DOMWindow* window, PassRefPtr<MessageEvent> event, SecurityOrigin* targetOrigin) + : m_window(window) + , m_event(event) + , m_targetOrigin(targetOrigin) + { + } + + MessageEvent* event() const { return m_event.get(); } + SecurityOrigin* targetOrigin() const { return m_targetOrigin.get(); } + +private: + virtual void fired() + { + m_window->postMessageTimerFired(this); + } + + RefPtr<DOMWindow> m_window; + RefPtr<MessageEvent> m_event; + RefPtr<SecurityOrigin> m_targetOrigin; +}; + // This function: // 1) Validates the pending changes are not changing to NaN // 2) Constrains the window rect to no smaller than 100 in each dimension and no @@ -105,6 +148,8 @@ DOMWindow::DOMWindow(Frame* frame) DOMWindow::~DOMWindow() { + if (m_frame) + m_frame->clearFormerDOMWindow(this); } void DOMWindow::disconnectFrame() @@ -154,83 +199,214 @@ void DOMWindow::clear() if (m_console) m_console->disconnectFrame(); m_console = 0; + + if (m_navigator) + m_navigator->disconnectFrame(); + m_navigator = 0; + + if (m_location) + m_location->disconnectFrame(); + m_location = 0; + +#if ENABLE(DOM_STORAGE) + if (m_sessionStorage) + m_sessionStorage->disconnectFrame(); + m_sessionStorage = 0; + + if (m_localStorage) + m_localStorage->disconnectFrame(); + m_localStorage = 0; +#endif + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (m_applicationCache) + m_applicationCache->disconnectFrame(); + m_applicationCache = 0; +#endif } Screen* DOMWindow::screen() const { if (!m_screen) - m_screen = new Screen(m_frame); + m_screen = Screen::create(m_frame); return m_screen.get(); } History* DOMWindow::history() const { if (!m_history) - m_history = new History(m_frame); + m_history = History::create(m_frame); return m_history.get(); } BarInfo* DOMWindow::locationbar() const { if (!m_locationbar) - m_locationbar = new BarInfo(m_frame, BarInfo::Locationbar); + m_locationbar = BarInfo::create(m_frame, BarInfo::Locationbar); return m_locationbar.get(); } BarInfo* DOMWindow::menubar() const { if (!m_menubar) - m_menubar = new BarInfo(m_frame, BarInfo::Menubar); + m_menubar = BarInfo::create(m_frame, BarInfo::Menubar); return m_menubar.get(); } BarInfo* DOMWindow::personalbar() const { if (!m_personalbar) - m_personalbar = new BarInfo(m_frame, BarInfo::Personalbar); + m_personalbar = BarInfo::create(m_frame, BarInfo::Personalbar); return m_personalbar.get(); } BarInfo* DOMWindow::scrollbars() const { if (!m_scrollbars) - m_scrollbars = new BarInfo(m_frame, BarInfo::Scrollbars); + m_scrollbars = BarInfo::create(m_frame, BarInfo::Scrollbars); return m_scrollbars.get(); } BarInfo* DOMWindow::statusbar() const { if (!m_statusbar) - m_statusbar = new BarInfo(m_frame, BarInfo::Statusbar); + m_statusbar = BarInfo::create(m_frame, BarInfo::Statusbar); return m_statusbar.get(); } BarInfo* DOMWindow::toolbar() const { if (!m_toolbar) - m_toolbar = new BarInfo(m_frame, BarInfo::Toolbar); + m_toolbar = BarInfo::create(m_frame, BarInfo::Toolbar); return m_toolbar.get(); } Console* DOMWindow::console() const { if (!m_console) - m_console = new Console(m_frame); + m_console = Console::create(m_frame); return m_console.get(); } -#if ENABLE(CROSS_DOCUMENT_MESSAGING) -void DOMWindow::postMessage(const String& message, const String& domain, const String& uri, DOMWindow* source) const +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +DOMApplicationCache* DOMWindow::applicationCache() const +{ + if (!m_applicationCache) + m_applicationCache = DOMApplicationCache::create(m_frame); + return m_applicationCache.get(); +} +#endif + +Navigator* DOMWindow::navigator() const +{ + if (!m_navigator) + m_navigator = Navigator::create(m_frame); + return m_navigator.get(); +} + +Location* DOMWindow::location() const { - ExceptionCode ec; - document()->dispatchEvent(new MessageEvent(message, domain, uri, source), ec, true); + if (!m_location) + m_location = Location::create(m_frame); + return m_location.get(); +} + +#if ENABLE(DOM_STORAGE) +Storage* DOMWindow::sessionStorage() const +{ + if (m_sessionStorage) + return m_sessionStorage.get(); + + Page* page = m_frame->page(); + if (!page) + return 0; + + Document* document = m_frame->document(); + if (!document) + return 0; + + RefPtr<StorageArea> storageArea = page->sessionStorage()->storageArea(document->securityOrigin()); + m_sessionStorage = Storage::create(m_frame, storageArea.release()); + return m_sessionStorage.get(); +} + +Storage* DOMWindow::localStorage() const +{ + Document* document = this->document(); + if (!document) + return 0; + + Page* page = document->page(); + if (!page) + return 0; + + LocalStorage* localStorage = page->group().localStorage(); + RefPtr<StorageArea> storageArea = localStorage ? localStorage->storageArea(m_frame, document->securityOrigin()) : 0; + if (storageArea) + m_localStorage = Storage::create(m_frame, storageArea.release()); + + return m_localStorage.get(); } #endif +void DOMWindow::postMessage(const String& message, MessagePort* messagePort, const String& targetOrigin, DOMWindow* source, ExceptionCode& ec) +{ + if (!m_frame) + return; + + // Compute the target origin. We need to do this synchronously in order + // to generate the SYNTAX_ERR exception correctly. + RefPtr<SecurityOrigin> target; + if (targetOrigin != "*") { + target = SecurityOrigin::create(KURL(targetOrigin)); + if (target->isEmpty()) { + ec = SYNTAX_ERR; + return; + } + } + + RefPtr<MessagePort> newMessagePort; + if (messagePort) + newMessagePort = messagePort->clone(document(), ec); + if (ec) + return; + + // Capture the source of the message. We need to do this synchronously + // in order to capture the source of the message correctly. + Document* sourceDocument = source->document(); + if (!sourceDocument) + return; + String sourceOrigin = sourceDocument->securityOrigin()->toString(); + + // Schedule the message. + PostMessageTimer* timer = new PostMessageTimer(this, MessageEvent::create(message, sourceOrigin, "", source, newMessagePort), target.get()); + timer->startOneShot(0); +} + +void DOMWindow::postMessageTimerFired(PostMessageTimer* t) +{ + OwnPtr<PostMessageTimer> timer(t); + + if (!document()) + return; + + if (timer->targetOrigin()) { + // Check target origin now since the target document may have changed since the simer was scheduled. + if (!timer->targetOrigin()->isSameSchemeHostPort(document()->securityOrigin())) { + String message = String::format("Unable to post message to %s. Recipient has origin %s.\n", + timer->targetOrigin()->toString().utf8().data(), document()->securityOrigin()->toString().utf8().data()); + console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 0, String()); + return; + } + } + + document()->dispatchWindowEvent(timer->event()); +} + DOMSelection* DOMWindow::getSelection() { if (!m_selection) - m_selection = new DOMSelection(m_frame); + m_selection = DOMSelection::create(m_frame); return m_selection.get(); } @@ -239,13 +415,7 @@ Element* DOMWindow::frameElement() const if (!m_frame) return 0; - Document* doc = m_frame->document(); - ASSERT(doc); - if (!doc) - return 0; - - // FIXME: could this use m_frame->ownerElement() instead of going through the Document. - return doc->ownerElement(); + return m_frame->ownerElement(); } void DOMWindow::focus() @@ -396,8 +566,8 @@ int DOMWindow::innerHeight() const FrameView* view = m_frame->view(); if (!view) return 0; - - return view->height(); + + return static_cast<int>(view->height() / m_frame->pageZoomFactor()); } int DOMWindow::innerWidth() const @@ -409,7 +579,7 @@ int DOMWindow::innerWidth() const if (!view) return 0; - return view->width(); + return static_cast<int>(view->width() / m_frame->pageZoomFactor()); } int DOMWindow::screenX() const @@ -450,7 +620,7 @@ int DOMWindow::scrollX() const if (doc) doc->updateLayoutIgnorePendingStylesheets(); - return view->contentsX(); + return static_cast<int>(view->scrollX() / m_frame->pageZoomFactor()); } #ifdef ANDROID_ORIENTATION_SUPPORT @@ -474,7 +644,7 @@ int DOMWindow::scrollY() const if (doc) doc->updateLayoutIgnorePendingStylesheets(); - return view->contentsY(); + return static_cast<int>(view->scrollY() / m_frame->pageZoomFactor()); } bool DOMWindow::closed() const @@ -563,7 +733,7 @@ DOMWindow* DOMWindow::parent() const if (!m_frame) return 0; - Frame* parent = m_frame->tree()->parent(); + Frame* parent = m_frame->tree()->parent(true); if (parent) return parent->domWindow(); @@ -579,7 +749,7 @@ DOMWindow* DOMWindow::top() const if (!page) return 0; - return page->mainFrame()->domWindow(); + return m_frame->tree()->top(true)->domWindow(); } Document* DOMWindow::document() const @@ -596,8 +766,8 @@ PassRefPtr<CSSStyleDeclaration> DOMWindow::getComputedStyle(Element* elt, const if (!elt) return 0; - // FIXME: This needs to work with pseudo elements. - return new CSSComputedStyleDeclaration(elt); + // FIXME: This needs take pseudo elements into account. + return computedStyle(elt); } PassRefPtr<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* elt, const String& pseudoElt, bool authorOnly) const @@ -611,7 +781,7 @@ PassRefPtr<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* elt, const String return 0; if (!pseudoElt.isEmpty()) - return doc->styleSelector()->pseudoStyleRulesForElement(elt, pseudoElt.impl(), authorOnly); + return doc->styleSelector()->pseudoStyleRulesForElement(elt, pseudoElt, authorOnly); return doc->styleSelector()->styleRulesForElement(elt, authorOnly); } @@ -656,7 +826,7 @@ void DOMWindow::scrollBy(int x, int y) const if (!view) return; - view->scrollBy(x, y); + view->scrollBy(IntSize(x, y)); } void DOMWindow::scrollTo(int x, int y) const @@ -673,7 +843,9 @@ void DOMWindow::scrollTo(int x, int y) const if (!view) return; - view->setContentsPos(x, y); + int zoomedX = static_cast<int>(x * m_frame->pageZoomFactor()); + int zoomedY = static_cast<int>(y * m_frame->pageZoomFactor()); + view->setScrollPosition(IntPoint(zoomedX, zoomedY)); } void DOMWindow::moveBy(float x, float y) const @@ -685,6 +857,9 @@ void DOMWindow::moveBy(float x, float y) const if (!page) return; + if (m_frame != page->mainFrame()) + return; + FloatRect fr = page->chrome()->windowRect(); FloatRect update = fr; update.move(x, y); @@ -702,6 +877,9 @@ void DOMWindow::moveTo(float x, float y) const if (!page) return; + if (m_frame != page->mainFrame()) + return; + FloatRect fr = page->chrome()->windowRect(); FloatRect sr = screenAvailableRect(page->mainFrame()->view()); fr.setLocation(sr.location()); @@ -721,6 +899,9 @@ void DOMWindow::resizeBy(float x, float y) const if (!page) return; + if (m_frame != page->mainFrame()) + return; + FloatRect fr = page->chrome()->windowRect(); FloatSize dest = fr.size() + FloatSize(x, y); FloatRect update(fr.location(), dest); @@ -737,6 +918,9 @@ void DOMWindow::resizeTo(float width, float height) const if (!page) return; + if (m_frame != page->mainFrame()) + return; + FloatRect fr = page->chrome()->windowRect(); FloatSize dest = FloatSize(width, height); FloatRect update(fr.location(), dest); @@ -744,4 +928,352 @@ void DOMWindow::resizeTo(float width, float height) const page->chrome()->setWindowRect(fr); } +inline void DOMWindow::setInlineEventListenerForType(const AtomicString& eventType, PassRefPtr<EventListener> eventListener) +{ + Document* document = this->document(); + if (!document) + return; + document->setWindowInlineEventListenerForType(eventType, eventListener); +} + +inline EventListener* DOMWindow::inlineEventListenerForType(const AtomicString& eventType) const +{ + Document* document = this->document(); + if (!document) + return 0; + return document->windowInlineEventListenerForType(eventType); +} + +EventListener* DOMWindow::onabort() const +{ + return inlineEventListenerForType(eventNames().abortEvent); +} + +void DOMWindow::setOnabort(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().abortEvent, eventListener); +} + +EventListener* DOMWindow::onblur() const +{ + return inlineEventListenerForType(eventNames().blurEvent); +} + +void DOMWindow::setOnblur(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().blurEvent, eventListener); +} + +EventListener* DOMWindow::onchange() const +{ + return inlineEventListenerForType(eventNames().changeEvent); +} + +void DOMWindow::setOnchange(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().changeEvent, eventListener); +} + +EventListener* DOMWindow::onclick() const +{ + return inlineEventListenerForType(eventNames().clickEvent); +} + +void DOMWindow::setOnclick(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().clickEvent, eventListener); +} + +EventListener* DOMWindow::ondblclick() const +{ + return inlineEventListenerForType(eventNames().dblclickEvent); +} + +void DOMWindow::setOndblclick(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().dblclickEvent, eventListener); +} + +EventListener* DOMWindow::onerror() const +{ + return inlineEventListenerForType(eventNames().errorEvent); +} + +void DOMWindow::setOnerror(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().errorEvent, eventListener); +} + +EventListener* DOMWindow::onfocus() const +{ + return inlineEventListenerForType(eventNames().focusEvent); +} + +void DOMWindow::setOnfocus(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().focusEvent, eventListener); +} + +EventListener* DOMWindow::onkeydown() const +{ + return inlineEventListenerForType(eventNames().keydownEvent); +} + +void DOMWindow::setOnkeydown(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().keydownEvent, eventListener); +} + +EventListener* DOMWindow::onkeypress() const +{ + return inlineEventListenerForType(eventNames().keypressEvent); +} + +void DOMWindow::setOnkeypress(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().keypressEvent, eventListener); +} + +EventListener* DOMWindow::onkeyup() const +{ + return inlineEventListenerForType(eventNames().keyupEvent); +} + +void DOMWindow::setOnkeyup(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().keyupEvent, eventListener); +} + +EventListener* DOMWindow::onload() const +{ + return inlineEventListenerForType(eventNames().loadEvent); +} + +void DOMWindow::setOnload(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().loadEvent, eventListener); +} + +EventListener* DOMWindow::onmousedown() const +{ + return inlineEventListenerForType(eventNames().mousedownEvent); +} + +void DOMWindow::setOnmousedown(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().mousedownEvent, eventListener); +} + +EventListener* DOMWindow::onmousemove() const +{ + return inlineEventListenerForType(eventNames().mousemoveEvent); +} + +void DOMWindow::setOnmousemove(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().mousemoveEvent, eventListener); +} + +EventListener* DOMWindow::onmouseout() const +{ + return inlineEventListenerForType(eventNames().mouseoutEvent); +} + +void DOMWindow::setOnmouseout(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().mouseoutEvent, eventListener); +} + +EventListener* DOMWindow::onmouseover() const +{ + return inlineEventListenerForType(eventNames().mouseoverEvent); +} + +void DOMWindow::setOnmouseover(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().mouseoverEvent, eventListener); +} + +EventListener* DOMWindow::onmouseup() const +{ + return inlineEventListenerForType(eventNames().mouseupEvent); +} + +void DOMWindow::setOnmouseup(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().mouseupEvent, eventListener); +} + +EventListener* DOMWindow::onmousewheel() const +{ + return inlineEventListenerForType(eventNames().mousewheelEvent); +} + +void DOMWindow::setOnmousewheel(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().mousewheelEvent, eventListener); +} + +EventListener* DOMWindow::onreset() const +{ + return inlineEventListenerForType(eventNames().resetEvent); +} + +void DOMWindow::setOnreset(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().resetEvent, eventListener); +} + +EventListener* DOMWindow::onresize() const +{ + return inlineEventListenerForType(eventNames().resizeEvent); +} + +void DOMWindow::setOnresize(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().resizeEvent, eventListener); +} + +EventListener* DOMWindow::onscroll() const +{ + return inlineEventListenerForType(eventNames().scrollEvent); +} + +void DOMWindow::setOnscroll(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().scrollEvent, eventListener); +} + +EventListener* DOMWindow::onsearch() const +{ + return inlineEventListenerForType(eventNames().searchEvent); +} + +void DOMWindow::setOnsearch(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().searchEvent, eventListener); +} + +EventListener* DOMWindow::onselect() const +{ + return inlineEventListenerForType(eventNames().selectEvent); +} + +void DOMWindow::setOnselect(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().selectEvent, eventListener); +} + +EventListener* DOMWindow::onsubmit() const +{ + return inlineEventListenerForType(eventNames().submitEvent); +} + +void DOMWindow::setOnsubmit(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().submitEvent, eventListener); +} + +EventListener* DOMWindow::onunload() const +{ + return inlineEventListenerForType(eventNames().unloadEvent); +} + +void DOMWindow::setOnunload(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().unloadEvent, eventListener); +} + +EventListener* DOMWindow::onbeforeunload() const +{ + return inlineEventListenerForType(eventNames().beforeunloadEvent); +} + +void DOMWindow::setOnbeforeunload(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().beforeunloadEvent, eventListener); +} + +EventListener* DOMWindow::onwebkitanimationstart() const +{ + return inlineEventListenerForType(eventNames().webkitAnimationStartEvent); +} + +void DOMWindow::setOnwebkitanimationstart(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().webkitAnimationStartEvent, eventListener); +} + +EventListener* DOMWindow::onwebkitanimationiteration() const +{ + return inlineEventListenerForType(eventNames().webkitAnimationIterationEvent); +} + +void DOMWindow::setOnwebkitanimationiteration(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().webkitAnimationIterationEvent, eventListener); +} + +EventListener* DOMWindow::onwebkitanimationend() const +{ + return inlineEventListenerForType(eventNames().webkitAnimationEndEvent); +} + +void DOMWindow::setOnwebkitanimationend(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().webkitAnimationEndEvent, eventListener); +} + +EventListener* DOMWindow::onwebkittransitionend() const +{ + return inlineEventListenerForType(eventNames().webkitTransitionEndEvent); +} + +void DOMWindow::setOnwebkittransitionend(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().webkitTransitionEndEvent, eventListener); +} + +#if ENABLE(TOUCH_EVENTS) // Android +EventListener* DOMWindow::ontouchstart() const +{ + return inlineEventListenerForType(eventNames().touchstartEvent); +} + +void DOMWindow::setOntouchstart(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().touchstartEvent, eventListener); +} + +EventListener* DOMWindow::ontouchend() const +{ + return inlineEventListenerForType(eventNames().touchendEvent); +} + +void DOMWindow::setOntouchend(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().touchendEvent, eventListener); +} + +EventListener* DOMWindow::ontouchmove() const +{ + return inlineEventListenerForType(eventNames().touchmoveEvent); +} + +void DOMWindow::setOntouchmove(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().touchmoveEvent, eventListener); +} + +EventListener* DOMWindow::ontouchcancel() const +{ + return inlineEventListenerForType(eventNames().touchcancelEvent); +} + +void DOMWindow::setOntouchcancel(PassRefPtr<EventListener> eventListener) +{ + setInlineEventListenerForType(eventNames().touchcancelEvent, eventListener); +} +#endif + } // namespace WebCore diff --git a/WebCore/page/DOMWindow.h b/WebCore/page/DOMWindow.h index c814126..da84e35 100644 --- a/WebCore/page/DOMWindow.h +++ b/WebCore/page/DOMWindow.h @@ -26,9 +26,11 @@ #ifndef DOMWindow_h #define DOMWindow_h +#include "KURL.h" #include "PlatformString.h" -#include <wtf/RefCounted.h> +#include "SecurityOrigin.h" #include <wtf/Forward.h> +#include <wtf/RefCounted.h> #include <wtf/RefPtr.h> namespace WebCore { @@ -41,16 +43,30 @@ namespace WebCore { class Database; class Document; class Element; + class EventListener; class FloatRect; class Frame; class History; + class Location; + class MessagePort; + class Navigator; + class PostMessageTimer; class Screen; +#if ENABLE(DOM_STORAGE) + class SessionStorage; + class Storage; +#endif + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + class DOMApplicationCache; +#endif + typedef int ExceptionCode; class DOMWindow : public RefCounted<DOMWindow> { public: - DOMWindow(Frame*); + static PassRefPtr<DOMWindow> create(Frame* frame) { return adoptRef(new DOMWindow(frame)); } virtual ~DOMWindow(); Frame* frame() { return m_frame; } @@ -58,6 +74,12 @@ namespace WebCore { void clear(); + void setSecurityOrigin(SecurityOrigin* securityOrigin) { m_securityOrigin = securityOrigin; } + SecurityOrigin* securityOrigin() const { return m_securityOrigin.get(); } + + void setURL(const KURL& url) { m_url = url; } + KURL url() const { return m_url; } + static void adjustWindowRect(const FloatRect& screen, FloatRect& window, const FloatRect& pendingChanges); // DOM Level 0 @@ -69,6 +91,9 @@ namespace WebCore { BarInfo* scrollbars() const; BarInfo* statusbar() const; BarInfo* toolbar() const; + Navigator* navigator() const; + Navigator* clientInformation() const { return navigator(); } + Location* location() const; DOMSelection* getSelection(); @@ -143,12 +168,21 @@ namespace WebCore { PassRefPtr<Database> openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, ExceptionCode&); #endif +#if ENABLE(DOM_STORAGE) + // HTML 5 key/value storage + Storage* sessionStorage() const; + Storage* localStorage() const; +#endif + Console* console() const; - -#if ENABLE(CROSS_DOCUMENT_MESSAGING) - void postMessage(const String& message, const String& domain, const String& uri, DOMWindow* source) const; + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + DOMApplicationCache* applicationCache() const; #endif + void postMessage(const String& message, MessagePort*, const String& targetOrigin, DOMWindow* source, ExceptionCode&); + void postMessageTimerFired(PostMessageTimer*); + void scrollBy(int x, int y) const; void scrollTo(int x, int y) const; void scroll(int x, int y) const { scrollTo(x, y); } @@ -159,7 +193,106 @@ namespace WebCore { void resizeBy(float x, float y) const; void resizeTo(float width, float height) const; + EventListener* onabort() const; + void setOnabort(PassRefPtr<EventListener>); + EventListener* onblur() const; + void setOnblur(PassRefPtr<EventListener>); + EventListener* onchange() const; + void setOnchange(PassRefPtr<EventListener>); + EventListener* onclick() const; + void setOnclick(PassRefPtr<EventListener>); + EventListener* ondblclick() const; + void setOndblclick(PassRefPtr<EventListener>); + EventListener* onerror() const; + void setOnerror(PassRefPtr<EventListener>); + EventListener* onfocus() const; + void setOnfocus(PassRefPtr<EventListener>); + EventListener* onkeydown() const; + void setOnkeydown(PassRefPtr<EventListener>); + EventListener* onkeypress() const; + void setOnkeypress(PassRefPtr<EventListener>); + EventListener* onkeyup() const; + void setOnkeyup(PassRefPtr<EventListener>); + EventListener* onload() const; + void setOnload(PassRefPtr<EventListener>); + EventListener* onmousedown() const; + void setOnmousedown(PassRefPtr<EventListener>); + EventListener* onmousemove() const; + void setOnmousemove(PassRefPtr<EventListener>); + EventListener* onmouseout() const; + void setOnmouseout(PassRefPtr<EventListener>); + EventListener* onmouseover() const; + void setOnmouseover(PassRefPtr<EventListener>); + EventListener* onmouseup() const; + void setOnmouseup(PassRefPtr<EventListener>); + EventListener* onmousewheel() const; + void setOnmousewheel(PassRefPtr<EventListener>); + EventListener* onreset() const; + void setOnreset(PassRefPtr<EventListener>); + EventListener* onresize() const; + void setOnresize(PassRefPtr<EventListener>); + EventListener* onscroll() const; + void setOnscroll(PassRefPtr<EventListener>); + EventListener* onsearch() const; + void setOnsearch(PassRefPtr<EventListener>); + EventListener* onselect() const; + void setOnselect(PassRefPtr<EventListener>); + EventListener* onsubmit() const; + void setOnsubmit(PassRefPtr<EventListener>); + EventListener* onunload() const; + void setOnunload(PassRefPtr<EventListener>); + EventListener* onbeforeunload() const; + void setOnbeforeunload(PassRefPtr<EventListener>); + EventListener* onwebkitanimationstart() const; + void setOnwebkitanimationstart(PassRefPtr<EventListener>); + EventListener* onwebkitanimationiteration() const; + void setOnwebkitanimationiteration(PassRefPtr<EventListener>); + EventListener* onwebkitanimationend() const; + void setOnwebkitanimationend(PassRefPtr<EventListener>); + EventListener* onwebkittransitionend() const; + void setOnwebkittransitionend(PassRefPtr<EventListener>); +#if ENABLE(TOUCH_EVENTS) // Android + EventListener* ontouchstart() const; + void setOntouchstart(PassRefPtr<EventListener>); + EventListener* ontouchend() const; + void setOntouchend(PassRefPtr<EventListener>); + EventListener* ontouchmove() const; + void setOntouchmove(PassRefPtr<EventListener>); + EventListener* ontouchcancel() const; + void setOntouchcancel(PassRefPtr<EventListener>); +#endif + + // These methods are used for GC marking. See JSDOMWindow::mark() in + // JSDOMWindowCustom.cpp. + Screen* optionalScreen() const { return m_screen.get(); } + DOMSelection* optionalSelection() const { return m_selection.get(); } + History* optionalHistory() const { return m_history.get(); } + BarInfo* optionalLocationbar() const { return m_locationbar.get(); } + BarInfo* optionalMenubar() const { return m_menubar.get(); } + BarInfo* optionalPersonalbar() const { return m_personalbar.get(); } + BarInfo* optionalScrollbars() const { return m_scrollbars.get(); } + BarInfo* optionalStatusbar() const { return m_statusbar.get(); } + BarInfo* optionalToolbar() const { return m_toolbar.get(); } + Console* optionalConsole() const { return m_console.get(); } + Navigator* optionalNavigator() const { return m_navigator.get(); } + Location* optionalLocation() const { return m_location.get(); } +#if ENABLE(DOM_STORAGE) + Storage* optionalSessionStorage() const { return m_sessionStorage.get(); } + Storage* optionalLocalStorage() const { return m_sessionStorage.get(); } +#endif +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + DOMApplicationCache* optionalApplicationCache() const { return m_applicationCache.get(); } +#endif + private: + DOMWindow(Frame*); + + void setInlineEventListenerForType(const AtomicString& eventType, PassRefPtr<EventListener>); + EventListener* inlineEventListenerForType(const AtomicString& eventType) const; + + RefPtr<SecurityOrigin> m_securityOrigin; + KURL m_url; + Frame* m_frame; mutable RefPtr<Screen> m_screen; mutable RefPtr<DOMSelection> m_selection; @@ -171,6 +304,15 @@ namespace WebCore { mutable RefPtr<BarInfo> m_statusbar; mutable RefPtr<BarInfo> m_toolbar; mutable RefPtr<Console> m_console; + mutable RefPtr<Navigator> m_navigator; + mutable RefPtr<Location> m_location; +#if ENABLE(DOM_STORAGE) + mutable RefPtr<Storage> m_sessionStorage; + mutable RefPtr<Storage> m_localStorage; +#endif +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + mutable RefPtr<DOMApplicationCache> m_applicationCache; +#endif }; } // namespace WebCore diff --git a/WebCore/page/DOMWindow.idl b/WebCore/page/DOMWindow.idl index 33850d0..ce519b5 100644 --- a/WebCore/page/DOMWindow.idl +++ b/WebCore/page/DOMWindow.idl @@ -26,14 +26,20 @@ module window { interface [ - LegacyParent=KJS::Window, - DoNotCache, CheckDomainSecurity, - GenerateNativeConverter, + CustomDefineGetter, + CustomDefineSetter, + CustomDeleteProperty, CustomGetOwnPropertySlot, + CustomGetPropertyAttributes, + CustomGetPropertyNames, + CustomLookupGetter, + CustomLookupSetter, + CustomMarkFunction, + CustomNativeConverter, CustomPutFunction, - CustomDeleteProperty, - CustomGetPropertyNames + GenerateNativeConverter, + LegacyParent=JSDOMWindowBase ] DOMWindow { // DOM Level 0 readonly attribute Screen screen; @@ -44,6 +50,9 @@ module window { attribute [Replaceable] BarInfo scrollbars; attribute [Replaceable] BarInfo statusbar; attribute [Replaceable] BarInfo toolbar; + attribute [Replaceable] Navigator navigator; + attribute [Replaceable] Navigator clientInformation; + attribute [DoNotCheckDomainSecurity, CustomSetter] Location location; DOMSelection getSelection(); @@ -130,18 +139,85 @@ module window { in [Optional] boolean authorOnly); attribute [Replaceable] double devicePixelRatio; -#if defined(ENABLE_DATABASE) +#if ENABLE_OFFLINE_WEB_APPLICATIONS + readonly attribute DOMApplicationCache applicationCache; +#endif +#if ENABLE_DATABASE Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName, in unsigned long estimatedSize) raises(DOMException); #endif +#if ENABLE_DOM_STORAGE + readonly attribute Storage sessionStorage; + readonly attribute Storage localStorage; +#endif attribute [Replaceable] Console console; -#if defined(ENABLE_CROSS_DOCUMENT_MESSAGING) // cross-document messaging - [DoNotCheckDomainSecurity, Custom] void postMessage(in DOMString message); + [DoNotCheckDomainSecurity, Custom] void postMessage(in DOMString message, in [Optional] MessagePort messagePort, in DOMString targetOrigin) + raises(DOMException); + + // Timers + [Custom] long setTimeout(in TimeoutHandler handler, in long timeout); + // [Custom] long setTimeout(in DOMString code, in long timeout); + [Custom] void clearTimeout(in long handle); + + [Custom] long setInterval(in TimeoutHandler handler, in long timeout); + // [Custom] long setInterval(in DOMString code, in long timeout); + [Custom] void clearInterval(in long handle); + + // Base64 + [Custom] DOMString atob(in DOMString string) + raises(DOMException); + [Custom] DOMString btoa(in DOMString string) + raises(DOMException); + + // Events + attribute [ProtectedListener] EventListener onabort; + attribute [ProtectedListener] EventListener onblur; + attribute [ProtectedListener] EventListener onchange; + attribute [ProtectedListener] EventListener onclick; + attribute [ProtectedListener] EventListener ondblclick; + attribute [ProtectedListener] EventListener onerror; + attribute [ProtectedListener] EventListener onfocus; + attribute [ProtectedListener] EventListener onkeydown; + attribute [ProtectedListener] EventListener onkeypress; + attribute [ProtectedListener] EventListener onkeyup; + attribute [ProtectedListener] EventListener onload; + attribute [ProtectedListener] EventListener onmousedown; + attribute [ProtectedListener] EventListener onmousemove; + attribute [ProtectedListener] EventListener onmouseout; + attribute [ProtectedListener] EventListener onmouseover; + attribute [ProtectedListener] EventListener onmouseup; + attribute [ProtectedListener] EventListener onmousewheel; + attribute [ProtectedListener] EventListener onreset; + attribute [ProtectedListener] EventListener onresize; + attribute [ProtectedListener] EventListener onscroll; + attribute [ProtectedListener] EventListener onsearch; + attribute [ProtectedListener] EventListener onselect; + attribute [ProtectedListener] EventListener onsubmit; + attribute [ProtectedListener] EventListener onunload; + attribute [ProtectedListener] EventListener onbeforeunload; + attribute [ProtectedListener] EventListener onwebkitanimationstart; + attribute [ProtectedListener] EventListener onwebkitanimationiteration; + attribute [ProtectedListener] EventListener onwebkitanimationend; + attribute [ProtectedListener] EventListener onwebkittransitionend; +#if ENABLE_TOUCH_EVENTS + attribute [ProtectedListener] EventListener ontouchstart; + attribute [ProtectedListener] EventListener ontouchend; + attribute [ProtectedListener] EventListener ontouchmove; + attribute [ProtectedListener] EventListener ontouchcancel; #endif + // EventTarget interface + [Custom] void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + [Custom] void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + // FIXME: Implement dispatchEvent + #if defined(LANGUAGE_JAVASCRIPT) // Global constructors attribute StyleSheetConstructor StyleSheet; @@ -158,6 +234,9 @@ module window { attribute CSSMediaRuleConstructor CSSMediaRule; attribute CSSPageRuleConstructor CSSPageRule; attribute CSSStyleRuleConstructor CSSStyleRule; + + attribute CSSVariablesRuleConstructor CSSVariablesRule; + attribute CSSVariablesDeclarationConstructor CSSVariablesDeclaration; attribute CSSStyleDeclarationConstructor CSSStyleDeclaration; attribute MediaListConstructor MediaList; @@ -256,6 +335,11 @@ module window { attribute HTMLTitleElementConstructor HTMLTitleElement; attribute HTMLUListElementConstructor HTMLUListElement; + attribute HTMLCollectionConstructor HTMLCollection; + + attribute CanvasRenderingContext2DConstructor CanvasRenderingContext2D; + attribute TextMetricsConstructor TextMetrics; + attribute EventConstructor Event; attribute KeyboardEventConstructor KeyboardEvent; attribute MouseEventConstructor MouseEvent; @@ -265,7 +349,18 @@ module window { attribute TextEventConstructor TextEvent; attribute UIEventConstructor UIEvent; attribute WheelEventConstructor WheelEvent; + attribute MessageEventConstructor MessageEvent; attribute EventExceptionConstructor EventException; +#if ENABLE_TOUCH_EVENTS + attribute TouchEventConstructor TouchEvent; +#endif + + attribute MessagePortConstructor MessagePort; + + attribute ClipboardConstructor Clipboard; + + attribute FileConstructor File; + attribute FileListConstructor FileList; attribute NodeFilterConstructor NodeFilter; attribute RangeConstructor Range; @@ -278,26 +373,28 @@ module window { attribute DOMParserConstructor DOMParser; attribute XMLSerializerConstructor XMLSerializer; + attribute XMLHttpRequestUploadConstructor XMLHttpRequestUpload; attribute XMLHttpRequestExceptionConstructor XMLHttpRequestException; -#if defined(ENABLE_CROSS_DOCUMENT_MESSAGING) - attribute MessageEventConstructor MessageEvent; +#if ENABLE_DOM_STORAGE + attribute StorageConstructor Storage; + attribute StorageEventConstructor StorageEvent; #endif -#if defined(ENABLE_VIDEO) +#if ENABLE_VIDEO attribute HTMLAudioElementConstructor HTMLAudioElement; attribute HTMLMediaElementConstructor HTMLMediaElement; attribute HTMLVideoElementConstructor HTMLVideoElement; attribute MediaErrorConstructor MediaError; #endif -#if defined(ENABLE_XPATH) +#if ENABLE_XPATH attribute XPathEvaluatorConstructor XPathEvaluator; attribute XPathResultConstructor XPathResult; attribute XPathExceptionConstructor XPathException; #endif -#if defined(ENABLE_SVG) +#if ENABLE_SVG attribute SVGAngleConstructor SVGAngle; attribute SVGColorConstructor SVGColor; // attribute SVGCSSRuleConstructor SVGCSSRule; @@ -316,7 +413,7 @@ module window { // attribute SVGZoomAndPanConstructor SVGZoomAndPan; #endif -#if defined(ENABLED_SVG_FILTERS) +#if ENABLE_SVG_FILTERS attribute SVGComponentTransferFunctionElementConstructor SVGComponentTransferFunctionElement; attribute SVGFEBlendElementConstructor SVGFEBlendElement; attribute SVGFEColorMatrixElementConstructor SVGFEColorMatrixElement; diff --git a/WebCore/page/DragController.cpp b/WebCore/page/DragController.cpp index 8eec6e0..268397e 100644 --- a/WebCore/page/DragController.cpp +++ b/WebCore/page/DragController.cpp @@ -51,7 +51,6 @@ #include "MoveSelectionCommand.h" #include "Node.h" #include "Page.h" -#include "PluginInfoStore.h" #include "RenderFileUploadControl.h" #include "RenderImage.h" #include "ReplaceSelectionCommand.h" @@ -60,6 +59,7 @@ #include "Settings.h" #include "SystemTime.h" #include "Text.h" +#include "htmlediting.h" #include "markup.h" #include <wtf/RefPtr.h> @@ -126,10 +126,10 @@ static PassRefPtr<DocumentFragment> documentFragmentFromDragData(DragData* dragD return 0; } -bool DragController::dragIsMove(SelectionController* selectionController, DragData* dragData) +bool DragController::dragIsMove(SelectionController* selection, DragData* dragData) { return m_document == m_dragInitiator - && selectionController->isContentEditable() + && selection->isContentEditable() && !isCopyKeyDown(); } @@ -288,7 +288,7 @@ DragOperation DragController::tryDocumentDrag(DragData* dragData, DragDestinatio m_page->dragCaretController()->setSelection(dragCaret); } - return dragIsMove(innerFrame->selectionController(), dragData) ? DragOperationMove : DragOperationCopy; + return dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy; } m_page->dragCaretController()->clear(); @@ -313,13 +313,13 @@ DragOperation DragController::operationForLoad(DragData* dragData) static bool setSelectionToDragCaret(Frame* frame, Selection& dragCaret, RefPtr<Range>& range, const IntPoint& point) { - frame->selectionController()->setSelection(dragCaret); - if (frame->selectionController()->isNone()) { + frame->selection()->setSelection(dragCaret); + if (frame->selection()->isNone()) { dragCaret = frame->visiblePositionForPoint(point); - frame->selectionController()->setSelection(dragCaret); + frame->selection()->setSelection(dragCaret); range = dragCaret.toRange(); } - return !frame->selectionController()->isNone() && frame->selectionController()->isContentEditable(); + return !frame->selection()->isNone() && frame->selection()->isContentEditable(); } bool DragController::concludeDrag(DragData* dragData, DragDestinationAction actionMask) @@ -343,7 +343,7 @@ bool DragController::concludeDrag(DragData* dragData, DragDestinationAction acti return false; if (!innerFrame) return false; - RefPtr<Range> innerRange = innerFrame->selectionController()->toRange(); + RefPtr<Range> innerRange = innerFrame->selection()->toRange(); RefPtr<CSSStyleDeclaration> style = m_document->createCSSStyleDeclaration(); ExceptionCode ec; style->setProperty("color", color.name(), ec); @@ -360,6 +360,10 @@ bool DragController::concludeDrag(DragData* dragData, DragDestinationAction acti } if (HTMLInputElement* fileInput = asFileInput(element)) { + + if (!fileInput->isEnabled()) + return false; + if (!dragData->containsFiles()) return false; @@ -376,8 +380,7 @@ bool DragController::concludeDrag(DragData* dragData, DragDestinationAction acti if (!renderer) return false; - // Only take the first filename as <input type="file" /> can only accept one - renderer->receiveDroppedFile(filenames[0]); + renderer->receiveDroppedFiles(filenames); return true; } @@ -391,7 +394,7 @@ bool DragController::concludeDrag(DragData* dragData, DragDestinationAction acti return false; DocLoader* loader = range->ownerDocument()->docLoader(); loader->setAllowStaleResources(true); - if (dragIsMove(innerFrame->selectionController(), dragData) || dragCaret.isContentRichlyEditable()) { + if (dragIsMove(innerFrame->selection(), dragData) || dragCaret.isContentRichlyEditable()) { bool chosePlainText = false; RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, range, true, chosePlainText); if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, range, EditorInsertActionDropped)) { @@ -400,14 +403,14 @@ bool DragController::concludeDrag(DragData* dragData, DragDestinationAction acti } m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); - if (dragIsMove(innerFrame->selectionController(), dragData)) { + if (dragIsMove(innerFrame->selection(), dragData)) { bool smartMove = innerFrame->selectionGranularity() == WordGranularity && innerFrame->editor()->smartInsertDeleteEnabled() && dragData->canSmartReplace(); - applyCommand(new MoveSelectionCommand(fragment, dragCaret.base(), smartMove)); + applyCommand(MoveSelectionCommand::create(fragment, dragCaret.base(), smartMove)); } else { if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) - applyCommand(new ReplaceSelectionCommand(m_document, fragment, true, dragData->canSmartReplace(), chosePlainText)); + applyCommand(ReplaceSelectionCommand::create(m_document, fragment, true, dragData->canSmartReplace(), chosePlainText)); } } else { String text = dragData->asPlainText(); @@ -418,7 +421,7 @@ bool DragController::concludeDrag(DragData* dragData, DragDestinationAction acti m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData); if (setSelectionToDragCaret(innerFrame, dragCaret, range, point)) - applyCommand(new ReplaceSelectionCommand(m_document, createFragmentFromText(range.get(), text), true, false, true)); + applyCommand(ReplaceSelectionCommand::create(m_document, createFragmentFromText(range.get(), text), true, false, true)); } loader->setAllowStaleResources(false); @@ -435,7 +438,7 @@ bool DragController::canProcessDrag(DragData* dragData) IntPoint point = m_page->mainFrame()->view()->windowToContents(dragData->clientPosition()); HitTestResult result = HitTestResult(point); - if (!m_page->mainFrame()->renderer()) + if (!m_page->mainFrame()->contentRenderer()) return false; result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, true); @@ -499,7 +502,7 @@ bool DragController::mayStartDragAtEventLocation(const Frame* frame, const IntPo ASSERT(frame); ASSERT(frame->settings()); - if (!frame->view() || !frame->renderer()) + if (!frame->view() || !frame->contentRenderer()) return false; HitTestResult mouseDownTarget = HitTestResult(framePos); @@ -554,7 +557,7 @@ static void prepareClipboardForImageDrag(Frame* src, Clipboard* clipboard, Eleme ExceptionCode ec = 0; range->selectNode(node, ec); ASSERT(ec == 0); - src->selectionController()->setSelection(Selection(range.get(), DOWNSTREAM)); + src->selection()->setSelection(Selection(range.get(), DOWNSTREAM)); clipboard->declareAndWriteDragImage(node, !linkURL.isEmpty() ? linkURL : imageURL, label, src); } @@ -594,7 +597,7 @@ bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation s ASSERT(src); ASSERT(clipboard); - if (!src->view() || !src->renderer()) + if (!src->view() || !src->contentRenderer()) return false; HitTestResult dragSource = HitTestResult(dragOrigin); @@ -650,7 +653,17 @@ bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation s // Simplify whitespace so the title put on the clipboard resembles what the user sees // on the web page. This includes replacing newlines with spaces. clipboard->writeURL(linkURL, dragSource.textContent().simplifyWhiteSpace(), src); - + + if (src->selection()->isCaret() && src->selection()->isContentEditable()) { + // a user can initiate a drag on a link without having any text + // selected. In this case, we should expand the selection to + // the enclosing anchor element + Position pos = src->selection()->base(); + Node* node = enclosingAnchorElement(pos); + if (node) + src->selection()->setSelection(Selection::selectionFromContentsOfNode(node)); + } + m_client->willPerformDragSourceAction(DragSourceActionLink, dragOrigin, clipboard); if (!dragImage) { dragImage = m_client->createDragImageForLink(linkURL, dragSource.textContent(), src); @@ -660,7 +673,7 @@ bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation s } doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, clipboard, src, true); } else if (isSelected && (m_dragSourceAction & DragSourceActionSelection)) { - RefPtr<Range> selectionRange = src->selectionController()->toRange(); + RefPtr<Range> selectionRange = src->selection()->toRange(); ASSERT(selectionRange); if (!clipboard->hasData()) clipboard->writeRange(selectionRange.get(), src); diff --git a/WebCore/page/EditorClient.h b/WebCore/page/EditorClient.h new file mode 100644 index 0000000..b36709f --- /dev/null +++ b/WebCore/page/EditorClient.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 EditorClient_h +#define EditorClient_h + +#include "EditorInsertAction.h" +#include "PlatformString.h" +#include "TextAffinity.h" +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +#if PLATFORM(MAC) +class NSArray; +class NSData; +class NSString; +class NSURL; +#endif + +namespace WebCore { + +class CSSStyleDeclaration; +class EditCommand; +class Element; +class Frame; +class HTMLElement; +class KeyboardEvent; +class Node; +class Range; +class Selection; +class String; +class VisiblePosition; + +struct GrammarDetail { + int location; + int length; + Vector<String> guesses; + String userDescription; +}; + +class EditorClient { +public: + virtual ~EditorClient() { } + virtual void pageDestroyed() = 0; + + virtual bool shouldDeleteRange(Range*) = 0; + virtual bool shouldShowDeleteInterface(HTMLElement*) = 0; + virtual bool smartInsertDeleteEnabled() = 0; + virtual bool isContinuousSpellCheckingEnabled() = 0; + virtual void toggleContinuousSpellChecking() = 0; + virtual bool isGrammarCheckingEnabled() = 0; + virtual void toggleGrammarChecking() = 0; + virtual int spellCheckerDocumentTag() = 0; + + virtual bool isEditable() = 0; + + virtual bool shouldBeginEditing(Range*) = 0; + virtual bool shouldEndEditing(Range*) = 0; + virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction) = 0; + virtual bool shouldInsertText(const String&, Range*, EditorInsertAction) = 0; + virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting) = 0; + + virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*) = 0; +// virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle) = 0; +// virtual bool doCommandBySelector(SEL selector) = 0; + virtual bool shouldMoveRangeAfterDelete(Range*, Range*) = 0; + + virtual void didBeginEditing() = 0; + virtual void respondToChangedContents() = 0; + virtual void respondToChangedSelection() = 0; + virtual void didEndEditing() = 0; + virtual void didWriteSelectionToPasteboard() = 0; + virtual void didSetSelectionTypesForPasteboard() = 0; +// virtual void didChangeTypingStyle:(NSNotification *)notification = 0; +// virtual void didChangeSelection:(NSNotification *)notification = 0; +// virtual NSUndoManager* undoManager:(WebView *)webView = 0; + + virtual void registerCommandForUndo(PassRefPtr<EditCommand>) = 0; + virtual void registerCommandForRedo(PassRefPtr<EditCommand>) = 0; + virtual void clearUndoRedoOperations() = 0; + + virtual bool canUndo() const = 0; + virtual bool canRedo() const = 0; + + virtual void undo() = 0; + virtual void redo() = 0; + + virtual void handleKeyboardEvent(KeyboardEvent*) = 0; + virtual void handleInputMethodKeydown(KeyboardEvent*) = 0; + + virtual void textFieldDidBeginEditing(Element*) = 0; + virtual void textFieldDidEndEditing(Element*) = 0; + virtual void textDidChangeInTextField(Element*) = 0; + virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) = 0; + virtual void textWillBeDeletedInTextField(Element*) = 0; + virtual void textDidChangeInTextArea(Element*) = 0; + +#if PLATFORM(MAC) + virtual NSString* userVisibleString(NSURL*) = 0; +#ifdef BUILDING_ON_TIGER + virtual NSArray* pasteboardTypesForSelection(Frame*) = 0; +#endif +#endif + + virtual void ignoreWordInSpellDocument(const String&) = 0; + virtual void learnWord(const String&) = 0; + virtual void checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength) = 0; + virtual void checkGrammarOfString(const UChar*, int length, Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength) = 0; + virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail& detail) = 0; + virtual void updateSpellingUIWithMisspelledWord(const String&) = 0; + virtual void showSpellingUI(bool show) = 0; + virtual bool spellingUIIsShowing() = 0; + virtual void getGuessesForWord(const String&, Vector<String>& guesses) = 0; + virtual void setInputMethodState(bool enabled) = 0; +}; + +} + +#endif // EditorClient_h diff --git a/WebCore/page/EventHandler.cpp b/WebCore/page/EventHandler.cpp index 4ecbe13..3f45b92 100644 --- a/WebCore/page/EventHandler.cpp +++ b/WebCore/page/EventHandler.cpp @@ -27,6 +27,7 @@ #include "config.h" #include "EventHandler.h" +#include "AXObjectCache.h" #include "CachedImage.h" #include "ChromeClient.h" #include "Cursor.h" @@ -35,6 +36,7 @@ #include "Editor.h" #include "EventNames.h" #include "FloatPoint.h" +#include "FloatRect.h" #include "FocusController.h" #include "Frame.h" #include "FrameLoader.h" @@ -47,28 +49,35 @@ #include "HTMLInputElement.h" #include "HTMLNames.h" #include "Image.h" +#include "InspectorController.h" #include "KeyboardEvent.h" #include "MouseEvent.h" #include "MouseEventWithHitTestResults.h" #include "Page.h" #include "PlatformKeyboardEvent.h" -#include "PlatformScrollBar.h" #include "PlatformWheelEvent.h" +#include "RenderFrameSet.h" #include "RenderWidget.h" +#include "RenderView.h" +#include "Scrollbar.h" #include "SelectionController.h" #include "Settings.h" #include "TextEvent.h" #if ENABLE(SVG) -#include "SVGCursorElement.h" #include "SVGDocument.h" -#include "SVGLength.h" +#include "SVGElementInstance.h" #include "SVGNames.h" +#include "SVGUseElement.h" +#endif + +#if ENABLE(TOUCH_EVENTS) // Android +#include "TouchEvent.h" +#include "PlatformTouchEvent.h" #endif namespace WebCore { -using namespace EventNames; using namespace HTMLNames; // The link drag hysteresis is much larger than the others because there @@ -78,7 +87,6 @@ const int LinkDragHysteresis = 40; const int ImageDragHysteresis = 5; const int TextDragHysteresis = 3; const int GeneralDragHysteresis = 3; -const double TextDragDelay = 0.15; // Match key code of composition keydown event on windows. // IE sends VK_PROCESSKEY which has value 229; @@ -88,9 +96,27 @@ const int CompositionEventKeyCode = 229; using namespace SVGNames; #endif -const double autoscrollInterval = 0.1; +// When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth +const double autoscrollInterval = 0.05; -static Frame* subframeForTargetNode(Node* node); +static Frame* subframeForTargetNode(Node*); +static Frame* subframeForHitTestResult(const MouseEventWithHitTestResults&); + +static inline void scrollAndAcceptEvent(float delta, ScrollDirection positiveDirection, ScrollDirection negativeDirection, PlatformWheelEvent& e, Node* node) +{ + if (!delta) + return; + if (e.granularity() == ScrollByPageWheelEvent) { + if (node->renderer()->scroll(delta < 0 ? negativeDirection : positiveDirection, ScrollByPage, 1)) + e.accept(); + return; + } + float pixelsToScroll = delta > 0 ? delta : -delta; + if (e.granularity() == ScrollByLineWheelEvent) + pixelsToScroll *= cMouseWheelPixelsPerLineStep; + if (node->renderer()->scroll(delta < 0 ? negativeDirection : positiveDirection, ScrollByPixel, pixelsToScroll)) + e.accept(); +} EventHandler::EventHandler(Frame* frame) : m_frame(frame) @@ -99,9 +125,11 @@ EventHandler::EventHandler(Frame* frame) , m_mouseDownMayStartDrag(false) , m_mouseDownWasSingleClickInSelection(false) , m_beganSelectingText(false) + , m_panScrollInProgress(false) , m_hoverTimer(this, &EventHandler::hoverTimerFired) , m_autoscrollTimer(this, &EventHandler::autoscrollTimerFired) , m_autoscrollRenderer(0) + , m_autoscrollInProgress(false) , m_mouseDownMayStartAutoscroll(false) , m_mouseDownWasInSubframe(false) #if ENABLE(SVG) @@ -111,6 +139,8 @@ EventHandler::EventHandler(Frame* frame) , m_capturingMouseEventsNode(0) , m_clickCount(0) , m_mouseDownTimestamp(0) + , m_pendingFrameUnloadEventCount(0) + , m_pendingFrameBeforeUnloadEventCount(0) #if PLATFORM(MAC) , m_mouseDownView(nil) , m_sendingEventToSubview(false) @@ -135,10 +165,17 @@ void EventHandler::clear() m_resizeLayer = 0; m_nodeUnderMouse = 0; m_lastNodeUnderMouse = 0; +#if ENABLE(SVG) + m_instanceUnderMouse = 0; + m_lastInstanceUnderMouse = 0; +#endif m_lastMouseMoveEventSubframe = 0; m_lastScrollbarUnderMouse = 0; m_clickCount = 0; m_clickNode = 0; +#if ENABLE(TOUCH_EVENTS) // Android + m_touch = 0; +#endif m_frameSetBeingResized = 0; m_dragTarget = 0; m_currentMousePosition = IntPoint(); @@ -165,7 +202,7 @@ void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestRe } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); } } @@ -189,7 +226,7 @@ void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHit } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); } } @@ -198,7 +235,7 @@ bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestR if (event.event().button() != LeftButton) return false; - if (m_frame->selectionController()->isRange()) + if (m_frame->selection()->isRange()) // A double-click when range is already selected // should not change the selection. So, do not call // selectClosestWordFromMouseEvent, but do set @@ -232,7 +269,7 @@ bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestR } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); return true; } @@ -252,7 +289,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR // Don't restart the selection when the mouse is pressed on an // existing selection so we can allow for text dragging. IntPoint vPoint = m_frame->view()->windowToContents(event.event().pos()); - if (!extendSelection && m_frame->selectionController()->contains(vPoint)) { + if (!extendSelection && m_frame->selection()->contains(vPoint)) { m_mouseDownWasSingleClickInSelection = true; return false; } @@ -262,9 +299,9 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR visiblePos = VisiblePosition(innerNode, 0, DOWNSTREAM); Position pos = visiblePos.deepEquivalent(); - Selection newSelection = m_frame->selectionController()->selection(); + Selection newSelection = m_frame->selection()->selection(); if (extendSelection && newSelection.isCaretOrRange()) { - m_frame->selectionController()->setLastChangeWasHorizontalExtension(false); + m_frame->selection()->setLastChangeWasHorizontalExtension(false); // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection // was created right-to-left @@ -285,7 +322,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); return true; } @@ -306,7 +343,7 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve m_mouseDownWasSingleClickInSelection = false; - if (passWidgetMouseDownEventToWidget(event)) + if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event)) return true; #if ENABLE(SVG) @@ -332,7 +369,7 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve bool swallowEvent = false; if (event.event().button() == LeftButton || event.event().button() == MiddleButton) { - m_frame->selectionController()->setCaretBlinkingSuspended(true); + m_frame->selection()->setCaretBlinkingSuspended(true); m_mousePressed = true; m_beganSelectingText = false; @@ -345,7 +382,7 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve } m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect || - (m_mousePressNode && m_mousePressNode->renderer() && m_mousePressNode->renderer()->shouldAutoscroll()); + (m_mousePressNode && m_mousePressNode->renderer() && m_mousePressNode->renderer()->canBeProgramaticallyScrolled(true)); return swallowEvent; } @@ -368,14 +405,23 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e m_mouseDownMayStartDrag = false; - if (m_mouseDownMayStartAutoscroll) { + if (m_mouseDownMayStartAutoscroll && !m_panScrollInProgress) { // If the selection is contained in a layer that can scroll, that layer should handle the autoscroll // Otherwise, let the bridge handle it so the view can scroll itself. RenderObject* renderer = targetNode->renderer(); - while (renderer && !renderer->shouldAutoscroll()) - renderer = renderer->parent(); - if (renderer) + while (renderer && !renderer->canBeProgramaticallyScrolled(false)) { + if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document()->ownerElement()) + renderer = renderer->document()->ownerElement()->renderer(); + else + renderer = renderer->parent(); + } + + if (renderer) { + m_autoscrollInProgress = true; handleAutoscroll(renderer); + } + + m_mouseDownMayStartAutoscroll = false; } updateSelectionForMouseDrag(targetNode, event.localPoint()); @@ -388,7 +434,7 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag // in handleMousePressEvent - if (!m_frame->renderer() || !m_frame->renderer()->hasLayer() + if (!m_frame->contentRenderer() || !m_frame->contentRenderer()->hasLayer() || event.button() != LeftButton || event.clickCount() != 1) return false; @@ -400,7 +446,7 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const HitTestRequest request(true, false); HitTestResult result(m_frame->view()->windowToContents(event.pos())); - m_frame->renderer()->layer()->hitTest(request, result); + m_frame->contentRenderer()->layer()->hitTest(request, result); bool srcIsDHTML; return result.innerNode() && result.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, result.point().x(), result.point().y(), srcIsDHTML); } @@ -410,7 +456,7 @@ void EventHandler::updateSelectionForMouseDrag() FrameView* view = m_frame->view(); if (!view) return; - RenderObject* renderer = m_frame->renderer(); + RenderObject* renderer = m_frame->contentRenderer(); if (!renderer) return; RenderLayer* layer = renderer->layer(); @@ -445,7 +491,7 @@ void EventHandler::updateSelectionForMouseDrag(Node* targetNode, const IntPoint& // Restart the selection if this is the first mouse move. This work is usually // done in handleMousePressEvent, but not if the mouse press was on an existing selection. - Selection newSelection = m_frame->selectionController()->selection(); + Selection newSelection = m_frame->selection()->selection(); #if ENABLE(SVG) // Special case to limit selection to the containing block for SVG text. @@ -467,8 +513,8 @@ void EventHandler::updateSelectionForMouseDrag(Node* targetNode, const IntPoint& newSelection.expandUsingGranularity(m_frame->selectionGranularity()); if (m_frame->shouldChangeSelection(newSelection)) { - m_frame->selectionController()->setLastChangeWasHorizontalExtension(false); - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setLastChangeWasHorizontalExtension(false); + m_frame->selection()->setSelection(newSelection); } } @@ -487,14 +533,15 @@ bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event) bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event) { - stopAutoscrollTimer(); + if (m_autoscrollInProgress) + stopAutoscrollTimer(); if (handleMouseUp(event)) return true; // Used to prevent mouseMoveEvent from initiating a drag before // the mouse is pressed again. - m_frame->selectionController()->setCaretBlinkingSuspended(false); + m_frame->selection()->setCaretBlinkingSuspended(false); m_mousePressed = false; m_mouseDownMayStartDrag = false; m_mouseDownMayStartSelect = false; @@ -508,7 +555,7 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e // However, if we are editing, place the caret. if (m_mouseDownWasSingleClickInSelection && !m_beganSelectingText && m_dragStartPos == event.event().pos() - && m_frame->selectionController()->isRange()) { + && m_frame->selection()->isRange()) { Selection newSelection; Node *node = event.targetNode(); if (node && node->isContentEditable() && node->renderer()) { @@ -516,34 +563,96 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e newSelection = Selection(pos); } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); handled = true; } m_frame->notifyRendererOfSelectionChange(true); - m_frame->selectionController()->selectFrameElementInParentIfFullySelected(); + m_frame->selection()->selectFrameElementInParentIfFullySelected(); return handled; } void EventHandler::handleAutoscroll(RenderObject* renderer) { + // We don't want to trigger the autoscroll or the panScroll if it's already active if (m_autoscrollTimer.isActive()) - return; + return; + setAutoscrollRenderer(renderer); + +#if ENABLE(PAN_SCROLLING) + if (m_panScrollInProgress) { + m_panScrollStartPos = currentMousePosition(); + m_frame->view()->addPanScrollIcon(m_panScrollStartPos); + // If we're not in the top frame we notify it that we are using the panScroll + if (m_frame != m_frame->page()->mainFrame()) + m_frame->page()->mainFrame()->eventHandler()->setPanScrollInProgress(true); + } +#endif + startAutoscrollTimer(); } void EventHandler::autoscrollTimerFired(Timer<EventHandler>*) { - if (!m_mousePressed) { + RenderObject* r = autoscrollRenderer(); + if (!r) { stopAutoscrollTimer(); return; } - if (RenderObject* r = autoscrollRenderer()) + + if (m_autoscrollInProgress) { + if (!m_mousePressed) { + stopAutoscrollTimer(); + return; + } r->autoscroll(); + } else { + // we verify that the main frame hasn't received the order to stop the panScroll + if (!m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress()) { + stopAutoscrollTimer(); + return; + } +#if ENABLE(PAN_SCROLLING) + setPanScrollCursor(); + r->panScroll(m_panScrollStartPos); +#endif + } +} + +void EventHandler::setPanScrollCursor() +{ + // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll + // So we don't want to change the cursor over this area + const int noScrollRadius = 9; + bool east = m_panScrollStartPos.x() < (m_currentMousePosition.x() - noScrollRadius); + bool west = m_panScrollStartPos.x() > (m_currentMousePosition.x() + noScrollRadius); + bool north = m_panScrollStartPos.y() > (m_currentMousePosition.y() + noScrollRadius); + bool south = m_panScrollStartPos.y() < (m_currentMousePosition.y() - noScrollRadius); + + if (north) { + if (east) + m_frame->view()->setCursor(northEastPanningCursor()); + else if (west) + m_frame->view()->setCursor(northWestPanningCursor()); + else + m_frame->view()->setCursor(northPanningCursor()); + } else if (south) { + if (east) + m_frame->view()->setCursor(southEastPanningCursor()); + else if (west) + m_frame->view()->setCursor(southWestPanningCursor()); + else + m_frame->view()->setCursor(southPanningCursor()); + } else if (east) + m_frame->view()->setCursor(eastPanningCursor()); + else if (west) + m_frame->view()->setCursor(westPanningCursor()); + else + m_frame->view()->setCursor(middlePanningCursor()); } RenderObject* EventHandler::autoscrollRenderer() const @@ -551,6 +660,20 @@ RenderObject* EventHandler::autoscrollRenderer() const return m_autoscrollRenderer; } +void EventHandler::updateAutoscrollRenderer() +{ + if (!m_autoscrollRenderer) + return; + + HitTestResult hitTest = hitTestResultAtPoint(m_panScrollStartPos, true); + + if (Node* nodeAtPoint = hitTest.innerNode()) + m_autoscrollRenderer = nodeAtPoint->renderer(); + + while (m_autoscrollRenderer && !m_autoscrollRenderer->canBeProgramaticallyScrolled(false)) + m_autoscrollRenderer = m_autoscrollRenderer->parent(); +} + void EventHandler::setAutoscrollRenderer(RenderObject* renderer) { m_autoscrollRenderer = renderer; @@ -561,6 +684,7 @@ void EventHandler::allowDHTMLDrag(bool& flagDHTML, bool& flagUA) const if (!m_frame || !m_frame->document()) { flagDHTML = false; flagUA = false; + return; } unsigned mask = m_frame->page()->dragController()->delegateDragSourceAction(m_frame->view()->contentsToWindow(m_mouseDownPos)); @@ -571,27 +695,37 @@ void EventHandler::allowDHTMLDrag(bool& flagDHTML, bool& flagUA) const HitTestResult EventHandler::hitTestResultAtPoint(const IntPoint& point, bool allowShadowContent) { HitTestResult result(point); - if (!m_frame->renderer()) + if (!m_frame->contentRenderer()) return result; - m_frame->renderer()->layer()->hitTest(HitTestRequest(true, true), result); + m_frame->contentRenderer()->layer()->hitTest(HitTestRequest(true, true), result); while (true) { Node* n = result.innerNode(); - if (!n || !n->renderer() || !n->renderer()->isWidget()) + if (!result.isOverWidget() || !n || !n->renderer() || !n->renderer()->isWidget()) break; Widget* widget = static_cast<RenderWidget*>(n->renderer())->widget(); if (!widget || !widget->isFrameView()) break; Frame* frame = static_cast<HTMLFrameElementBase*>(n)->contentFrame(); - if (!frame || !frame->renderer()) + if (!frame || !frame->contentRenderer()) break; FrameView* view = static_cast<FrameView*>(widget); - IntPoint widgetPoint(result.localPoint().x() + view->contentsX() - n->renderer()->borderLeft() - n->renderer()->paddingLeft(), - result.localPoint().y() + view->contentsY() - n->renderer()->borderTop() - n->renderer()->paddingTop()); + IntPoint widgetPoint(result.localPoint().x() + view->scrollX() - n->renderer()->borderLeft() - n->renderer()->paddingLeft(), + result.localPoint().y() + view->scrollY() - n->renderer()->borderTop() - n->renderer()->paddingTop()); HitTestResult widgetHitTestResult(widgetPoint); - frame->renderer()->layer()->hitTest(HitTestRequest(true, true), widgetHitTestResult); + frame->contentRenderer()->layer()->hitTest(HitTestRequest(true, true), widgetHitTestResult); result = widgetHitTestResult; } + + // If our HitTestResult is not visible, then we started hit testing too far down the frame chain. + // Another hit test at the main frame level should get us the correct visible result. + Frame* resultFrame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : 0; + Frame* mainFrame = m_frame->page()->mainFrame(); + if (m_frame != mainFrame && resultFrame && resultFrame != mainFrame && !resultFrame->editor()->insideVisibleArea(result.point())) { + IntPoint windowPoint = resultFrame->view()->contentsToWindow(result.point()); + IntPoint mainFramePoint = mainFrame->view()->windowToContents(windowPoint); + result = mainFrame->eventHandler()->hitTestResultAtPoint(mainFramePoint, allowShadowContent); + } if (!allowShadowContent) result.setToNonShadowAncestor(); @@ -607,16 +741,34 @@ void EventHandler::startAutoscrollTimer() void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed) { - if (m_mouseDownWasInSubframe) { - if (Frame* subframe = subframeForTargetNode(m_mousePressNode.get())) - subframe->eventHandler()->stopAutoscrollTimer(rendererIsBeingDestroyed); - return; + if (m_autoscrollInProgress) { + if (m_mouseDownWasInSubframe) { + if (Frame* subframe = subframeForTargetNode(m_mousePressNode.get())) + subframe->eventHandler()->stopAutoscrollTimer(rendererIsBeingDestroyed); + return; + } + } + + if (autoscrollRenderer()) { + if (!rendererIsBeingDestroyed && (m_autoscrollInProgress || m_panScrollInProgress)) + autoscrollRenderer()->stopAutoscroll(); +#if ENABLE(PAN_SCROLLING) + if (m_panScrollInProgress) { + m_frame->view()->removePanScrollIcon(); + m_frame->view()->setCursor(pointerCursor()); + } +#endif + + setAutoscrollRenderer(0); } - if (!rendererIsBeingDestroyed && autoscrollRenderer()) - autoscrollRenderer()->stopAutoscroll(); - setAutoscrollRenderer(0); m_autoscrollTimer.stop(); + + m_panScrollInProgress = false; + // If we're not in the top frame we notify it that we are not using the panScroll anymore + if (m_frame->page() && m_frame != m_frame->page()->mainFrame()) + m_frame->page()->mainFrame()->eventHandler()->setPanScrollInProgress(false); + m_autoscrollInProgress = false; } Node* EventHandler::mousePressNode() const @@ -652,6 +804,13 @@ IntPoint EventHandler::currentMousePosition() const return m_currentMousePosition; } +Frame* subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult) +{ + if (!hitTestResult.isOverWidget()) + return 0; + return subframeForTargetNode(hitTestResult.targetNode()); +} + Frame* subframeForTargetNode(Node* node) { if (!node) @@ -677,37 +836,42 @@ static bool isSubmitImage(Node* node) // Returns true if the node's editable block is not current focused for editing static bool nodeIsNotBeingEdited(Node* node, Frame* frame) { - return frame->selectionController()->rootEditableElement() != node->rootEditableElement(); + return frame->selection()->rootEditableElement() != node->rootEditableElement(); } -Cursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, PlatformScrollbar* scrollbar) +Cursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, Scrollbar* scrollbar) { // During selection, use an I-beam no matter what we're over. // If you're capturing mouse events for a particular node, don't treat this as a selection. - if (m_mousePressed && m_mouseDownMayStartSelect && m_frame->selectionController()->isCaretOrRange() && !m_capturingMouseEventsNode) + if (m_mousePressed && m_mouseDownMayStartSelect && m_frame->selection()->isCaretOrRange() && !m_capturingMouseEventsNode) return iBeamCursor(); Node* node = event.targetNode(); RenderObject* renderer = node ? node->renderer() : 0; RenderStyle* style = renderer ? renderer->style() : 0; + if (renderer && renderer->isFrameSet()) { + RenderFrameSet* fs = static_cast<RenderFrameSet*>(renderer); + if (fs->canResizeRow(event.localPoint())) + return rowResizeCursor(); + if (fs->canResizeColumn(event.localPoint())) + return columnResizeCursor(); + } + if (style && style->cursors()) { const CursorList* cursors = style->cursors(); for (unsigned i = 0; i < cursors->size(); ++i) { - CachedImage* cimage = (*cursors)[i].cursorImage; + CachedImage* cimage = (*cursors)[i].cursorImage.get(); IntPoint hotSpot = (*cursors)[i].hotSpot; -#if ENABLE(SVG) - if (!cimage) { - Element* e = node->document()->getElementById((*cursors)[i].cursorFragmentId); - if (e && e->hasTagName(cursorTag)) { - hotSpot.setX(int(static_cast<SVGCursorElement*>(e)->x().value())); - hotSpot.setY(int(static_cast<SVGCursorElement*>(e)->y().value())); - cimage = static_cast<SVGCursorElement*>(e)->cachedImage(); - } - } -#endif if (!cimage) continue; + // Limit the size of cursors so that they cannot be used to cover UI elements in chrome. + IntSize size = cimage->image()->size(); + if (size.width() > 128 || size.height() > 128) + continue; + // Do not let the hotspot be outside the bounds of the image. + if (hotSpot.x() < 0 || hotSpot.y() < 0 || hotSpot.x() > size.width() || hotSpot.y() > size.height()) + continue; if (cimage->image()->isNull()) break; if (!cimage->errorOccurred()) @@ -820,6 +984,10 @@ Cursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, Pla return zoomInCursor(); case CURSOR_WEBKIT_ZOOM_OUT: return zoomOutCursor(); + case CURSOR_WEBKIT_GRAB: + return grabCursor(); + case CURSOR_WEBKIT_GRABBING: + return grabbingCursor(); } return pointerCursor(); } @@ -839,7 +1007,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) m_mouseDownMayStartAutoscroll = false; m_mouseDownPos = m_frame->view()->windowToContents(mouseEvent.pos()); m_mouseDownWasInSubframe = false; - + MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, true), mouseEvent); if (!mev.targetNode()) { @@ -849,7 +1017,14 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) m_mousePressNode = mev.targetNode(); - Frame* subframe = subframeForTargetNode(mev.targetNode()); + InspectorController* inspector = m_frame->page()->inspectorController(); + if (inspector && inspector->enabled() && inspector->searchingForNodeInPage()) { + inspector->handleMousePressOnNode(m_mousePressNode.get()); + invalidateClick(); + return true; + } + + Frame* subframe = subframeForHitTestResult(mev); if (subframe && passMousePressEventToSubframe(mev, subframe)) { // Start capturing future events for this frame. We only do this if we didn't clear // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop. @@ -859,6 +1034,32 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) return true; } +#if ENABLE(PAN_SCROLLING) + if (m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress() || m_autoscrollInProgress) { + stopAutoscrollTimer(); + invalidateClick(); + return true; + } + + if (mouseEvent.button() == MiddleButton && !mev.isOverLink()) { + RenderObject* renderer = mev.targetNode()->renderer(); + + while (renderer && !renderer->canBeProgramaticallyScrolled(false)) { + if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document()->ownerElement()) + renderer = renderer->document()->ownerElement()->renderer(); + else + renderer = renderer->parent(); + } + + if (renderer) { + m_panScrollInProgress = true; + handleAutoscroll(renderer); + invalidateClick(); + return true; + } + } +#endif + m_clickCount = mouseEvent.clickCount(); m_clickNode = mev.targetNode(); @@ -872,7 +1073,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) return true; } - bool swallowEvent = dispatchMouseEvent(mousedownEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + bool swallowEvent = dispatchMouseEvent(eventNames().mousedownEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults // in case the scrollbar widget was destroyed when the mouse event was handled. @@ -896,7 +1097,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) if (mev.targetNode()->isShadowNode() && mev.targetNode()->shadowParentNode()->hasTagName(inputTag)) mev = prepareMouseEvent(HitTestRequest(true, true), mouseEvent); - PlatformScrollbar* scrollbar = m_frame->view()->scrollbarUnderMouse(mouseEvent); + Scrollbar* scrollbar = m_frame->view()->scrollbarUnderMouse(mouseEvent); if (!scrollbar) scrollbar = mev.scrollbar(); if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar)) @@ -921,20 +1122,23 @@ bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& mouseEv m_currentMousePosition = mouseEvent.pos(); MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, true), mouseEvent); - Frame* subframe = subframeForTargetNode(mev.targetNode()); + Frame* subframe = subframeForHitTestResult(mev); if (subframe && passMousePressEventToSubframe(mev, subframe)) { m_capturingMouseEventsNode = 0; return true; } m_clickCount = mouseEvent.clickCount(); - bool swallowMouseUpEvent = dispatchMouseEvent(mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + bool swallowMouseUpEvent = dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); bool swallowClickEvent = false; // Don't ever dispatch click events for right clicks if (mouseEvent.button() != RightButton && mev.targetNode() == m_clickNode) - swallowClickEvent = dispatchMouseEvent(clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + swallowClickEvent = dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + if (m_lastScrollbarUnderMouse) + swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(); + bool swallowMouseReleaseEvent = false; if (!swallowMouseUpEvent) swallowMouseReleaseEvent = handleMouseReleaseEvent(mev); @@ -983,11 +1187,11 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi #endif if (m_frameSetBeingResized) - return dispatchMouseEvent(mousemoveEvent, m_frameSetBeingResized.get(), false, 0, mouseEvent, false); + return dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, mouseEvent, false); // Send events right to a scrollbar if the mouse is pressed. if (m_lastScrollbarUnderMouse && m_mousePressed) - return m_lastScrollbarUnderMouse->handleMouseMoveEvent(mouseEvent); + return m_lastScrollbarUnderMouse->mouseMoved(m_lastScrollbarUnderMouse->transformEvent(mouseEvent)); // Treat mouse move events while the mouse is pressed as "read-only" in prepareMouseEvent // if we are allowed to select. @@ -998,7 +1202,7 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (hoveredNode) *hoveredNode = mev.hitTestResult(); - PlatformScrollbar* scrollbar = 0; + Scrollbar* scrollbar = 0; if (m_resizeLayer && m_resizeLayer->inResizeMode()) m_resizeLayer->resize(mouseEvent, m_offsetFromResizeCorner); @@ -1012,15 +1216,14 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (m_lastScrollbarUnderMouse != scrollbar) { // Send mouse exited to the old scrollbar. if (m_lastScrollbarUnderMouse) - m_lastScrollbarUnderMouse->handleMouseOutEvent(mouseEvent); + m_lastScrollbarUnderMouse->mouseExited(); m_lastScrollbarUnderMouse = m_mousePressed ? 0 : scrollbar; } } bool swallowEvent = false; - Node* targetNode = m_capturingMouseEventsNode ? m_capturingMouseEventsNode.get() : mev.targetNode(); - RefPtr<Frame> newSubframe = subframeForTargetNode(targetNode); - + RefPtr<Frame> newSubframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev); + // We want mouseouts to happen first, from the inside out. First send a move event to the last subframe so that it will fire mouseouts. if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree()->isDescendantOf(m_frame) && m_lastMouseMoveEventSubframe != newSubframe) passMouseMoveEventToSubframe(mev, m_lastMouseMoveEventSubframe.get()); @@ -1028,11 +1231,15 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (newSubframe) { // Update over/out state before passing the event to the subframe. updateMouseEventTargetNode(mev.targetNode(), mouseEvent, true); - swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode); + + // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target + // node to be detached from its FrameView, in which case the event should not be passed. + if (newSubframe->view()) + swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode); } else { if (scrollbar && !m_mousePressed) - scrollbar->handleMouseMoveEvent(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. - if ((!m_resizeLayer || !m_resizeLayer->inResizeMode()) && m_frame->view()) + scrollbar->mouseMoved(scrollbar->transformEvent(mouseEvent)); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. + if ((!m_resizeLayer || !m_resizeLayer->inResizeMode()) && !m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress() && m_frame->view()) m_frame->view()->setCursor(selectCursor(mev, scrollbar)); } @@ -1041,7 +1248,7 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (swallowEvent) return true; - swallowEvent = dispatchMouseEvent(mousemoveEvent, mev.targetNode(), false, 0, mouseEvent, true); + swallowEvent = dispatchMouseEvent(eventNames().mousemoveEvent, mev.targetNode(), false, 0, mouseEvent, true); if (!swallowEvent) swallowEvent = handleMouseDraggedEvent(mev); @@ -1073,27 +1280,26 @@ bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) #endif if (m_frameSetBeingResized) - return dispatchMouseEvent(mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false); + return dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false); if (m_lastScrollbarUnderMouse) { invalidateClick(); - return m_lastScrollbarUnderMouse->handleMouseReleaseEvent(mouseEvent); + return m_lastScrollbarUnderMouse->mouseUp(); } MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, false, false, true), mouseEvent); - Node* targetNode = m_capturingMouseEventsNode.get() ? m_capturingMouseEventsNode.get() : mev.targetNode(); - Frame* subframe = subframeForTargetNode(targetNode); + Frame* subframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev); if (subframe && passMouseReleaseEventToSubframe(mev, subframe)) { m_capturingMouseEventsNode = 0; return true; } - bool swallowMouseUpEvent = dispatchMouseEvent(mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + bool swallowMouseUpEvent = dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); // Don't ever dispatch click events for right clicks bool swallowClickEvent = false; if (m_clickCount > 0 && mouseEvent.button() != RightButton && mev.targetNode() == m_clickNode) - swallowClickEvent = dispatchMouseEvent(clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + swallowClickEvent = dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); if (m_resizeLayer) { m_resizeLayer->setInResizeMode(false); @@ -1113,14 +1319,14 @@ bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTa { IntPoint contentsPos = m_frame->view()->windowToContents(event.pos()); - RefPtr<MouseEvent> me = new MouseEvent(eventType, + RefPtr<MouseEvent> me = MouseEvent::create(eventType, true, true, m_frame->document()->defaultView(), 0, event.globalX(), event.globalY(), contentsPos.x(), contentsPos.y(), event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), 0, 0, clipboard); ExceptionCode ec = 0; - EventTargetNodeCast(dragTarget)->dispatchEvent(me.get(), ec, true); + EventTargetNodeCast(dragTarget)->dispatchEvent(me.get(), ec); return me->defaultPrevented(); } @@ -1151,7 +1357,7 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (newTarget->hasTagName(frameTag) || newTarget->hasTagName(iframeTag)) accept = static_cast<HTMLFrameElementBase*>(newTarget)->contentFrame()->eventHandler()->updateDragAndDrop(event, clipboard); else - accept = dispatchDragEvent(dragenterEvent, newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragenterEvent, newTarget, event, clipboard); if (m_dragTarget) { Frame* frame = (m_dragTarget->hasTagName(frameTag) || m_dragTarget->hasTagName(iframeTag)) @@ -1159,14 +1365,14 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (frame) accept = frame->eventHandler()->updateDragAndDrop(event, clipboard); else - dispatchDragEvent(dragleaveEvent, m_dragTarget.get(), event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, m_dragTarget.get(), event, clipboard); } } else { if (newTarget) if (newTarget->hasTagName(frameTag) || newTarget->hasTagName(iframeTag)) accept = static_cast<HTMLFrameElementBase*>(newTarget)->contentFrame()->eventHandler()->updateDragAndDrop(event, clipboard); else - accept = dispatchDragEvent(dragoverEvent, newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragoverEvent, newTarget, event, clipboard); } m_dragTarget = newTarget; @@ -1181,7 +1387,7 @@ void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (frame) frame->eventHandler()->cancelDragAndDrop(event, clipboard); else - dispatchDragEvent(dragleaveEvent, m_dragTarget.get(), event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, m_dragTarget.get(), event, clipboard); } clearDragState(); } @@ -1195,7 +1401,7 @@ bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard if (frame) accept = frame->eventHandler()->performDragAndDrop(event, clipboard); else - accept = dispatchDragEvent(dropEvent, m_dragTarget.get(), event, clipboard); + accept = dispatchDragEvent(eventNames().dropEvent, m_dragTarget.get(), event, clipboard); } clearDragState(); return accept; @@ -1210,11 +1416,6 @@ void EventHandler::clearDragState() #endif } -Node* EventHandler::nodeUnderMouse() const -{ - return m_nodeUnderMouse.get(); -} - void EventHandler::setCapturingMouseEventsNode(PassRefPtr<Node> n) { m_capturingMouseEventsNode = n; @@ -1229,6 +1430,25 @@ MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestReques return m_frame->document()->prepareMouseEvent(request, documentPoint, mev); } +#if ENABLE(SVG) +static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* referenceNode) +{ + if (!referenceNode || !referenceNode->isSVGElement()) + return 0; + + Node* shadowTreeElement = referenceNode->shadowTreeRootNode(); + if (!shadowTreeElement) + return 0; + + Node* shadowTreeParentElement = shadowTreeElement->shadowParentNode(); + if (!shadowTreeParentElement) + return 0; + + ASSERT(shadowTreeParentElement->hasTagName(useTag)); + return static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode); +} +#endif + void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& mouseEvent, bool fireMouseOverOut) { Node* result = targetNode; @@ -1244,23 +1464,63 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo result = result->shadowAncestorNode(); } m_nodeUnderMouse = result; - +#if ENABLE(SVG) + m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(result); + + // <use> shadow tree elements may have been recloned, update node under mouse in any case + if (m_lastInstanceUnderMouse) { + SVGElement* lastCorrespondingElement = m_lastInstanceUnderMouse->correspondingElement(); + SVGElement* lastCorrespondingUseElement = m_lastInstanceUnderMouse->correspondingUseElement(); + + if (lastCorrespondingElement && lastCorrespondingUseElement) { + HashSet<SVGElementInstance*> instances = lastCorrespondingElement->instancesForElement(); + + // Locate the recloned shadow tree element for our corresponding instance + HashSet<SVGElementInstance*>::iterator end = instances.end(); + for (HashSet<SVGElementInstance*>::iterator it = instances.begin(); it != end; ++it) { + SVGElementInstance* instance = (*it); + ASSERT(instance->correspondingElement() == lastCorrespondingElement); + + if (instance == m_lastInstanceUnderMouse) + continue; + + if (instance->correspondingUseElement() != lastCorrespondingUseElement) + continue; + + SVGElement* shadowTreeElement = instance->shadowTreeElement(); + if (!shadowTreeElement->inDocument() || m_lastNodeUnderMouse == shadowTreeElement) + continue; + + m_lastNodeUnderMouse = shadowTreeElement; + m_lastInstanceUnderMouse = instance; + break; + } + } + } +#endif + // Fire mouseout/mouseover if the mouse has shifted to a different node. if (fireMouseOverOut) { if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame->document()) { m_lastNodeUnderMouse = 0; m_lastScrollbarUnderMouse = 0; +#if ENABLE(SVG) + m_lastInstanceUnderMouse = 0; +#endif } if (m_lastNodeUnderMouse != m_nodeUnderMouse) { // send mouseout event to the old node if (m_lastNodeUnderMouse) - EventTargetNodeCast(m_lastNodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, mouseoutEvent, 0, m_nodeUnderMouse.get()); + EventTargetNodeCast(m_lastNodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, eventNames().mouseoutEvent, 0, m_nodeUnderMouse.get()); // send mouseover event to the new node if (m_nodeUnderMouse) - EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, mouseoverEvent, 0, m_lastNodeUnderMouse.get()); + EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, eventNames().mouseoverEvent, 0, m_lastNodeUnderMouse.get()); } m_lastNodeUnderMouse = m_nodeUnderMouse; +#if ENABLE(SVG) + m_lastInstanceUnderMouse = instanceAssociatedWithShadowTreeElement(m_nodeUnderMouse.get()); +#endif } } @@ -1273,7 +1533,7 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe if (m_nodeUnderMouse) swallowEvent = EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, eventType, clickCount); - if (!swallowEvent && eventType == mousedownEvent) { + if (!swallowEvent && eventType == eventNames().mousedownEvent) { // Blur current focus node when a link/button is clicked; this // is expected by some sites that rely on onChange handlers running // from form fields before the button click is processed. @@ -1291,8 +1551,8 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe // will set a selection inside it, which will call setFocuseNodeIfNeeded. ExceptionCode ec = 0; Node* n = node->isShadowNode() ? node->shadowParentNode() : node; - if (m_frame->selectionController()->isRange() && - m_frame->selectionController()->toRange()->compareNode(n, ec) == Range::NODE_INSIDE && + if (m_frame->selection()->isRange() && + m_frame->selection()->toRange()->compareNode(n, ec) == Range::NODE_INSIDE && n->isDescendantOf(m_frame->document()->focusedNode())) return false; @@ -1336,7 +1596,7 @@ bool EventHandler::handleWheelEvent(PlatformWheelEvent& e) // Figure out which view to send the event to. RenderObject* target = node->renderer(); - if (target && target->isWidget()) { + if (result.isOverWidget() && target && target->isWidget()) { Widget* widget = static_cast<RenderWidget*>(target)->widget(); if (widget && passWheelEventToWidget(e, widget)) { @@ -1353,14 +1613,8 @@ bool EventHandler::handleWheelEvent(PlatformWheelEvent& e) if (node->renderer()) { // Just break up into two scrolls if we need to. Diagonal movement on // a MacBook pro is an example of a 2-dimensional mouse wheel event (where both deltaX and deltaY can be set). - float deltaX = e.isContinuous() ? e.continuousDeltaX() : e.deltaX(); - float deltaY = e.isContinuous() ? e.continuousDeltaY() : e.deltaY(); - if (deltaX && node->renderer()->scroll(deltaX < 0 ? ScrollRight : ScrollLeft, e.isContinuous() ? ScrollByPixel : ScrollByLine, - deltaX < 0 ? -deltaX : deltaX)) - e.accept(); - if (deltaY && node->renderer()->scroll(deltaY < 0 ? ScrollDown : ScrollUp, e.isContinuous() ? ScrollByPixel : ScrollByLine, - deltaY < 0 ? -deltaY : deltaY)) - e.accept(); + scrollAndAcceptEvent(e.deltaX(), ScrollLeft, ScrollRight, e, node); + scrollAndAcceptEvent(e.deltaY(), ScrollUp, ScrollDown, e, node); } } @@ -1381,16 +1635,19 @@ bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) IntPoint viewportPos = v->windowToContents(event.pos()); MouseEventWithHitTestResults mev = doc->prepareMouseEvent(HitTestRequest(false, true), viewportPos, event); - if (!m_frame->selectionController()->contains(viewportPos) && + // Context menu events shouldn't select text in GTK+ applications. +#if !PLATFORM(GTK) + if (!m_frame->selection()->contains(viewportPos) && // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse. // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items // available for text selections. But only if we're above text. - (m_frame->selectionController()->isContentEditable() || mev.targetNode() && mev.targetNode()->isTextNode())) { + (m_frame->selection()->isContentEditable() || mev.targetNode() && mev.targetNode()->isTextNode())) { m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection selectClosestWordOrLinkFromMouseEvent(mev); } +#endif - swallowEvent = dispatchMouseEvent(contextmenuEvent, mev.targetNode(), true, 0, event, true); + swallowEvent = dispatchMouseEvent(eventNames().contextmenuEvent, mev.targetNode(), true, 0, event, true); return swallowEvent; } @@ -1413,7 +1670,7 @@ bool EventHandler::canMouseDownStartSelect(Node* node) for (RenderObject* curr = node->renderer(); curr; curr = curr->parent()) if (Node* node = curr->element()) - return EventTargetNodeCast(node)->dispatchHTMLEvent(selectstartEvent, true, true); + return EventTargetNodeCast(node)->dispatchEventForType(eventNames().selectstartEvent, true, true); return true; } @@ -1425,7 +1682,7 @@ bool EventHandler::canMouseDragExtendSelect(Node* node) for (RenderObject* curr = node->renderer(); curr; curr = curr->parent()) if (Node* node = curr->element()) - return EventTargetNodeCast(node)->dispatchHTMLEvent(selectstartEvent, true, true); + return EventTargetNodeCast(node)->dispatchEventForType(eventNames().selectstartEvent, true, true); return true; } @@ -1448,7 +1705,7 @@ void EventHandler::hoverTimerFired(Timer<EventHandler>*) ASSERT(m_frame); ASSERT(m_frame->document()); - if (RenderObject* renderer = m_frame->renderer()) { + if (RenderObject* renderer = m_frame->contentRenderer()) { HitTestResult result(m_frame->view()->windowToContents(m_currentMousePosition)); renderer->layer()->hitTest(HitTestRequest(false, false, true), result); m_frame->document()->updateRendering(); @@ -1473,21 +1730,14 @@ static EventTargetNode* eventTargetNodeForDocument(Document* doc) bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& evt) { -#if PLATFORM(MAC) || PLATFORM(QT) - if (evt.ctrlKey()) -#else - if (evt.altKey()) -#endif - { - String key = evt.unmodifiedText(); - Element* elem = m_frame->document()->getElementByAccessKey(key.lower()); - if (elem) { - elem->accessKeyAction(false); - return true; - } - } - - return false; + if ((evt.modifiers() & s_accessKeyModifiers) != s_accessKeyModifiers) + return false; + String key = evt.unmodifiedText(); + Element* elem = m_frame->document()->getElementByAccessKey(key.lower()); + if (!elem) + return false; + elem->accessKeyAction(false); + return true; } #if !PLATFORM(MAC) @@ -1499,6 +1749,18 @@ bool EventHandler::needsKeyboardEventDisambiguationQuirks() const bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) { +#if ENABLE(PAN_SCROLLING) + if (m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress() || m_autoscrollInProgress) { + String escKeyId = "U+001B"; + // If a key is pressed while the autoscroll/panScroll is in progress then we want to stop + if (initialKeyEvent.keyIdentifier() == escKeyId && initialKeyEvent.type() == PlatformKeyboardEvent::KeyUp) + stopAutoscrollTimer(); + + // If we were in autoscroll/panscroll mode, we swallow the key event + return true; + } +#endif + // Check for cases where we are too early for events -- possible unmatched key up // from pressing return in the location bar. RefPtr<EventTargetNode> node = eventTargetNodeForDocument(m_frame->document()); @@ -1527,13 +1789,13 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) PlatformKeyboardEvent keyDownEvent = initialKeyEvent; if (keyDownEvent.type() != PlatformKeyboardEvent::RawKeyDown) keyDownEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown, backwardCompatibilityMode); - RefPtr<KeyboardEvent> keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView()); + RefPtr<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame->document()->defaultView()); if (matchedAnAccessKey) keydown->setDefaultPrevented(true); keydown->setTarget(node); if (initialKeyEvent.type() == PlatformKeyboardEvent::RawKeyDown) { - node->dispatchEvent(keydown, ec, true); + node->dispatchEvent(keydown, ec); return keydown->defaultHandled() || keydown->defaultPrevented(); } @@ -1548,12 +1810,12 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) if (handledByInputMethod) { keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode); - keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView()); + keydown = KeyboardEvent::create(keyDownEvent, m_frame->document()->defaultView()); keydown->setTarget(node); keydown->setDefaultHandled(); } - node->dispatchEvent(keydown, ec, true); + node->dispatchEvent(keydown, ec); bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented(); if (handledByInputMethod || (keydownResult && !backwardCompatibilityMode)) return keydownResult; @@ -1570,28 +1832,59 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keyPressEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::Char, backwardCompatibilityMode); if (keyPressEvent.text().isEmpty()) return keydownResult; - RefPtr<KeyboardEvent> keypress = new KeyboardEvent(keyPressEvent, m_frame->document()->defaultView()); + RefPtr<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame->document()->defaultView()); keypress->setTarget(node); if (keydownResult) keypress->setDefaultPrevented(true); #if PLATFORM(MAC) keypress->keypressCommands() = keydown->keypressCommands(); #endif - node->dispatchEvent(keypress, ec, true); + node->dispatchEvent(keypress, ec); return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled(); } +void EventHandler::handleKeyboardSelectionMovement(KeyboardEvent* event) +{ + if (!event) + return; + + String key = event->keyIdentifier(); + bool isShifted = event->getModifierState("Shift"); + bool isOptioned = event->getModifierState("Alt"); + + if (key == "Up") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::BACKWARD, LineGranularity, true); + event->setDefaultHandled(); + } + else if (key == "Down") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::FORWARD, LineGranularity, true); + event->setDefaultHandled(); + } + else if (key == "Left") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::LEFT, (isOptioned) ? WordGranularity : CharacterGranularity, true); + event->setDefaultHandled(); + } + else if (key == "Right") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::RIGHT, (isOptioned) ? WordGranularity : CharacterGranularity, true); + event->setDefaultHandled(); + } +} + void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event) { - if (event->type() == keydownEvent) { + if (event->type() == eventNames().keydownEvent) { m_frame->editor()->handleKeyboardEvent(event); if (event->defaultHandled()) return; if (event->keyIdentifier() == "U+0009") defaultTabEventHandler(event); + + // provides KB navigation and selection for enhanced accessibility users + if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) + handleKeyboardSelectionMovement(event); } - if (event->type() == keypressEvent) { + if (event->type() == eventNames().keypressEvent) { m_frame->editor()->handleKeyboardEvent(event); if (event->defaultHandled()) return; @@ -1638,7 +1931,7 @@ void EventHandler::dragSourceMovedTo(const PlatformMouseEvent& event) { if (dragState().m_dragSrc && dragState().m_dragSrcMayBeDHTML) // for now we don't care if event handler cancels default behavior, since there is none - dispatchDragSrcEvent(dragEvent, event); + dispatchDragSrcEvent(eventNames().dragEvent, event); } void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation) @@ -1646,7 +1939,7 @@ void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperat if (dragState().m_dragSrc && dragState().m_dragSrcMayBeDHTML) { dragState().m_dragClipboard->setDestinationOperation(operation); // for now we don't care if event handler cancels default behavior, since there is none - dispatchDragSrcEvent(dragendEvent, event); + dispatchDragSrcEvent(eventNames().dragendEvent, event); } freeClipboard(); dragState().m_dragSrc = 0; @@ -1684,7 +1977,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) // try to find an element that wants to be dragged HitTestRequest request(true, false); HitTestResult result(m_mouseDownPos); - m_frame->renderer()->layer()->hitTest(request, result); + m_frame->contentRenderer()->layer()->hitTest(request, result); Node* node = result.innerNode(); if (node && node->renderer()) dragState().m_dragSrc = node->renderer()->draggableNode(dragState().m_dragSrcMayBeDHTML, dragState().m_dragSrcMayBeUA, @@ -1702,7 +1995,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) node = result.innerNonSharedNode(); dragState().m_dragSrcIsImage = node && node->renderer() && node->renderer()->isImage(); - dragState().m_dragSrcInSelection = m_frame->selectionController()->contains(m_mouseDownPos); + dragState().m_dragSrcInSelection = m_frame->selection()->contains(m_mouseDownPos); } } @@ -1739,13 +2032,20 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) // image and offset if (dragState().m_dragSrcIsDHTML) { int srcX, srcY; - dragState().m_dragSrc->renderer()->absolutePosition(srcX, srcY); - IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY); - dragState().m_dragClipboard->setDragImageElement(dragState().m_dragSrc.get(), IntPoint() + delta); + if (RenderObject* renderer = dragState().m_dragSrc->renderer()) { + renderer->absolutePosition(srcX, srcY); + IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY); + dragState().m_dragClipboard->setDragImageElement(dragState().m_dragSrc.get(), IntPoint() + delta); + } else { + // The renderer has disappeared, this can happen if the onStartDrag handler has hidden + // the element in some way. In this case we just kill the drag. + m_mouseDownMayStartDrag = false; + goto cleanupDrag; + } } - m_mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown) - && !m_frame->selectionController()->isInPasswordField(); + m_mouseDownMayStartDrag = dispatchDragSrcEvent(eventNames().dragstartEvent, m_mouseDown) + && !m_frame->selection()->isInPasswordField(); // Invalidate clipboard here against anymore pasteboard writing for security. The drag // image can still be changed as we drag, but not the pasteboard data. @@ -1767,11 +2067,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) bool startedDrag = dragController && dragController->startDrag(m_frame, dragState().m_dragClipboard.get(), srcOp, event.event(), m_mouseDownPos, dragState().m_dragSrcIsDHTML); if (!startedDrag && dragState().m_dragSrcMayBeDHTML) { // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event - dispatchDragSrcEvent(dragendEvent, event.event()); + dispatchDragSrcEvent(eventNames().dragendEvent, event.event()); m_mouseDownMayStartDrag = false; } } - + +cleanupDrag: if (!m_mouseDownMayStartDrag) { // something failed to start the drag, cleanup freeClipboard(); @@ -1791,7 +2092,7 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline), // and avoid dispatching text input events from keydown default handlers. if (underlyingEvent && underlyingEvent->isKeyboardEvent()) - ASSERT(static_cast<KeyboardEvent*>(underlyingEvent)->type() == keypressEvent); + ASSERT(static_cast<KeyboardEvent*>(underlyingEvent)->type() == eventNames().keypressEvent); #endif EventTarget* target; if (underlyingEvent) @@ -1800,12 +2101,12 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve target = eventTargetNodeForDocument(m_frame->document()); if (!target) return false; - RefPtr<TextEvent> event = new TextEvent(m_frame->domWindow(), text); + RefPtr<TextEvent> event = TextEvent::create(m_frame->domWindow(), text); event->setUnderlyingEvent(underlyingEvent); event->setIsLineBreak(isLineBreak); event->setIsBackTab(isBackTab); ExceptionCode ec; - return target->dispatchEvent(event.release(), ec, true); + return target->dispatchEvent(event.release(), ec); } @@ -1873,6 +2174,175 @@ void EventHandler::capsLockStateMayHaveChanged() if (Node* node = d->focusedNode()) if (RenderObject* r = node->renderer()) r->capsLockStateMayHaveChanged(); -} +} + +unsigned EventHandler::pendingFrameUnloadEventCount() +{ + return m_pendingFrameUnloadEventCount; +} + +void EventHandler::addPendingFrameUnloadEventCount() +{ + m_pendingFrameUnloadEventCount += 1; + m_frame->page()->changePendingUnloadEventCount(1); + return; +} + +void EventHandler::removePendingFrameUnloadEventCount() +{ + ASSERT( (-1 + (int)m_pendingFrameUnloadEventCount) >= 0 ); + m_pendingFrameUnloadEventCount -= 1; + m_frame->page()->changePendingUnloadEventCount(-1); + return; +} + +void EventHandler::clearPendingFrameUnloadEventCount() +{ + m_frame->page()->changePendingUnloadEventCount(-((int)m_pendingFrameUnloadEventCount)); + m_pendingFrameUnloadEventCount = 0; + return; +} + +unsigned EventHandler::pendingFrameBeforeUnloadEventCount() +{ + return m_pendingFrameBeforeUnloadEventCount; +} + +void EventHandler::addPendingFrameBeforeUnloadEventCount() +{ + m_pendingFrameBeforeUnloadEventCount += 1; + m_frame->page()->changePendingBeforeUnloadEventCount(1); + return; +} + +void EventHandler::removePendingFrameBeforeUnloadEventCount() +{ + ASSERT( (-1 + (int)m_pendingFrameBeforeUnloadEventCount) >= 0 ); + m_pendingFrameBeforeUnloadEventCount -= 1; + m_frame->page()->changePendingBeforeUnloadEventCount(-1); + return; +} + + void EventHandler::clearPendingFrameBeforeUnloadEventCount() +{ + m_frame->page()->changePendingBeforeUnloadEventCount(-((int)m_pendingFrameBeforeUnloadEventCount)); + m_pendingFrameBeforeUnloadEventCount = 0; + return; +} + +bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, Scrollbar* scrollbar) +{ + if (!scrollbar || !scrollbar->enabled()) + return false; + return scrollbar->mouseDown(mev.event()); +} + +#if ENABLE(TOUCH_EVENTS) // Android +bool EventHandler::handleTouchEvent(const PlatformTouchEvent& e) +{ + // only handle the touch event in the top frame handler + if (m_frame->tree()->parent(true)) + return m_frame->tree()->parent()->eventHandler()->handleTouchEvent(e); + + Document* doc = m_frame->document(); + if (!doc) + return false; + + RenderObject* docRenderer = doc->renderer(); + if (!docRenderer) + return false; + + if (doc->touchEventListeners().size() == 0) + return false; + + TouchEventType type = e.eventType(); + if (type == TouchEventStart) { + Frame* frame = m_frame; + IntPoint vPoint = frame->view()->windowToContents(e.pos()); + HitTestRequest request(true, false); + HitTestResult result(vPoint); + frame->contentRenderer()->layer()->hitTest(request, result); + Node* node = result.innerNode(); + if (node) { + RenderObject* target = node->renderer(); + while (target && target->isWidget()) { + Widget* widget = static_cast<RenderWidget*>(target)->widget(); + if (widget->isFrameView()) { + frame = static_cast<FrameView*>(widget)->frame(); + vPoint = frame->view()->windowToContents(e.pos()); + HitTestResult ret(vPoint); + frame->contentRenderer()->layer()->hitTest(request, ret); + node = ret.innerNode(); + if (!node) + break; + else + target = node->renderer(); + } else + // plugin view?? + break; + } + } + + if (!node || !node->isEventTargetNode()) { + // reset to the top document node + node = doc; + frame = m_frame; + vPoint = frame->view()->windowToContents(e.pos()); + } + + m_touch = Touch::create(frame, EventTargetNodeCast(node), 0, + e.x(), e.y(), vPoint.x(), vPoint.y()); + } else if (m_touch) { + if ((type == TouchEventMove) && (e.x() == m_touch->screenX()) && + (e.y() == m_touch->screenY())) { + // don't trigger the event if it hasn't really moved + return false; + } + + IntPoint vPoint = m_touch->frame()->view()->windowToContents(e.pos()); + m_touch->updateLocation(e.x(), e.y(), vPoint.x(), vPoint.y()); + } else { + return false; + } + + RefPtr<TouchList> touchList = TouchList::create(); + touchList->append(m_touch); + RefPtr<TouchEvent> te; + switch(type) { + case TouchEventStart: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchstartEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + case TouchEventEnd: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchendEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + case TouchEventMove: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchmoveEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + case TouchEventCancel: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchcancelEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + default: + return false; + } + ExceptionCode ec = 0; + static_cast<EventTargetNode*>(m_touch->target())->dispatchEvent(te.get(), ec); + if (type == TouchEventEnd || type == TouchEventCancel) { + m_touch = 0; + } + return te->defaultPrevented(); +} +#endif } diff --git a/WebCore/page/EventHandler.h b/WebCore/page/EventHandler.h index 95a976d..8035494 100644 --- a/WebCore/page/EventHandler.h +++ b/WebCore/page/EventHandler.h @@ -60,15 +60,20 @@ class KeyboardEvent; class MouseEventWithHitTestResults; class Node; class PlatformKeyboardEvent; -class PlatformScrollbar; class PlatformWheelEvent; class RenderLayer; class RenderObject; class RenderWidget; +class Scrollbar; class String; +class SVGElementInstance; class TextEvent; class VisiblePosition; class Widget; +#if ENABLE(TOUCH_EVENTS) // Android +class PlatformTouchEvent; +class Touch; +#endif struct HitTestRequest; @@ -76,7 +81,6 @@ extern const int LinkDragHysteresis; extern const int ImageDragHysteresis; extern const int TextDragHysteresis; extern const int GeneralDragHysteresis; -extern const double TextDragDelay; class EventHandler : Noncopyable { public: @@ -90,8 +94,12 @@ public: Node* mousePressNode() const; void setMousePressNode(PassRefPtr<Node>); + bool panScrollInProgress() { return m_panScrollInProgress; } + void setPanScrollInProgress(bool inProgress) { m_panScrollInProgress = inProgress; } + void stopAutoscrollTimer(bool rendererIsBeingDestroyed = false); RenderObject* autoscrollRenderer() const; + void updateAutoscrollRenderer(); HitTestResult hitTestResultAtPoint(const IntPoint&, bool allowShadowContent); @@ -130,12 +138,17 @@ public: bool handleMouseReleaseEvent(const PlatformMouseEvent&); bool handleWheelEvent(PlatformWheelEvent&); +#if ENABLE(TOUCH_EVENTS) // Android + bool handleTouchEvent(const PlatformTouchEvent&); +#endif + bool sendContextMenuEvent(const PlatformMouseEvent&); void setMouseDownMayStartAutoscroll() { m_mouseDownMayStartAutoscroll = true; } bool needsKeyboardEventDisambiguationQuirks() const; + static unsigned accessKeyModifiers() { return s_accessKeyModifiers; } bool handleAccessKey(const PlatformKeyboardEvent&); bool keyEvent(const PlatformKeyboardEvent&); void defaultKeyboardEventHandler(KeyboardEvent*); @@ -153,6 +166,15 @@ public: void capsLockStateMayHaveChanged(); + unsigned pendingFrameUnloadEventCount(); + void addPendingFrameUnloadEventCount(); + void removePendingFrameUnloadEventCount(); + void clearPendingFrameUnloadEventCount(); + unsigned pendingFrameBeforeUnloadEventCount(); + void addPendingFrameBeforeUnloadEventCount(); + void removePendingFrameBeforeUnloadEventCount(); + void clearPendingFrameBeforeUnloadEventCount(); + #if PLATFORM(MAC) PassRefPtr<KeyboardEvent> currentKeyboardEvent() const; @@ -182,8 +204,9 @@ private: RefPtr<Clipboard> m_dragClipboard; // used on only the source side of dragging }; static EventHandlerDragState& dragState(); + static const double TextDragDelay; - Clipboard* createDraggingClipboard() const; + PassRefPtr<Clipboard> createDraggingClipboard() const; bool eventActivatedView(const PlatformMouseEvent&) const; void selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& event); @@ -198,7 +221,10 @@ private: bool handleMouseDraggedEvent(const MouseEventWithHitTestResults&); bool handleMouseReleaseEvent(const MouseEventWithHitTestResults&); - Cursor selectCursor(const MouseEventWithHitTestResults&, PlatformScrollbar*); + void handleKeyboardSelectionMovement(KeyboardEvent*); + + Cursor selectCursor(const MouseEventWithHitTestResults&, Scrollbar*); + void setPanScrollCursor(); void hoverTimerFired(Timer<EventHandler>*); @@ -208,7 +234,6 @@ private: void handleAutoscroll(RenderObject*); void startAutoscrollTimer(); void setAutoscrollRenderer(RenderObject*); - void autoscrollTimerFired(Timer<EventHandler>*); void invalidateClick(); @@ -240,7 +265,7 @@ private: bool passSubframeEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe, HitTestResult* hoveredNode = 0); - bool passMousePressEventToScrollbar(MouseEventWithHitTestResults&, PlatformScrollbar*); + bool passMousePressEventToScrollbar(MouseEventWithHitTestResults&, Scrollbar*); bool passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults&); bool passWidgetMouseDownEventToWidget(RenderWidget*); @@ -279,14 +304,20 @@ private: IntPoint m_dragStartPos; + IntPoint m_panScrollStartPos; + bool m_panScrollInProgress; + Timer<EventHandler> m_hoverTimer; Timer<EventHandler> m_autoscrollTimer; RenderObject* m_autoscrollRenderer; + bool m_autoscrollInProgress; bool m_mouseDownMayStartAutoscroll; bool m_mouseDownWasInSubframe; #if ENABLE(SVG) bool m_svgPan; + RefPtr<SVGElementInstance> m_instanceUnderMouse; + RefPtr<SVGElementInstance> m_lastInstanceUnderMouse; #endif RenderLayer* m_resizeLayer; @@ -296,10 +327,13 @@ private: RefPtr<Node> m_nodeUnderMouse; RefPtr<Node> m_lastNodeUnderMouse; RefPtr<Frame> m_lastMouseMoveEventSubframe; - RefPtr<PlatformScrollbar> m_lastScrollbarUnderMouse; + RefPtr<Scrollbar> m_lastScrollbarUnderMouse; int m_clickCount; RefPtr<Node> m_clickNode; +#if ENABLE(TOUCH_EVENTS) // Android + RefPtr<Touch> m_touch; +#endif RefPtr<Node> m_dragTarget; @@ -312,6 +346,11 @@ private: double m_mouseDownTimestamp; PlatformMouseEvent m_mouseDown; + static unsigned s_accessKeyModifiers; + + unsigned m_pendingFrameUnloadEventCount; + unsigned m_pendingFrameBeforeUnloadEventCount; + #if PLATFORM(MAC) NSView *m_mouseDownView; bool m_sendingEventToSubview; diff --git a/WebCore/page/FocusController.cpp b/WebCore/page/FocusController.cpp index a3542aa..9b30362 100644 --- a/WebCore/page/FocusController.cpp +++ b/WebCore/page/FocusController.cpp @@ -34,7 +34,6 @@ #include "Element.h" #include "Event.h" #include "EventHandler.h" -#include "EventNames.h" #include "Frame.h" #include "FrameView.h" #include "FrameTree.h" @@ -51,7 +50,6 @@ namespace WebCore { -using namespace EventNames; using namespace HTMLNames; FocusController::FocusController(Page* page) @@ -65,13 +63,13 @@ void FocusController::setFocusedFrame(PassRefPtr<Frame> frame) if (m_focusedFrame == frame) return; - if (m_focusedFrame) - m_focusedFrame->selectionController()->setFocused(false); + if (m_focusedFrame && m_focusedFrame->view()) + m_focusedFrame->selection()->setFocused(false); m_focusedFrame = frame; - if (m_focusedFrame) - m_focusedFrame->selectionController()->setFocused(true); + if (m_focusedFrame && m_focusedFrame->view()) + m_focusedFrame->selection()->setFocused(true); } Frame* FocusController::focusedOrMainFrame() @@ -228,7 +226,7 @@ static void clearSelectionIfNeeded(Frame* oldFocusedFrame, Frame* newFocusedFram if (oldFocusedFrame->document() != newFocusedFrame->document()) return; - SelectionController* s = oldFocusedFrame->selectionController(); + SelectionController* s = oldFocusedFrame->selection(); if (s->isNone()) return; @@ -296,15 +294,14 @@ void FocusController::setActive(bool active) m_isActive = active; - // FIXME: It would be nice to make Mac use this implementation someday. - // Right now Mac calls updateControlTints from within WebKit, and moving - // the call to here is not simple. -#if !PLATFORM(MAC) - if (FrameView* view = m_page->mainFrame()->view()) - view->updateControlTints(); -#endif + if (FrameView* view = m_page->mainFrame()->view()) { + if (!view->platformWidget()) { + view->layoutIfNeededRecursive(); + view->updateControlTints(); + } + } - focusedOrMainFrame()->selectionController()->pageActivationChanged(); + focusedOrMainFrame()->selection()->pageActivationChanged(); } } // namespace WebCore diff --git a/WebCore/page/Frame.cpp b/WebCore/page/Frame.cpp index 6e8081e..fa1a514 100644 --- a/WebCore/page/Frame.cpp +++ b/WebCore/page/Frame.cpp @@ -7,7 +7,8 @@ * 2001 George Staikos <staikos@kde.org> * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com> - * Copyright (C) 2007 Trolltech ASA + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,6 +25,9 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ +#ifdef ANDROID_INSTRUMENT +#define LOG_TAG "WebCore" +#endif #include "config.h" #include "Frame.h" @@ -45,15 +49,18 @@ #include "FrameLoader.h" #include "FrameView.h" #include "GraphicsContext.h" -#include "HitTestResult.h" #include "HTMLDocument.h" #include "HTMLFormElement.h" #include "HTMLFrameElementBase.h" -#include "HTMLGenericFormElement.h" +#include "HTMLFormControlElement.h" #include "HTMLNames.h" #include "HTMLTableCellElement.h" +#include "HitTestResult.h" +#include "JSDOMWindowShell.h" #include "Logging.h" +#include "markup.h" #include "MediaFeatureNames.h" +#include "Navigator.h" #include "NodeList.h" #include "Page.h" #include "RegularExpression.h" @@ -67,29 +74,16 @@ #include "TextIterator.h" #include "TextResourceDecoder.h" #include "XMLNames.h" -#include "bindings/NP_jsobject.h" -#include "bindings/npruntime_impl.h" -#include "bindings/runtime_root.h" -#include "kjs_proxy.h" -#include "kjs_window.h" +#include "ScriptController.h" +#include "npruntime_impl.h" +#include "runtime_root.h" #include "visible_units.h" +#include <wtf/RefCountedLeakCounter.h> -#ifdef MANUAL_MERGE_REQUIRED -#ifdef ANDROID_BRIDGE - #define LOG_TAG "webcore" - #undef LOG - #include <utils/Log.h> - - #include "WebCoreViewBridge.h" - #include "FrameAndroid.h" -#endif - -#else // MANUAL_MERGE_REQUIRED #if FRAME_LOADS_USER_STYLESHEET #include "UserStyleSheetLoader.h" #endif -#endif // MANUAL_MERGE_REQUIRED #if ENABLE(SVG) #include "SVGDocument.h" #include "SVGDocumentExtensions.h" @@ -97,34 +91,23 @@ #include "XLinkNames.h" #endif -using namespace std; +#if PLATFORM(ANDROID) +#include "WebViewCore.h" +#endif + +#ifdef ANDROID_INSTRUMENT +#include "CString.h" +#include "Cache.h" +#endif -using KJS::JSLock; +using namespace std; namespace WebCore { -using namespace EventNames; using namespace HTMLNames; -double Frame::s_currentPaintTimeStamp = 0.0; - -#ifndef NDEBUG -WTFLogChannel LogWebCoreFrameLeaks = { 0x00000000, "", WTFLogChannelOn }; - -struct FrameCounter { - static int count; - ~FrameCounter() - { - if (count) -#ifdef ANDROID_BRIDGE - fprintf(stderr, "LEAK: %d Frame\n", count); -#else - LOG(WebCoreFrameLeaks, "LEAK: %d Frame\n", count); -#endif - } -}; -int FrameCounter::count = 0; -static FrameCounter frameCounter; +#ifndef NDEBUG +static WTF::RefCountedLeakCounter frameCounter("Frame"); #endif static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement) @@ -153,15 +136,14 @@ Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* if (!ownerElement) page->setMainFrame(this); else { - // FIXME: Frames were originally created with a refcount of 1. - // Leave this ref call here until we can straighten that out. - ref(); page->incrementFrameCount(); + // Make sure we will not end up with two frames referencing the same owner element. + ASSERT((!(ownerElement->m_contentFrame)) || (ownerElement->m_contentFrame->ownerElement() != ownerElement)); ownerElement->m_contentFrame = this; } #ifndef NDEBUG - ++FrameCounter::count; + frameCounter.increment(); #endif } @@ -169,11 +151,6 @@ Frame::~Frame() { setView(0); loader()->clearRecordedFormValues(); - -#if PLATFORM(MAC) - setBridge(0); -#endif - loader()->cancelAndClear(); // FIXME: We should not be doing all this work inside the destructor @@ -181,22 +158,26 @@ Frame::~Frame() ASSERT(!d->m_lifeSupportTimer.isActive()); #ifndef NDEBUG - --FrameCounter::count; + frameCounter.decrement(); #endif - if (d->m_jscript && d->m_jscript->haveGlobalObject()) - static_cast<KJS::Window*>(d->m_jscript->globalObject())->disconnectFrame(); + if (d->m_script.haveWindowShell()) + d->m_script.windowShell()->disconnectFrame(); disconnectOwnerElement(); if (d->m_domWindow) d->m_domWindow->disconnectFrame(); + + HashSet<DOMWindow*>::iterator end = d->m_liveFormerWindows.end(); + for (HashSet<DOMWindow*>::iterator it = d->m_liveFormerWindows.begin(); it != end; ++it) + (*it)->disconnectFrame(); if (d->m_view) { d->m_view->hide(); d->m_view->clearFrame(); } - + ASSERT(!d->m_lifeSupportTimer.isActive()); #if FRAME_LOADS_USER_STYLESHEET @@ -209,12 +190,12 @@ Frame::~Frame() void Frame::init() { - d->m_loader->init(); + d->m_loader.init(); } FrameLoader* Frame::loader() const { - return d->m_loader; + return &d->m_loader; } FrameView* Frame::view() const @@ -224,6 +205,14 @@ FrameView* Frame::view() const void Frame::setView(FrameView* view) { +#if PLATFORM(ANDROID) + if (!view && d->m_view) { + // FIXME(for Cary): This is moved from FrameAndroid destructor. Do we + // need to call removeFrameGeneration per Frame or per FrameView? + android::WebViewCore::getWebViewCore(d->m_view.get())->removeFrameGeneration(this); + } +#endif + // Detach the document now, so any onUnload handlers get run - if // we wait until the view is destroyed, then things won't be // hooked up enough for some JavaScript calls to work. @@ -243,18 +232,14 @@ void Frame::setView(FrameView* view) loader()->resetMultipleFormSubmissionProtection(); } -KJSProxy *Frame::scriptProxy() +ScriptController* Frame::script() { - if (!d->m_jscript) - d->m_jscript = new KJSProxy(this); - return d->m_jscript; + return &d->m_script; } -Document *Frame::document() const +Document* Frame::document() const { - if (d) - return d->m_doc.get(); - return 0; + return d->m_doc.get(); } void Frame::setDocument(PassRefPtr<Document> newDoc) @@ -265,15 +250,14 @@ void Frame::setDocument(PassRefPtr<Document> newDoc) } d->m_doc = newDoc; - if (d->m_doc && selectionController()->isFocusedAndActive()) + if (d->m_doc && selection()->isFocusedAndActive()) setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive()); if (d->m_doc && !d->m_doc->attached()) d->m_doc->attach(); - - // Remove the cached 'document' property, which is now stale. - if (d->m_jscript) - d->m_jscript->clearDocumentWrapper(); + + // Update the cached 'document' property, which is now stale. + d->m_script.updateDocument(); } Settings* Frame::settings() const @@ -283,7 +267,7 @@ Settings* Frame::settings() const String Frame::selectedText() const { - return plainText(selectionController()->toRange().get()); + return plainText(selection()->toRange().get()); } IntRect Frame::firstRectForRange(Range* range) const @@ -292,11 +276,16 @@ IntRect Frame::firstRectForRange(Range* range) const ExceptionCode ec = 0; ASSERT(range->startContainer(ec)); ASSERT(range->endContainer(ec)); - IntRect startCaretRect = range->startContainer(ec)->renderer()->caretRect(range->startOffset(ec), DOWNSTREAM, &extraWidthToEndOfLine); - ASSERT(!ec); - IntRect endCaretRect = range->endContainer(ec)->renderer()->caretRect(range->endOffset(ec), UPSTREAM); - ASSERT(!ec); - + InlineBox* startInlineBox; + int startCaretOffset; + range->startPosition().getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset); + IntRect startCaretRect = range->startContainer(ec)->renderer()->caretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine); + + InlineBox* endInlineBox; + int endCaretOffset; + range->endPosition().getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset); + IntRect endCaretRect = range->endContainer(ec)->renderer()->caretRect(endInlineBox, endCaretOffset); + if (startCaretRect.y() == endCaretRect.y()) { // start and end are on the same line return IntRect(min(startCaretRect.x(), endCaretRect.x()), @@ -312,7 +301,7 @@ IntRect Frame::firstRectForRange(Range* range) const startCaretRect.height()); } -SelectionController* Frame::selectionController() const +SelectionController* Frame::selection() const { return &d->m_selectionController; } @@ -338,28 +327,28 @@ SelectionController* Frame::dragCaretController() const } -AnimationController* Frame::animationController() const +AnimationController* Frame::animation() const { return &d->m_animationController; } -static RegularExpression *createRegExpForLabels(const Vector<String>& labels) +static RegularExpression* createRegExpForLabels(const Vector<String>& labels) { // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being // the same across calls. We can't do that. static RegularExpression wordRegExp = RegularExpression("\\w"); - DeprecatedString pattern("("); + String pattern("("); unsigned int numLabels = labels.size(); unsigned int i; for (i = 0; i < numLabels; i++) { - DeprecatedString label = labels[i].deprecatedString(); + String label = labels[i]; bool startsWithWordChar = false; bool endsWithWordChar = false; if (label.length() != 0) { - startsWithWordChar = wordRegExp.search(label.at(0)) >= 0; - endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0; + startsWithWordChar = wordRegExp.search(label.substring(0, 1)) >= 0; + endsWithWordChar = wordRegExp.search(label.substring(label.length() - 1, 1)) >= 0; } if (i != 0) @@ -395,10 +384,10 @@ String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellE for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) { if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { // For each text chunk, run the regexp - DeprecatedString nodeString = n->nodeValue().deprecatedString(); + String nodeString = n->nodeValue(); int pos = regExp->searchRev(nodeString); if (pos >= 0) - return nodeString.mid(pos, regExp->matchedLength()); + return nodeString.substring(pos, regExp->matchedLength()); } } } @@ -442,15 +431,14 @@ String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element searchedCellAbove = true; } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { // For each text chunk, run the regexp - DeprecatedString nodeString = n->nodeValue().deprecatedString(); + String nodeString = n->nodeValue(); // add 100 for slop, to make it more likely that we'll search whole nodes if (lengthSearched + nodeString.length() > maxCharsSearched) nodeString = nodeString.right(charsSearchedThreshold - lengthSearched); int pos = regExp->searchRev(nodeString); if (pos >= 0) - return nodeString.mid(pos, regExp->matchedLength()); - else - lengthSearched += nodeString.length(); + return nodeString.substring(pos, regExp->matchedLength()); + lengthSearched += nodeString.length(); } } @@ -464,9 +452,12 @@ String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element) { - DeprecatedString name = element->getAttribute(nameAttr).deprecatedString(); + String name = element->getAttribute(nameAttr); + if (name.isEmpty()) + return String(); + // Make numbers and _'s in field names behave like word boundaries, e.g., "address2" - name.replace(RegularExpression("\\d"), " "); + replace(name, RegularExpression("\\d"), " "); name.replace('_', ' '); OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels)); @@ -484,12 +475,12 @@ String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* e bestPos = pos; bestLength = length; } - start = pos+1; + start = pos + 1; } } while (pos != -1); if (bestPos != -1) - return name.mid(bestPos, bestLength); + return name.substring(bestPos, bestLength); return String(); } @@ -511,8 +502,8 @@ void Frame::setMark(const Selection& s) void Frame::notifyRendererOfSelectionChange(bool userTriggered) { RenderObject* renderer = 0; - if (selectionController()->rootEditableElement()) - renderer = selectionController()->rootEditableElement()->shadowAncestorNode()->renderer(); + if (selection()->rootEditableElement()) + renderer = selection()->rootEditableElement()->shadowAncestorNode()->renderer(); // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed if (renderer && (renderer->isTextArea() || renderer->isTextField())) @@ -521,7 +512,7 @@ void Frame::notifyRendererOfSelectionChange(bool userTriggered) void Frame::invalidateSelection() { - selectionController()->setNeedsLayout(); + selection()->setNeedsLayout(); selectionLayoutChanged(); } @@ -536,10 +527,10 @@ void Frame::setCaretVisible(bool flag) void Frame::clearCaretRectIfNeeded() { -#ifndef ANDROID_DRAW_OWN_CARET +#if ENABLE(TEXT_CARET) if (d->m_caretPaint) { d->m_caretPaint = false; - selectionController()->invalidateCaretRect(); + selection()->invalidateCaretRect(); } #endif } @@ -559,10 +550,10 @@ static bool isFrameElement(const Node *n) void Frame::setFocusedNodeIfNeeded() { - if (!document() || selectionController()->isNone() || !selectionController()->isFocusedAndActive()) + if (!document() || selection()->isNone() || !selection()->isFocusedAndActive()) return; - Node* target = selectionController()->rootEditableElement(); + Node* target = selection()->rootEditableElement(); if (target) { RenderObject* renderer = target->renderer(); @@ -586,11 +577,11 @@ void Frame::setFocusedNodeIfNeeded() void Frame::selectionLayoutChanged() { - bool caretRectChanged = selectionController()->recomputeCaretRect(); + bool caretRectChanged = selection()->recomputeCaretRect(); -#ifndef ANDROID_DRAW_OWN_CARET +#if ENABLE(TEXT_CARET) bool shouldBlink = d->m_caretVisible - && selectionController()->isCaret() && selectionController()->isContentEditable(); + && selection()->isCaret() && selection()->isContentEditable(); shouldBlink = false; // If the caret moved, stop the blink timer so we can restart with a @@ -604,19 +595,19 @@ void Frame::selectionLayoutChanged() d->m_caretBlinkTimer.startRepeating(theme()->caretBlinkFrequency()); if (!d->m_caretPaint) { d->m_caretPaint = true; - selectionController()->invalidateCaretRect(); + selection()->invalidateCaretRect(); } } #else - if (caretRectChanged == false) + if (!caretRectChanged) return; #endif - if (!renderer()) + RenderView* canvas = contentRenderer(); + if (!canvas) return; - RenderView* canvas = static_cast<RenderView*>(renderer()); - Selection selection = selectionController()->selection(); + Selection selection = this->selection()->selection(); if (!selection.isRange()) canvas->clearSelection(); @@ -625,10 +616,10 @@ void Frame::selectionLayoutChanged() // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3] // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected // and will fill the gap before 'bar'. - Position startPos = selection.visibleStart().deepEquivalent(); + Position startPos = selection.start(); if (startPos.downstream().isCandidate()) startPos = startPos.downstream(); - Position endPos = selection.visibleEnd().deepEquivalent(); + Position endPos = selection.end(); if (endPos.upstream().isCandidate()) endPos = endPos.upstream(); @@ -644,28 +635,28 @@ void Frame::selectionLayoutChanged() void Frame::caretBlinkTimerFired(Timer<Frame>*) { -#ifndef ANDROID_DRAW_OWN_CARET +#if ENABLE(TEXT_CARET) ASSERT(d->m_caretVisible); - ASSERT(selectionController()->isCaret()); + ASSERT(selection()->isCaret()); bool caretPaint = d->m_caretPaint; - if (selectionController()->isCaretBlinkingSuspended() && caretPaint) + if (selection()->isCaretBlinkingSuspended() && caretPaint) return; d->m_caretPaint = !caretPaint; - selectionController()->invalidateCaretRect(); + selection()->invalidateCaretRect(); #endif } void Frame::paintCaret(GraphicsContext* p, const IntRect& rect) const { -#ifndef ANDROID_DRAW_OWN_CARET +#if ENABLE(TEXT_CARET) if (d->m_caretPaint && d->m_caretVisible) - selectionController()->paintCaret(p, rect); + selection()->paintCaret(p, rect); #endif } void Frame::paintDragCaret(GraphicsContext* p, const IntRect& rect) const { -#ifndef ANDROID_DRAW_OWN_CARET +#if ENABLE(TEXT_CARET) SelectionController* dragCaretController = d->m_page->dragCaretController(); ASSERT(dragCaretController->selection().isCaret()); if (dragCaretController->selection().start().node()->document()->frame() == this) @@ -673,35 +664,68 @@ void Frame::paintDragCaret(GraphicsContext* p, const IntRect& rect) const #endif } -int Frame::zoomFactor() const +float Frame::zoomFactor() const +{ + return d->m_zoomFactor; +} + +bool Frame::isZoomFactorTextOnly() const { - return d->m_zoomFactor; + return d->m_page->settings()->zoomsTextOnly(); } -void Frame::setZoomFactor(int percent) +bool Frame::shouldApplyTextZoom() const +{ + if (d->m_zoomFactor == 1.0f || !isZoomFactorTextOnly()) + return false; +#if ENABLE(SVG) + if (d->m_doc && d->m_doc->isSVGDocument()) + return false; +#endif + return true; +} + +bool Frame::shouldApplyPageZoom() const +{ + if (d->m_zoomFactor == 1.0f || isZoomFactorTextOnly()) + return false; +#if ENABLE(SVG) + if (d->m_doc && d->m_doc->isSVGDocument()) + return false; +#endif + return true; +} + +void Frame::setZoomFactor(float percent, bool isTextOnly) { - if (d->m_zoomFactor == percent) - return; + if (d->m_zoomFactor == percent && isZoomFactorTextOnly()) + return; #if ENABLE(SVG) + // SVG doesn't care if the zoom factor is text only. It will always apply a + // zoom to the whole SVG. if (d->m_doc && d->m_doc->isSVGDocument()) { if (!static_cast<SVGDocument*>(d->m_doc.get())->zoomAndPanEnabled()) return; d->m_zoomFactor = percent; + d->m_page->settings()->setZoomsTextOnly(true); // We do this to avoid doing any scaling of CSS pixels, since the SVG has its own notion of zoom. if (d->m_doc->renderer()) d->m_doc->renderer()->repaint(); return; } #endif - d->m_zoomFactor = percent; - if (d->m_doc) - d->m_doc->recalcStyle(Node::Force); - for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->setZoomFactor(d->m_zoomFactor); + d->m_zoomFactor = percent; + d->m_page->settings()->setZoomsTextOnly(isTextOnly); + + if (d->m_doc) + d->m_doc->recalcStyle(Node::Force); + + for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) + child->setZoomFactor(d->m_zoomFactor, isTextOnly); - if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->needsLayout()) - view()->layout(); + if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->needsLayout() && view()->didFirstLayout()) + view()->layout(); } void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize) @@ -751,7 +775,8 @@ void Frame::setNeedsReapplyStyles() // Invalidate the FrameView so that FrameView::layout will get called, // which calls reapplyStyles. - view()->invalidate(); + if (view()) + view()->invalidate(); } bool Frame::needsReapplyStyles() const @@ -787,7 +812,7 @@ void Frame::reapplyStyles() bool Frame::shouldChangeSelection(const Selection& newSelection) const { - return shouldChangeSelection(selectionController()->selection(), newSelection, newSelection.affinity(), false); + return shouldChangeSelection(selection()->selection(), newSelection, newSelection.affinity(), false); } bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const @@ -820,7 +845,7 @@ void Frame::setUseSecureKeyboardEntry(bool) void Frame::updateSecureKeyboardEntryIfActive() { - if (selectionController()->isFocusedAndActive()) + if (selection()->isFocusedAndActive()) setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive()); } @@ -853,15 +878,14 @@ void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction edit mutableStyle = typingStyle(); } - Node *node = selectionController()->selection().visibleStart().deepEquivalent().node(); - CSSComputedStyleDeclaration computedStyle(node); - computedStyle.diff(mutableStyle.get()); + Node* node = selection()->selection().visibleStart().deepEquivalent().node(); + computedStyle(node)->diff(mutableStyle.get()); // Handle block styles, substracting these from the typing style. RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties(); blockStyle->diff(mutableStyle.get()); if (document() && blockStyle->length() > 0) - applyCommand(new ApplyStyleCommand(document(), blockStyle.get(), editingAction)); + applyCommand(ApplyStyleCommand::create(document(), blockStyle.get(), editingAction)); // Set the remaining style as the typing style. d->m_typingStyle = mutableStyle.release(); @@ -885,17 +909,17 @@ String Frame::selectionStartStylePropertyValue(int stylePropertyID) const return value; } -CSSComputedStyleDeclaration *Frame::selectionComputedStyle(Node *&nodeToRemove) const +PassRefPtr<CSSComputedStyleDeclaration> Frame::selectionComputedStyle(Node*& nodeToRemove) const { nodeToRemove = 0; if (!document()) return 0; - if (selectionController()->isNone()) + if (selection()->isNone()) return 0; - RefPtr<Range> range(selectionController()->toRange()); + RefPtr<Range> range(selection()->toRange()); Position pos = range->editingStartPosition(); Element *elem = pos.element(); @@ -932,7 +956,7 @@ CSSComputedStyleDeclaration *Frame::selectionComputedStyle(Node *&nodeToRemove) nodeToRemove = styleElement.get(); } - return new CSSComputedStyleDeclaration(styleElement); + return computedStyle(styleElement.release()); } void Frame::textFieldDidBeginEditing(Element* e) @@ -1006,11 +1030,11 @@ void Frame::applyEditingStyleToElement(Element* element) const ASSERT(style); ExceptionCode ec = 0; - style->setProperty(CSS_PROP_WORD_WRAP, "break-word", false, ec); + style->setProperty(CSSPropertyWordWrap, "break-word", false, ec); ASSERT(ec == 0); - style->setProperty(CSS_PROP__WEBKIT_NBSP_MODE, "space", false, ec); + style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec); ASSERT(ec == 0); - style->setProperty(CSS_PROP__WEBKIT_LINE_BREAK, "after-white-space", false, ec); + style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec); ASSERT(ec == 0); } @@ -1058,141 +1082,88 @@ void Frame::lifeSupportTimerFired(Timer<Frame>*) deref(); } -KJS::Bindings::RootObject* Frame::bindingRootObject() +void Frame::clearDOMWindow() { - if (!scriptProxy()->isEnabled()) - return 0; - - if (!d->m_bindingRootObject) { - JSLock lock; - d->m_bindingRootObject = KJS::Bindings::RootObject::create(0, scriptProxy()->globalObject()); + if (d->m_domWindow) { + d->m_liveFormerWindows.add(d->m_domWindow.get()); + d->m_domWindow->clear(); } - return d->m_bindingRootObject.get(); + d->m_domWindow = 0; } -PassRefPtr<KJS::Bindings::RootObject> Frame::createRootObject(void* nativeHandle, KJS::JSGlobalObject* globalObject) -{ - RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle); - if (it != d->m_rootObjects.end()) - return it->second; - - RefPtr<KJS::Bindings::RootObject> rootObject = KJS::Bindings::RootObject::create(nativeHandle, globalObject); - - d->m_rootObjects.set(nativeHandle, rootObject); - return rootObject.release(); -} - -#if USE(NPOBJECT) -NPObject* Frame::windowScriptNPObject() -{ - if (!d->m_windowScriptNPObject) { - if (scriptProxy()->isEnabled()) { - // JavaScript is enabled, so there is a JavaScript window object. Return an NPObject bound to the window - // object. - KJS::JSLock lock; - KJS::JSObject* win = KJS::Window::retrieveWindow(this); - ASSERT(win); - KJS::Bindings::RootObject* root = bindingRootObject(); - d->m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root); - } else { - // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object. - // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object. - d->m_windowScriptNPObject = _NPN_CreateNoScriptObject(); - } - } - - return d->m_windowScriptNPObject; -} -#endif - -void Frame::clearScriptProxy() +RenderView* Frame::contentRenderer() const { - if (d->m_jscript) - d->m_jscript->clear(); + Document* doc = document(); + if (!doc) + return 0; + RenderObject* object = doc->renderer(); + if (!object) + return 0; + ASSERT(object->isRenderView()); + return static_cast<RenderView*>(object); } -void Frame::clearDOMWindow() +HTMLFrameOwnerElement* Frame::ownerElement() const { - if (d->m_domWindow) - d->m_domWindow->clear(); + return d->m_ownerElement; } -void Frame::cleanupScriptObjectsForPlugin(void* nativeHandle) +RenderPart* Frame::ownerRenderer() const { - RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle); - - if (it == d->m_rootObjects.end()) - return; - - it->second->invalidate(); - d->m_rootObjects.remove(it); + HTMLFrameOwnerElement* ownerElement = d->m_ownerElement; + if (!ownerElement) + return 0; + RenderObject* object = ownerElement->renderer(); + if (!object) + return 0; + // FIXME: If <object> is ever fixed to disassociate itself from frames + // that it has started but canceled, then this can turn into an ASSERT + // since d->m_ownerElement would be 0 when the load is canceled. + // https://bugs.webkit.org/show_bug.cgi?id=18585 + if (!object->isRenderPart()) + return 0; + return static_cast<RenderPart*>(object); } - -void Frame::clearScriptObjects() -{ - JSLock lock; - - RootObjectMap::const_iterator end = d->m_rootObjects.end(); - for (RootObjectMap::const_iterator it = d->m_rootObjects.begin(); it != end; ++it) - it->second->invalidate(); - - d->m_rootObjects.clear(); - if (d->m_bindingRootObject) { - d->m_bindingRootObject->invalidate(); - d->m_bindingRootObject = 0; - } - -#if USE(NPOBJECT) - if (d->m_windowScriptNPObject) { - // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window - // script object properly. - // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point. - _NPN_DeallocateObject(d->m_windowScriptNPObject); - d->m_windowScriptNPObject = 0; - } -#endif - - clearPlatformScriptObjects(); +bool Frame::isDisconnected() const +{ + return d->m_isDisconnected; } -RenderObject *Frame::renderer() const +void Frame::setIsDisconnected(bool isDisconnected) { - Document *doc = document(); - return doc ? doc->renderer() : 0; + d->m_isDisconnected = isDisconnected; } -HTMLFrameOwnerElement* Frame::ownerElement() const +bool Frame::excludeFromTextSearch() const { - return d->m_ownerElement; + return d->m_excludeFromTextSearch; } -RenderPart* Frame::ownerRenderer() +void Frame::setExcludeFromTextSearch(bool exclude) { - HTMLFrameOwnerElement* ownerElement = d->m_ownerElement; - if (!ownerElement) - return 0; - return static_cast<RenderPart*>(ownerElement->renderer()); + d->m_excludeFromTextSearch = exclude; } // returns FloatRect because going through IntRect would truncate any floats FloatRect Frame::selectionRect(bool clipToVisibleContent) const { - RenderView *root = static_cast<RenderView*>(renderer()); - if (!root) + RenderView* root = contentRenderer(); + FrameView* view = d->m_view.get(); + if (!root || !view) return IntRect(); IntRect selectionRect = root->selectionRect(clipToVisibleContent); - return clipToVisibleContent ? intersection(selectionRect, d->m_view->visibleContentRect()) : selectionRect; + return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect; } void Frame::selectionTextRects(Vector<FloatRect>& rects, bool clipToVisibleContent) const { - RenderView *root = static_cast<RenderView*>(renderer()); + RenderView* root = contentRenderer(); if (!root) return; - RefPtr<Range> selectedRange = selectionController()->toRange(); + RefPtr<Range> selectedRange = selection()->toRange(); Vector<IntRect> intRects; selectedRange->addLineBoxRects(intRects, true); @@ -1224,7 +1195,7 @@ static HTMLFormElement *scanForForm(Node *start) if (n->hasTagName(formTag)) return static_cast<HTMLFormElement*>(n); else if (n->isHTMLElement() && static_cast<HTMLElement*>(n)->isGenericFormElement()) - return static_cast<HTMLGenericFormElement*>(n)->form(); + return static_cast<HTMLFormControlElement*>(n)->form(); else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) { Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument(); if (HTMLFormElement *frameResult = scanForForm(childDoc)) @@ -1240,7 +1211,7 @@ HTMLFormElement *Frame::currentForm() const // start looking either at the active (first responder) node, or where the selection is Node *start = d->m_doc ? d->m_doc->focusedNode() : 0; if (!start) - start = selectionController()->start().node(); + start = selection()->start().node(); // try walking up the node tree to find a form element Node *n; @@ -1249,7 +1220,7 @@ HTMLFormElement *Frame::currentForm() const return static_cast<HTMLFormElement*>(n); else if (n->isHTMLElement() && static_cast<HTMLElement*>(n)->isGenericFormElement()) - return static_cast<HTMLGenericFormElement*>(n)->form(); + return static_cast<HTMLFormControlElement*>(n)->form(); } // try walking forward in the node tree to find a form element @@ -1261,12 +1232,12 @@ void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const { IntRect rect; - switch (selectionController()->state()) { + switch (selection()->state()) { case Selection::NONE: return; case Selection::CARET: - rect = selectionController()->caretRect(); + rect = selection()->caretRect(); break; case Selection::RANGE: @@ -1274,7 +1245,7 @@ void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const break; } - Position start = selectionController()->start(); + Position start = selection()->start(); ASSERT(start.node()); if (start.node() && start.node()->renderer()) { @@ -1282,87 +1253,27 @@ void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const // the selection rect could intersect more than just that. // See <rdar://problem/4799899>. if (RenderLayer *layer = start.node()->renderer()->enclosingLayer()) - layer->scrollRectToVisible(rect, alignment, alignment); + layer->scrollRectToVisible(rect, false, alignment, alignment); } } void Frame::revealCaret(const RenderLayer::ScrollAlignment& alignment) const { - if (selectionController()->isNone()) + if (selection()->isNone()) return; - Position extent = selectionController()->extent(); + Position extent = selection()->extent(); if (extent.node() && extent.node()->renderer()) { IntRect extentRect = VisiblePosition(extent).caretRect(); RenderLayer* layer = extent.node()->renderer()->enclosingLayer(); if (layer) - layer->scrollRectToVisible(extentRect, alignment, alignment); + layer->scrollRectToVisible(extentRect, false, alignment, alignment); } } -// FIXME: why is this here instead of on the FrameView? -void Frame::paint(GraphicsContext* p, const IntRect& rect) -{ -#ifndef NDEBUG - bool fillWithRed; - if (!document() || document()->printing()) - fillWithRed = false; // Printing, don't fill with red (can't remember why). - else if (document()->ownerElement()) - fillWithRed = false; // Subframe, don't fill with red. - else if (view() && view()->isTransparent()) - fillWithRed = false; // Transparent, don't fill with red. - else if (d->m_paintRestriction == PaintRestrictionSelectionOnly || d->m_paintRestriction == PaintRestrictionSelectionOnlyBlackText) - fillWithRed = false; // Selections are transparent, don't fill with red. - else if (d->m_elementToDraw) - fillWithRed = false; // Element images are transparent, don't fill with red. - else - fillWithRed = true; - - if (fillWithRed) - p->fillRect(rect, Color(0xFF, 0, 0)); -#endif - - bool isTopLevelPainter = !s_currentPaintTimeStamp; - if (isTopLevelPainter) - s_currentPaintTimeStamp = currentTime(); - - if (renderer()) { - ASSERT(d->m_view && !d->m_view->needsLayout()); - ASSERT(!d->m_isPainting); - - d->m_isPainting = true; - - // d->m_elementToDraw is used to draw only one element - RenderObject *eltRenderer = d->m_elementToDraw ? d->m_elementToDraw->renderer() : 0; - if (d->m_paintRestriction == PaintRestrictionNone) - renderer()->document()->invalidateRenderedRectsForMarkersInRect(rect); - renderer()->layer()->paint(p, rect, d->m_paintRestriction, eltRenderer); - - d->m_isPainting = false; - - // Regions may have changed as a result of the visibility/z-index of element changing. - if (renderer()->document()->dashboardRegionsDirty()) - renderer()->view()->frameView()->updateDashboardRegions(); - } else - LOG_ERROR("called Frame::paint with nil renderer"); - - if (isTopLevelPainter) - s_currentPaintTimeStamp = 0; -} - -void Frame::setPaintRestriction(PaintRestriction pr) -{ - d->m_paintRestriction = pr; -} - -bool Frame::isPainting() const -{ - return d->m_isPainting; -} - void Frame::adjustPageHeight(float *newBottom, float oldTop, float oldBottom, float bottomLimit) { - RenderView *root = static_cast<RenderView*>(document()->renderer()); + RenderView* root = contentRenderer(); if (root) { // Use a context with painting disabled. GraphicsContext context((PlatformGraphicsContext*)0); @@ -1436,7 +1347,7 @@ void Frame::forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth void Frame::sendResizeEvent() { if (Document* doc = document()) - doc->dispatchWindowEvent(EventNames::resizeEvent, false, false); + doc->dispatchWindowEvent(eventNames().resizeEvent, false, false); } void Frame::sendScrollEvent() @@ -1448,25 +1359,25 @@ void Frame::sendScrollEvent() Document* doc = document(); if (!doc) return; - doc->dispatchHTMLEvent(scrollEvent, true, false); + doc->dispatchEventForType(eventNames().scrollEvent, true, false); } -void Frame::clearTimers(FrameView *view) +void Frame::clearTimers(FrameView *view, Document *document) { if (view) { view->unscheduleRelayout(); if (view->frame()) { - Document* document = view->frame()->document(); if (document && document->renderer() && document->renderer()->hasLayer()) document->renderer()->layer()->suspendMarquees(); - view->frame()->animationController()->suspendAnimations(); + view->frame()->animation()->suspendAnimations(document); + view->frame()->eventHandler()->stopAutoscrollTimer(); } } } void Frame::clearTimers() { - clearTimers(d->m_view.get()); + clearTimers(d->m_view.get(), document()); } RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const @@ -1475,10 +1386,10 @@ RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const if (!document()) return 0; - if (selectionController()->isNone()) + if (selection()->isNone()) return 0; - Position pos = selectionController()->selection().visibleStart().deepEquivalent(); + Position pos = selection()->selection().visibleStart().deepEquivalent(); if (!pos.isCandidate()) return 0; Node *node = pos.node(); @@ -1511,14 +1422,14 @@ void Frame::setSelectionFromNone() // Put a caret inside the body if the entire frame is editable (either the // entire WebView is editable or designMode is on for this document). Document *doc = document(); - if (!doc || !selectionController()->isNone() || !isContentEditable()) + if (!doc || !selection()->isNone() || !isContentEditable()) return; Node* node = doc->documentElement(); while (node && !node->hasTagName(bodyTag)) node = node->traverseNextNode(); if (node) - selectionController()->setSelection(Selection(Position(node, 0), DOWNSTREAM)); + selection()->setSelection(Selection(Position(node, 0), DOWNSTREAM)); } bool Frame::inViewSourceMode() const @@ -1543,51 +1454,75 @@ UChar Frame::backslashAsCurrencySymbol() const return decoder->encoding().backslashAsCurrencySymbol(); } -static bool isInShadowTree(Node* node) -{ - for (Node* n = node; n; n = n->parentNode()) - if (n->isShadowNode()) - return true; - return false; -} - // Searches from the beginning of the document if nothing is selected. bool Frame::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection) { if (target.isEmpty() || !document()) return false; + if (excludeFromTextSearch()) + return false; + // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge // is used depends on whether we're searching forward or backward, and whether startInSelection is set. RefPtr<Range> searchRange(rangeOfContents(document())); - Selection selection(selectionController()->selection()); - Node* selectionBaseNode = selection.base().node(); - - // FIXME 3099526: We don't search in the shadow trees (e.g. text fields and textareas), though we'd like to - // someday. If we don't explicitly skip them here, we'll miss hits in the regular content. - bool selectionIsInMainContent = selectionBaseNode && !isInShadowTree(selectionBaseNode); + Selection selection = this->selection()->selection(); + + if (forward) + setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd()); + else + setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart()); - if (selectionIsInMainContent) { + Node* shadowTreeRoot = selection.shadowTreeRootNode(); + if (shadowTreeRoot) { + ExceptionCode ec = 0; if (forward) - setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd()); + searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); else - setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart()); + searchRange->setStart(shadowTreeRoot, 0, ec); } + RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag)); // If we started in the selection and the found range exactly matches the existing selection, find again. // Build a selection with the found range to remove collapsed whitespace. // Compare ranges instead of selection objects to ignore the way that the current selection was made. - if (startInSelection && selectionIsInMainContent && *Selection(resultRange.get()).toRange() == *selection.toRange()) { + if (startInSelection && *Selection(resultRange.get()).toRange() == *selection.toRange()) { searchRange = rangeOfContents(document()); if (forward) setStart(searchRange.get(), selection.visibleEnd()); else setEnd(searchRange.get(), selection.visibleStart()); + + if (shadowTreeRoot) { + ExceptionCode ec = 0; + if (forward) + searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); + else + searchRange->setStart(shadowTreeRoot, 0, ec); + } + resultRange = findPlainText(searchRange.get(), target, forward, caseFlag); } - int exception = 0; + ExceptionCode exception = 0; + + // If nothing was found in the shadow tree, search in main content following the shadow tree. + if (resultRange->collapsed(exception) && shadowTreeRoot) { + searchRange = rangeOfContents(document()); + if (forward) + searchRange->setStartAfter(shadowTreeRoot->shadowParentNode(), exception); + else + searchRange->setEndBefore(shadowTreeRoot->shadowParentNode(), exception); + + resultRange = findPlainText(searchRange.get(), target, forward, caseFlag); + } + if (!editor()->insideVisibleArea(resultRange.get())) { + resultRange = editor()->nextVisibleRange(resultRange.get(), target, forward, caseFlag, wrapFlag); + if (!resultRange) + return false; + } + // If we didn't find anything and we're wrapping, search again in the entire document (this will // redundantly re-search the area already searched in some cases). if (resultRange->collapsed(exception) && wrapFlag) { @@ -1601,7 +1536,7 @@ bool Frame::findString(const String& target, bool forward, bool caseFlag, bool w if (resultRange->collapsed(exception)) return false; - selectionController()->setSelection(Selection(resultRange.get(), DOWNSTREAM)); + this->selection()->setSelection(Selection(resultRange.get(), DOWNSTREAM)); revealSelection(); return true; } @@ -1613,12 +1548,18 @@ unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsig RefPtr<Range> searchRange(rangeOfContents(document())); - int exception = 0; + ExceptionCode exception = 0; unsigned matchCount = 0; do { RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag)); - if (resultRange->collapsed(exception)) - break; + if (resultRange->collapsed(exception)) { + if (!resultRange->startContainer()->isInShadowTree()) + break; + + searchRange = rangeOfContents(document()); + searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), exception); + continue; + } // A non-collapsed result range can in some funky whitespace cases still not // advance the range's start position (4509328). Break to avoid infinite loop. @@ -1626,26 +1567,33 @@ unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsig if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM)) break; - ++matchCount; - - document()->addMarker(resultRange.get(), DocumentMarker::TextMatch); + // Only treat the result as a match if it is visible + if (editor()->insideVisibleArea(resultRange.get())) { + ++matchCount; + document()->addMarker(resultRange.get(), DocumentMarker::TextMatch); + } // Stop looking if we hit the specified limit. A limit of 0 means no limit. if (limit > 0 && matchCount >= limit) break; setStart(searchRange.get(), newStart); + Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); + if (searchRange->collapsed(exception) && shadowTreeRoot) + searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), exception); } while (true); // Do a "fake" paint in order to execute the code that computes the rendered rect for // each text match. Document* doc = document(); - if (doc && d->m_view && renderer()) { + if (doc && d->m_view && contentRenderer()) { doc->updateLayout(); // Ensure layout is up to date. - IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect())); - GraphicsContext context((PlatformGraphicsContext*)0); - context.setPaintingDisabled(true); - paint(&context, visibleRect); + IntRect visibleRect = d->m_view->visibleContentRect(); + if (!visibleRect.isEmpty()) { + GraphicsContext context((PlatformGraphicsContext*)0); + context.setPaintingDisabled(true); + d->m_view->paintContents(&context, visibleRect); + } } return matchCount; @@ -1673,11 +1621,16 @@ FrameTree* Frame::tree() const DOMWindow* Frame::domWindow() const { if (!d->m_domWindow) - d->m_domWindow = new DOMWindow(const_cast<Frame*>(this)); + d->m_domWindow = DOMWindow::create(const_cast<Frame*>(this)); return d->m_domWindow.get(); } +void Frame::clearFormerDOMWindow(DOMWindow* window) +{ + d->m_liveFormerWindows.remove(window); +} + Page* Frame::page() const { return d->m_page; @@ -1693,22 +1646,27 @@ void Frame::pageDestroyed() if (Frame* parent = tree()->parent()) parent->loader()->checkLoadComplete(); - if (d->m_page && d->m_page->focusController()->focusedFrame() == this) - d->m_page->focusController()->setFocusedFrame(0); + // FIXME: It's unclear as to why this is called more than once, but it is, + // so page() could be NULL. + if (page() && page()->focusController()->focusedFrame() == this) + page()->focusController()->setFocusedFrame(0); + + script()->clearWindowShell(); // This will stop any JS timers - if (d->m_jscript && d->m_jscript->haveGlobalObject()) - if (KJS::Window* w = KJS::Window::retrieveWindow(this)) - w->disconnectFrame(); + if (script()->haveWindowShell()) + script()->windowShell()->disconnectFrame(); + + script()->clearScriptObjects(); - clearScriptObjects(); - d->m_page = 0; } void Frame::disconnectOwnerElement() { if (d->m_ownerElement) { + if (Document* doc = document()) + doc->clearAXObjectCache(); d->m_ownerElement->m_contentFrame = 0; if (d->m_page) d->m_page->decrementFrameCount(); @@ -1718,23 +1676,14 @@ void Frame::disconnectOwnerElement() String Frame::documentTypeString() const { - if (Document *doc = document()) - if (DocumentType *doctype = doc->realDocType()) - return doctype->toString(); + if (Document* doc = document()) { + if (DocumentType* doctype = doc->doctype()) + return createMarkup(doctype); + } return String(); } -bool Frame::prohibitsScrolling() const -{ - return d->m_prohibitsScrolling; -} - -void Frame::setProhibitsScrolling(bool prohibit) -{ - d->m_prohibitsScrolling = prohibit; -} - void Frame::focusWindow() { if (!page()) @@ -1770,7 +1719,7 @@ bool Frame::shouldClose() if (!body) return true; - RefPtr<BeforeUnloadEvent> beforeUnloadEvent = new BeforeUnloadEvent; + RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create(); beforeUnloadEvent->setTarget(doc); doc->handleWindowEvent(beforeUnloadEvent.get(), false); @@ -1803,8 +1752,8 @@ void Frame::respondToChangedSelection(const Selection& oldSelection, bool closeT if (isContinuousSpellCheckingEnabled) { Selection newAdjacentWords; Selection newSelectedSentence; - if (selectionController()->selection().isContentEditable()) { - VisiblePosition newStart(selectionController()->selection().visibleStart()); + if (selection()->selection().isContentEditable()) { + VisiblePosition newStart(selection()->selection().visibleStart()); newAdjacentWords = Selection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary)); if (isContinuousGrammarCheckingEnabled) newSelectedSentence = Selection(startOfSentence(newStart), endOfSentence(newStart)); @@ -1867,7 +1816,7 @@ Document* Frame::documentAtPoint(const IntPoint& point) IntPoint pt = view()->windowToContents(point); HitTestResult result = HitTestResult(pt); - if (renderer()) + if (contentRenderer()) result = eventHandler()->hitTestResultAtPoint(pt, false); return result.innerNode() ? result.innerNode()->document() : 0; } @@ -1876,41 +1825,66 @@ FramePrivate::FramePrivate(Page* page, Frame* parent, Frame* thisFrame, HTMLFram FrameLoaderClient* frameLoaderClient) : m_page(page) , m_treeNode(thisFrame, parent) + , m_loader(thisFrame, frameLoaderClient) , m_ownerElement(ownerElement) - , m_jscript(0) - , m_zoomFactor(parent ? parent->d->m_zoomFactor : 100) + , m_script(thisFrame) + , m_zoomFactor(parent ? parent->d->m_zoomFactor : 1.0f) , m_selectionGranularity(CharacterGranularity) , m_selectionController(thisFrame) , m_caretBlinkTimer(thisFrame, &Frame::caretBlinkTimerFired) , m_editor(thisFrame) , m_eventHandler(thisFrame) , m_animationController(thisFrame) + , m_lifeSupportTimer(thisFrame, &Frame::lifeSupportTimerFired) , m_caretVisible(false) , m_caretPaint(true) - , m_isPainting(false) - , m_lifeSupportTimer(thisFrame, &Frame::lifeSupportTimerFired) - , m_loader(new FrameLoader(thisFrame, frameLoaderClient)) - , m_paintRestriction(PaintRestrictionNone) , m_highlightTextMatches(false) , m_inViewSourceMode(false) - , frameCount(0) - , m_prohibitsScrolling(false) , m_needsReapplyStyles(false) - , m_windowScriptNPObject(0) + , m_isDisconnected(false) + , m_excludeFromTextSearch(false) #if FRAME_LOADS_USER_STYLESHEET , m_userStyleSheetLoader(0) #endif -#if PLATFORM(MAC) - , m_windowScriptObject(nil) - , m_bridge(nil) -#endif { } FramePrivate::~FramePrivate() { - delete m_jscript; - delete m_loader; } +#ifdef ANDROID_INSTRUMENT +void Frame::resetTimeCounter() { + JSC::JSGlobalObject::resetTimeCounter(); + resetLayoutTimeCounter(); + resetPaintTimeCounter(); + resetCSSTimeCounter(); + resetParsingTimeCounter(); + resetCalculateStyleTimeCounter(); + resetFramebridgeTimeCounter(); + resetSharedTimerTimeCounter(); + resetResourceLoadTimeCounter(); + resetWebViewCoreTimeCounter(); + LOGD("*-* Start browser instrument\n"); +} + +void Frame::reportTimeCounter(String url, int total, int totalThreadTime) +{ + LOGD("*-* Total load time: %d ms, thread time: %d ms for %s\n", + total, totalThreadTime, url.utf8().data()); + JSC::JSGlobalObject::reportTimeCounter(); + reportLayoutTimeCounter(); + reportPaintTimeCounter(); + reportCSSTimeCounter(); + reportParsingTimeCounter(); + reportCalculateStyleTimeCounter(); + reportFramebridgeTimeCounter(); + reportSharedTimerTimeCounter(); + reportResourceLoadTimeCounter(); + reportWebViewCoreTimeCounter(); + LOGD("Current cache has %d bytes live and %d bytes dead", + cache()->getLiveSize(), cache()->getDeadSize()); +} +#endif + } // namespace WebCore diff --git a/WebCore/page/Frame.h b/WebCore/page/Frame.h index ea9eadc..0dd3a71 100644 --- a/WebCore/page/Frame.h +++ b/WebCore/page/Frame.h @@ -1,4 +1,3 @@ -// -*- c-basic-offset: 4 -*- /* * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> * 1999-2001 Lars Knoll <knoll@kde.org> @@ -6,8 +5,9 @@ * 2000-2001 Simon Hausmann <hausmann@kde.org> * 2000-2001 Dirk Mueller <mueller@kde.org> * 2000 Stefan Schimanski <1Stein@gmx.de> - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2007 Trolltech ASA + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -28,101 +28,54 @@ #ifndef Frame_h #define Frame_h -#include "Color.h" -#include "EditAction.h" #include "DragImage.h" +#include "EditAction.h" #include "RenderLayer.h" #include "TextGranularity.h" -#include "VisiblePosition.h" -#include <wtf/unicode/Unicode.h> -#include <wtf/Forward.h> -#include <wtf/Vector.h> -#include "RenderObject.h" - -struct NPObject; - -namespace KJS { - - class Interpreter; - class JSGlobalObject; - - namespace Bindings { - class Instance; - class RootObject; - } - -} #if PLATFORM(MAC) -#ifdef __OBJC__ -@class NSArray; -@class NSDictionary; -@class NSMenu; -@class NSMutableDictionary; -@class NSString; -@class WebCoreFrameBridge; -@class WebScriptObject; -#else +#ifndef __OBJC__ class NSArray; class NSDictionary; -class NSMenu; class NSMutableDictionary; class NSString; -class WebCoreFrameBridge; -class WebScriptObject; typedef int NSWritingDirection; #endif #endif +#if PLATFORM(ANDROID) +#include "CacheBuilder.h" +#endif + namespace WebCore { -class AnimationController; -class CSSComputedStyleDeclaration; -class CSSMutableStyleDeclaration; -class CSSStyleDeclaration; -class DOMWindow; -class Document; class Editor; -class Element; class EventHandler; -class FloatRect; class FrameLoader; class FrameLoaderClient; -class HTMLFrameOwnerElement; -class HTMLTableCellElement; class FramePrivate; class FrameTree; -class FrameView; -class GraphicsContext; -class HTMLFormElement; -class IntRect; -class KJSProxy; -class KURL; -class Node; -class Page; -class Range; +class HTMLFrameOwnerElement; +class HTMLTableCellElement; +class ScriptController; +class RegularExpression; class RenderPart; class Selection; class SelectionController; -class Settings; class Widget; -struct FrameLoadRequest; - template <typename T> class Timer; class Frame : public RefCounted<Frame> { public: - Frame(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*); - virtual void setView(FrameView*); - virtual ~Frame(); - - void init(); + static PassRefPtr<Frame> create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client) + { + return adoptRef(new Frame(page, ownerElement, client)); + } + void setView(FrameView*); + ~Frame(); -#if PLATFORM(MAC) - void setBridge(WebCoreFrameBridge*); - WebCoreFrameBridge* bridge() const; -#endif + void init(); Page* page() const; HTMLFrameOwnerElement* ownerElement() const; @@ -134,21 +87,24 @@ public: FrameView* view() const; DOMWindow* domWindow() const; + void clearFormerDOMWindow(DOMWindow*); Editor* editor() const; EventHandler* eventHandler() const; FrameLoader* loader() const; - SelectionController* selectionController() const; + SelectionController* selection() const; FrameTree* tree() const; - AnimationController* animationController() const; + AnimationController* animation() const; + ScriptController* script(); - // FIXME: Rename to contentRenderer and change type to RenderView. - RenderObject* renderer() const; // root renderer for the document contained in this frame - RenderPart* ownerRenderer(); // renderer for the element that contains this frame + RenderView* contentRenderer() const; // root renderer for the document contained in this frame + RenderPart* ownerRenderer() const; // renderer for the element that contains this frame + + bool isDisconnected() const; + void setIsDisconnected(bool); + bool excludeFromTextSearch() const; + void setExcludeFromTextSearch(bool); friend class FramePrivate; -#ifdef ANDROID_BRIDGE - friend class FrameAndroid; -#endif #ifdef ANDROID_INSTRUMENT void resetTimeCounter(); @@ -166,13 +122,13 @@ private: void resetLayoutTimeCounter(); void reportLayoutTimeCounter(); - + void resetPaintTimeCounter(); void reportPaintTimeCounter(); - + void resetSharedTimerTimeCounter(); void reportSharedTimerTimeCounter(); - + void resetResourceLoadTimeCounter(); void reportResourceLoadTimeCounter(); @@ -184,6 +140,8 @@ private: #endif private: + Frame(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*); + FramePrivate* d; // === undecided, would like to consider moving to another class @@ -208,25 +166,10 @@ public: static void cancelAllKeepAlive(); #endif - KJS::Bindings::Instance* createScriptInstanceForWidget(Widget*); - KJS::Bindings::RootObject* bindingRootObject(); - - PassRefPtr<KJS::Bindings::RootObject> createRootObject(void* nativeHandle, KJS::JSGlobalObject*); - -#if PLATFORM(MAC) - WebScriptObject* windowScriptObject(); -#endif - -#if USE(NPOBJECT) - NPObject* windowScriptNPObject(); -#endif - void setDocument(PassRefPtr<Document>); - KJSProxy* scriptProxy(); - void clearTimers(); - static void clearTimers(FrameView*); + static void clearTimers(FrameView*, Document*); // Convenience, to avoid repeating the code to dig down to get this. UChar backslashAsCurrencySymbol() const; @@ -237,19 +180,13 @@ public: String documentTypeString() const; - void dashboardRegionsChanged(); - - void clearScriptProxy(); + // This method -- and the corresponding list of former DOM windows -- + // should move onto ScriptController void clearDOMWindow(); - void clearScriptObjects(); - void cleanupScriptObjectsForPlugin(void*); - private: - void clearPlatformScriptObjects(); - void lifeSupportTimerFired(Timer<Frame>*); - + // === to be moved into Document public: @@ -263,26 +200,19 @@ public: // === to be moved into FrameView -public: - void paint(GraphicsContext*, const IntRect&); - void setPaintRestriction(PaintRestriction); - bool isPainting() const; - - static double currentPaintTimeStamp() { return s_currentPaintTimeStamp; } // returns 0 if not painting - +public: void forceLayout(bool allowSubtree = false); void forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth, bool adjustViewSize); void adjustPageHeight(float* newBottom, float oldTop, float oldBottom, float bottomLimit); - void setZoomFactor(int percent); - int zoomFactor() const; // FIXME: This is a multiplier for text size only; needs a better name. - - bool prohibitsScrolling() const; - void setProhibitsScrolling(const bool); - -private: - static double s_currentPaintTimeStamp; // used for detecting decoded resource thrash in the cache + void setZoomFactor(float scale, bool isTextOnly); + float zoomFactor() const; + bool isZoomFactorTextOnly() const; + bool shouldApplyTextZoom() const; + bool shouldApplyPageZoom() const; + float pageZoomFactor() const { return shouldApplyPageZoom() ? zoomFactor() : 1.0f; } + float textZoomFactor() const { return shouldApplyTextZoom() ? zoomFactor() : 1.0f; } // === to be moved into Chrome @@ -324,7 +254,7 @@ public: bool markedTextMatchesAreHighlighted() const; void setMarkedTextMatchesAreHighlighted(bool flag); - CSSComputedStyleDeclaration* selectionComputedStyle(Node*& nodeToRemove) const; + PassRefPtr<CSSComputedStyleDeclaration> selectionComputedStyle(Node*& nodeToRemove) const; void textFieldDidBeginEditing(Element*); void textFieldDidEndEditing(Element*); @@ -395,31 +325,35 @@ public: NSString* searchForLabelsBeforeElement(NSArray* labels, Element*); NSString* matchLabelsAgainstElement(NSArray* labels, Element*); +#if ENABLE(DASHBOARD_SUPPORT) NSMutableDictionary* dashboardRegionsDictionary(); - - void willPopupMenu(NSMenu*); +#endif NSImage* selectionImage(bool forceBlackText = false) const; NSImage* snapshotDragImage(Node*, NSRect* imageRect, NSRect* elementRect) const; + NSImage* nodeImage(Node*) const; private: NSImage* imageFromRect(NSRect) const; -// === to be moved into Chrome - -public: - FloatRect customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect, Node*); - void paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line, Node*); - // === to be moved into Editor public: NSDictionary* fontAttributesForSelectionStart() const; NSWritingDirection baseWritingDirectionForSelectionStart() const; - void issuePasteCommand(); #endif +#if PLATFORM(ANDROID) + +public: + CacheBuilder& getCacheBuilder() { return m_cacheBuilder; } + +private: + CacheBuilder m_cacheBuilder; + friend class CacheBuilder; + +#endif }; } // namespace WebCore diff --git a/WebCore/page/FrameLoadRequest.h b/WebCore/page/FrameLoadRequest.h index c916a05..bfb750c 100644 --- a/WebCore/page/FrameLoadRequest.h +++ b/WebCore/page/FrameLoadRequest.h @@ -1,4 +1,3 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. * @@ -35,12 +34,18 @@ namespace WebCore { public: FrameLoadRequest() : m_lockHistory(false) +#ifdef ANDROID_USER_GESTURE + , m_wasUserGesture(true) +#endif { } FrameLoadRequest(const ResourceRequest& resourceRequest) : m_resourceRequest(resourceRequest) , m_lockHistory(false) +#ifdef ANDROID_USER_GESTURE + , m_wasUserGesture(true) +#endif { } @@ -48,6 +53,9 @@ namespace WebCore { : m_resourceRequest(resourceRequest) , m_frameName(frameName) , m_lockHistory(false) +#ifdef ANDROID_USER_GESTURE + , m_wasUserGesture(true) +#endif { } @@ -62,10 +70,18 @@ namespace WebCore { bool lockHistory() const { return m_lockHistory; } void setLockHistory(bool lock) { m_lockHistory = lock; } +#ifdef ANDROID_USER_GESTURE + void setWasUserGesture(bool wasUserGesture) { m_wasUserGesture = wasUserGesture; } + bool wasUserGesture() const { return m_wasUserGesture; } +#endif + private: ResourceRequest m_resourceRequest; String m_frameName; bool m_lockHistory; +#ifdef ANDROID_USER_GESTURE + bool m_wasUserGesture; +#endif }; } diff --git a/WebCore/page/FramePrivate.h b/WebCore/page/FramePrivate.h index c780db2..2f7c59a 100644 --- a/WebCore/page/FramePrivate.h +++ b/WebCore/page/FramePrivate.h @@ -4,8 +4,8 @@ * 2000-2001 Simon Hausmann <hausmann@kde.org> * 2000-2001 Dirk Mueller <mueller@kde.org> * 2000 Stefan Schimanski <1Stein@gmx.de> - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2007 Trolltech ASA + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -29,29 +29,12 @@ #include "AnimationController.h" #include "Editor.h" #include "EventHandler.h" +#include "FrameLoader.h" #include "FrameTree.h" #include "Range.h" #include "SelectionController.h" #include "StringHash.h" - -namespace KJS { - class Interpreter; - - namespace Bindings { - class Instance; - class RootObject; - } -} - -#if PLATFORM(MAC) -#ifdef __OBJC__ -@class WebCoreFrameBridge; -@class WebScriptObject; -#else -class WebCoreFrameBridge; -class WebScriptObject; -#endif -#endif +#include "ScriptController.h" #if PLATFORM(WIN) #include "FrameWin.h" @@ -62,8 +45,6 @@ namespace WebCore { #if FRAME_LOADS_USER_STYLESHEET class UserStyleSheetLoader; #endif - - typedef HashMap<void*, RefPtr<KJS::Bindings::RootObject> > RootObjectMap; class FramePrivate { public: @@ -72,18 +53,20 @@ namespace WebCore { Page* m_page; FrameTree m_treeNode; + FrameLoader m_loader; RefPtr<DOMWindow> m_domWindow; + HashSet<DOMWindow*> m_liveFormerWindows; HTMLFrameOwnerElement* m_ownerElement; RefPtr<FrameView> m_view; RefPtr<Document> m_doc; - KJSProxy* m_jscript; + ScriptController m_script; String m_kjsStatusBarText; String m_kjsDefaultStatusBarText; - int m_zoomFactor; + float m_zoomFactor; TextGranularity m_selectionGranularity; @@ -94,40 +77,22 @@ namespace WebCore { EventHandler m_eventHandler; AnimationController m_animationController; - bool m_caretVisible : 1; - bool m_caretPaint : 1; - bool m_isPainting : 1; - RefPtr<CSSMutableStyleDeclaration> m_typingStyle; Timer<Frame> m_lifeSupportTimer; - FrameLoader* m_loader; - - RefPtr<Node> m_elementToDraw; - PaintRestriction m_paintRestriction; + bool m_caretVisible; + bool m_caretPaint; bool m_highlightTextMatches; - bool m_inViewSourceMode; - - unsigned frameCount; - - bool m_prohibitsScrolling; - bool m_needsReapplyStyles; + bool m_isDisconnected; + bool m_excludeFromTextSearch; - // The root object used for objects bound outside the context of a plugin. - RefPtr<KJS::Bindings::RootObject> m_bindingRootObject; - RootObjectMap m_rootObjects; - NPObject* m_windowScriptNPObject; #if FRAME_LOADS_USER_STYLESHEET UserStyleSheetLoader* m_userStyleSheetLoader; #endif -#if PLATFORM(MAC) - RetainPtr<WebScriptObject> m_windowScriptObject; - WebCoreFrameBridge* m_bridge; -#endif }; } diff --git a/WebCore/page/FrameTree.cpp b/WebCore/page/FrameTree.cpp index 92e198d..c9b4172 100644 --- a/WebCore/page/FrameTree.cpp +++ b/WebCore/page/FrameTree.cpp @@ -1,4 +1,3 @@ -// -*- c-basic-offset: 4 -*- /* * Copyright (C) 2006 Apple Computer, Inc. * @@ -23,6 +22,7 @@ #include "Frame.h" #include "Page.h" +#include "PageGroup.h" #include <stdarg.h> #include <wtf/Platform.h> #include <wtf/StringExtras.h> @@ -48,6 +48,18 @@ void FrameTree::setName(const AtomicString& name) m_name = parent()->tree()->uniqueChildName(name); } +void FrameTree::clearName() +{ + m_name = AtomicString(); +} + +Frame* FrameTree::parent(bool checkForDisconnectedFrame) const +{ + if (checkForDisconnectedFrame && m_thisFrame->isDisconnected()) + return 0; + return m_parent; +} + void FrameTree::appendChild(PassRefPtr<Frame> child) { ASSERT(child->page() == m_thisFrame->page()); @@ -118,7 +130,7 @@ AtomicString FrameTree::uniqueChildName(const AtomicString& requestedName) const String name; name += framePathPrefix; if (frame) - name += frame->tree()->name().domString().substring(framePathPrefixLength, + name += frame->tree()->name().string().substring(framePathPrefixLength, frame->tree()->name().length() - framePathPrefixLength - framePathSuffixLength); for (int i = chain.size() - 1; i >= 0; --i) { frame = chain[i]; @@ -162,7 +174,7 @@ Frame* FrameTree::find(const AtomicString& name) const return m_thisFrame; if (name == "_top") - return m_thisFrame->page()->mainFrame(); + return top(); if (name == "_parent") return parent() ? parent() : m_thisFrame; @@ -178,20 +190,26 @@ Frame* FrameTree::find(const AtomicString& name) const // Search the entire tree for this page next. Page* page = m_thisFrame->page(); + + // The frame could have been detached from the page, so check it. + if (!page) + return 0; + for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) if (frame->tree()->name() == name) return frame; - // Search the entire tree for all other pages in this namespace. - const HashSet<Page*>* pages = page->frameNamespace(); - if (pages) { - HashSet<Page*>::const_iterator end = pages->end(); - for (HashSet<Page*>::const_iterator it = pages->begin(); it != end; ++it) { - Page* otherPage = *it; - if (otherPage != page) - for (Frame* frame = otherPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) - if (frame->tree()->name() == name) - return frame; + // Search the entire tree of each of the other pages in this namespace. + // FIXME: Is random order OK? + const HashSet<Page*>& pages = page->group().pages(); + HashSet<Page*>::const_iterator end = pages.end(); + for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) { + Page* otherPage = *it; + if (otherPage != page) { + for (Frame* frame = otherPage->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (frame->tree()->name() == name) + return frame; + } } } @@ -282,14 +300,14 @@ Frame* FrameTree::deepLastChild() const return result; } -Frame* FrameTree::top() const +Frame* FrameTree::top(bool checkForDisconnectedFrame) const { - if (Page* page = m_thisFrame->page()) - return page->mainFrame(); - Frame* frame = m_thisFrame; - while (Frame* parent = frame->tree()->parent()) + for (Frame* parent = m_thisFrame; parent; parent = parent->tree()->parent()) { frame = parent; + if (checkForDisconnectedFrame && frame->isDisconnected()) + return frame; + } return frame; } diff --git a/WebCore/page/FrameTree.h b/WebCore/page/FrameTree.h index 3f3e20a..0952dcd 100644 --- a/WebCore/page/FrameTree.h +++ b/WebCore/page/FrameTree.h @@ -1,4 +1,3 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* * Copyright (C) 2006 Apple Computer, Inc. * @@ -22,11 +21,10 @@ #define FrameTree_h #include "AtomicString.h" +#include "Frame.h" namespace WebCore { - class Frame; - class FrameTree : Noncopyable { public: FrameTree(Frame* thisFrame, Frame* parentFrame) @@ -41,7 +39,8 @@ namespace WebCore { const AtomicString& name() const { return m_name; } void setName(const AtomicString&); - Frame* parent() const { return m_parent; } + void clearName(); + Frame* parent(bool checkForDisconnectedFrame = false) const; void setParent(Frame* parent) { m_parent = parent; } Frame* nextSibling() const { return m_nextSibling.get(); } @@ -64,7 +63,7 @@ namespace WebCore { AtomicString uniqueChildName(const AtomicString& requestedName) const; - Frame* top() const; + Frame* top(bool checkForDisconnectedFrame = false) const; private: Frame* deepLastChild() const; diff --git a/WebCore/page/FrameView.cpp b/WebCore/page/FrameView.cpp index 21adc10..e503b0d 100644 --- a/WebCore/page/FrameView.cpp +++ b/WebCore/page/FrameView.cpp @@ -3,7 +3,7 @@ * 1999 Lars Knoll <knoll@kde.org> * 1999 Antti Koivisto <koivisto@kde.org> * 2000 Dirk Mueller <mueller@kde.org> - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * (C) 2006 Graham Dennis (graham.dennis@gmail.com) * (C) 2006 Alexey Proskuryakov (ap@nypop.com) * @@ -22,29 +22,37 @@ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ +#ifdef ANDROID_INSTRUMENT +#define LOG_TAG "WebCore" +#endif #include "config.h" #include "FrameView.h" #include "AXObjectCache.h" +#include "CSSStyleSelector.h" +#include "ChromeClient.h" #include "EventHandler.h" #include "FloatRect.h" +#include "FocusController.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "GraphicsContext.h" #include "HTMLDocument.h" +#include "HTMLFrameElement.h" #include "HTMLFrameSetElement.h" #include "HTMLNames.h" #include "OverflowEvent.h" +#include "Page.h" #include "RenderPart.h" #include "RenderPartObject.h" #include "RenderTheme.h" #include "RenderView.h" +#include "Settings.h" +#include "SystemTime.h" #ifdef ANDROID_INSTRUMENT -#undef LOG -#include <utils/Log.h> #include "SystemTime.h" #include "FrameTree.h" #endif @@ -53,93 +61,111 @@ namespace WebCore { using namespace HTMLNames; +double FrameView::sCurrentPaintTimeStamp = 0.0; + struct ScheduledEvent { RefPtr<Event> m_event; RefPtr<EventTargetNode> m_eventTarget; - bool m_tempEvent; }; class FrameViewPrivate { public: FrameViewPrivate(FrameView* view) : m_slowRepaintObjectCount(0) - , layoutTimer(view, &FrameView::layoutTimerFired) - , layoutRoot(0) - , postLayoutTasksTimer(view, &FrameView::postLayoutTimerFired) + , m_layoutTimer(view, &FrameView::layoutTimerFired) + , m_layoutRoot(0) + , m_postLayoutTasksTimer(view, &FrameView::postLayoutTimerFired) , m_mediaType("screen") , m_enqueueEvents(0) , m_overflowStatusDirty(true) , m_viewportRenderer(0) , m_wasScrolledByUser(false) , m_inProgrammaticScroll(false) + , m_shouldUpdateWhileOffscreen(true) { - isTransparent = false; - baseBackgroundColor = Color::white; - vmode = hmode = ScrollbarAuto; - needToInitScrollbars = true; + m_isTransparent = false; + m_baseBackgroundColor = Color::white; + m_vmode = m_hmode = ScrollbarAuto; + m_needToInitScrollbars = true; reset(); } void reset() { - useSlowRepaints = false; - borderX = 30; - borderY = 30; - layoutTimer.stop(); - layoutRoot = 0; - delayedLayout = false; - doFullRepaint = true; - layoutSchedulingEnabled = true; - midLayout = false; - layoutCount = 0; - nestedLayoutCount = 0; - postLayoutTasksTimer.stop(); - firstLayout = true; - repaintRects.clear(); + m_useSlowRepaints = false; + m_borderX = 30; + m_borderY = 30; + m_layoutTimer.stop(); + m_layoutRoot = 0; + m_delayedLayout = false; + m_doFullRepaint = true; + m_layoutSchedulingEnabled = true; + m_midLayout = false; + m_layoutCount = 0; + m_nestedLayoutCount = 0; + m_postLayoutTasksTimer.stop(); + m_firstLayout = true; + m_firstLayoutCallbackPending = false; m_wasScrolledByUser = false; - lastLayoutSize = IntSize(); + m_lastLayoutSize = IntSize(); + m_lastZoomFactor = 1.0f; + m_deferringRepaints = 0; + m_repaintCount = 0; + m_repaintRect = IntRect(); + m_repaintRects.clear(); + m_paintRestriction = PaintRestrictionNone; + m_isPainting = false; } - bool doFullRepaint; + bool m_doFullRepaint; - ScrollbarMode vmode; - ScrollbarMode hmode; - bool useSlowRepaints; + ScrollbarMode m_vmode; + ScrollbarMode m_hmode; + bool m_useSlowRepaints; unsigned m_slowRepaintObjectCount; - int borderX, borderY; + int m_borderX, m_borderY; - Timer<FrameView> layoutTimer; - bool delayedLayout; - RenderObject* layoutRoot; - - bool layoutSchedulingEnabled; - bool midLayout; - int layoutCount; - unsigned nestedLayoutCount; - Timer<FrameView> postLayoutTasksTimer; - - bool firstLayout; - bool needToInitScrollbars; - bool isTransparent; - Color baseBackgroundColor; - IntSize lastLayoutSize; - - // Used by objects during layout to communicate repaints that need to take place only - // after all layout has been completed. - Vector<RenderObject::RepaintInfo> repaintRects; + Timer<FrameView> m_layoutTimer; + bool m_delayedLayout; + RenderObject* m_layoutRoot; + bool m_layoutSchedulingEnabled; + bool m_midLayout; + int m_layoutCount; + unsigned m_nestedLayoutCount; + Timer<FrameView> m_postLayoutTasksTimer; + bool m_firstLayoutCallbackPending; + + bool m_firstLayout; + bool m_needToInitScrollbars; + bool m_isTransparent; + Color m_baseBackgroundColor; + IntSize m_lastLayoutSize; + float m_lastZoomFactor; + String m_mediaType; unsigned m_enqueueEvents; Vector<ScheduledEvent*> m_scheduledEvents; bool m_overflowStatusDirty; - bool horizontalOverflow; + bool m_horizontalOverflow; bool m_verticalOverflow; RenderObject* m_viewportRenderer; bool m_wasScrolledByUser; bool m_inProgrammaticScroll; + + unsigned m_deferringRepaints; + unsigned m_repaintCount; + IntRect m_repaintRect; + Vector<IntRect> m_repaintRects; + + bool m_shouldUpdateWhileOffscreen; + + RefPtr<Node> m_nodeToDraw; + PaintRestriction m_paintRestriction; + bool m_isPainting; }; FrameView::FrameView(Frame* frame) @@ -151,34 +177,34 @@ FrameView::FrameView(Frame* frame) show(); } -#if !PLATFORM(MAC) FrameView::FrameView(Frame* frame, const IntSize& initialSize) : m_refCount(1) , m_frame(frame) , d(new FrameViewPrivate(this)) { init(); - Widget::setFrameGeometry(IntRect(x(), y(), initialSize.width(), initialSize.height())); + Widget::setFrameRect(IntRect(x(), y(), initialSize.width(), initialSize.height())); show(); } -#endif FrameView::~FrameView() { - if (d->postLayoutTasksTimer.isActive()) { - d->postLayoutTasksTimer.stop(); + if (d->m_postLayoutTasksTimer.isActive()) { + d->m_postLayoutTasksTimer.stop(); d->m_scheduledEvents.clear(); d->m_enqueueEvents = 0; } resetScrollbars(); - + setHasHorizontalScrollbar(false); // Remove native scrollbars now before we lose the connection to the HostWindow. + setHasVerticalScrollbar(false); + ASSERT(m_refCount == 0); ASSERT(d->m_scheduledEvents.isEmpty()); ASSERT(!d->m_enqueueEvents); if (m_frame) { - ASSERT(m_frame->view() != this || !m_frame->document() || !m_frame->document()->renderer()); + ASSERT(m_frame->view() != this || !m_frame->document() || !m_frame->contentRenderer()); RenderPart* renderer = m_frame->ownerRenderer(); if (renderer && renderer->widget() == this) renderer->setWidget(0); @@ -201,22 +227,35 @@ void FrameView::clearFrame() void FrameView::resetScrollbars() { // Reset the document's scrollbars back to our defaults before we yield the floor. - d->firstLayout = true; - suppressScrollbars(true); - ScrollView::setVScrollbarMode(d->vmode); - ScrollView::setHScrollbarMode(d->hmode); - suppressScrollbars(false); + d->m_firstLayout = true; + setScrollbarsSuppressed(true); + setScrollbarModes(d->m_hmode, d->m_vmode); + setScrollbarsSuppressed(false); } void FrameView::init() { m_margins = IntSize(-1, -1); // undefined m_size = IntSize(); + + // Propagate the marginwidth/height and scrolling modes to the view. + Element* ownerElement = m_frame && m_frame->document() ? m_frame->document()->ownerElement() : 0; + if (ownerElement && (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))) { + HTMLFrameElement* frameElt = static_cast<HTMLFrameElement*>(ownerElement); + if (frameElt->scrollingMode() == ScrollbarAlwaysOff) + setCanHaveScrollbars(false); + int marginWidth = frameElt->getMarginWidth(); + int marginHeight = frameElt->getMarginHeight(); + if (marginWidth != -1) + setMarginWidth(marginWidth); + if (marginHeight != -1) + setMarginHeight(marginHeight); + } } void FrameView::clear() { - setStaticBackground(false); + setCanBlitOnScroll(true); d->reset(); @@ -224,20 +263,43 @@ void FrameView::clear() if (RenderPart* renderer = m_frame->ownerRenderer()) renderer->viewCleared(); - suppressScrollbars(true); + setScrollbarsSuppressed(true); } bool FrameView::didFirstLayout() const { - return !d->firstLayout; + return !d->m_firstLayout; } void FrameView::initScrollbars() { - if (!d->needToInitScrollbars) + if (!d->m_needToInitScrollbars) return; - d->needToInitScrollbars = false; - setScrollbarsMode(hScrollbarMode()); + d->m_needToInitScrollbars = false; + d->m_hmode = horizontalScrollbarMode(); + d->m_vmode = verticalScrollbarMode(); + setScrollbarModes(d->m_hmode, d->m_vmode); +} + +void FrameView::invalidateRect(const IntRect& rect) +{ + if (!parent()) { + if (hostWindow()) + hostWindow()->repaint(rect, true); + return; + } + + if (!m_frame) + return; + + RenderPart* renderer = m_frame->ownerRenderer(); + if (!renderer) + return; + + IntRect repaintRect = rect; + repaintRect.move(renderer->borderLeft() + renderer->paddingLeft(), + renderer->borderTop() + renderer->paddingTop()); + renderer->repaintRectangle(repaintRect); } void FrameView::setMarginWidth(int w) @@ -252,13 +314,19 @@ void FrameView::setMarginHeight(int h) m_margins.setHeight(h); } +void FrameView::setCanHaveScrollbars(bool canScroll) +{ + ScrollView::setCanHaveScrollbars(canScroll); + scrollbarModes(d->m_hmode, d->m_vmode); +} + void FrameView::adjustViewSize() { ASSERT(m_frame->view() == this); - RenderView* root = static_cast<RenderView*>(m_frame->renderer()); + RenderView* root = m_frame->contentRenderer(); if (!root) return; - resizeContents(root->overflowWidth(), root->overflowHeight()); + setContentsSize(IntSize(root->overflowWidth(), root->overflowHeight())); } void FrameView::applyOverflowToViewport(RenderObject* o, ScrollbarMode& hMode, ScrollbarMode& vMode) @@ -302,22 +370,17 @@ void FrameView::applyOverflowToViewport(RenderObject* o, ScrollbarMode& hMode, S int FrameView::layoutCount() const { - return d->layoutCount; + return d->m_layoutCount; } bool FrameView::needsFullRepaint() const { - return d->doFullRepaint; -} - -void FrameView::addRepaintInfo(RenderObject* o, const IntRect& r) -{ - d->repaintRects.append(RenderObject::RepaintInfo(o, r)); + return d->m_doFullRepaint; } RenderObject* FrameView::layoutRoot(bool onlyDuringLayout) const { - return onlyDuringLayout && layoutPending() ? 0 : d->layoutRoot; + return onlyDuringLayout && layoutPending() ? 0 : d->m_layoutRoot; } #ifdef ANDROID_INSTRUMENT @@ -332,18 +395,18 @@ void Frame::resetLayoutTimeCounter() void Frame::reportLayoutTimeCounter() { - LOG(LOG_DEBUG, "WebCore", "*-* Total layout time: %d ms called %d times\n", + LOGD("*-* Total layout time: %d ms called %d times\n", sTotalTimeUsed, sCounter); } #endif void FrameView::layout(bool allowSubtree) { - if (d->midLayout) + if (d->m_midLayout) return; - d->layoutTimer.stop(); - d->delayedLayout = false; + d->m_layoutTimer.stop(); + d->m_delayedLayout = false; // Protect the view from being deleted during layout (in recalcStyle) RefPtr<FrameView> protector(this); @@ -356,13 +419,13 @@ void FrameView::layout(bool allowSubtree) } // we shouldn't enter layout() while painting - ASSERT(!m_frame->isPainting()); - if (m_frame->isPainting()) + ASSERT(!isPainting()); + if (isPainting()) return; - if (!allowSubtree && d->layoutRoot) { - d->layoutRoot->markContainingBlocksForLayout(false); - d->layoutRoot = 0; + if (!allowSubtree && d->m_layoutRoot) { + d->m_layoutRoot->markContainingBlocksForLayout(false); + d->m_layoutRoot = 0; } ASSERT(m_frame->view() == this); @@ -379,15 +442,20 @@ void FrameView::layout(bool allowSubtree) return; } - d->layoutSchedulingEnabled = false; + d->m_layoutSchedulingEnabled = false; - if (!d->nestedLayoutCount && d->postLayoutTasksTimer.isActive()) { + if (!d->m_nestedLayoutCount && d->m_postLayoutTasksTimer.isActive()) { // This is a new top-level layout. If there are any remaining tasks from the previous // layout, finish them now. - d->postLayoutTasksTimer.stop(); + d->m_postLayoutTasksTimer.stop(); performPostLayoutTasks(); } + // Viewport-dependent media queries may cause us to need completely different style information. + // Check that here. + if (document->styleSelector()->affectedByViewportChange()) + document->updateStyleSelector(); + // Always ensure our style info is up-to-date. This can happen in situations where // the layout beats any sort of style recalc update that needs to occur. if (m_frame->needsReapplyStyles()) @@ -395,17 +463,17 @@ void FrameView::layout(bool allowSubtree) else if (document->hasChangedChild()) document->recalcStyle(); - bool subtree = d->layoutRoot; + bool subtree = d->m_layoutRoot; // If there is only one ref to this view left, then its going to be destroyed as soon as we exit, // so there's no point to continuing to layout if (protector->hasOneRef()) return; - RenderObject* root = subtree ? d->layoutRoot : document->renderer(); + RenderObject* root = subtree ? d->m_layoutRoot : document->renderer(); if (!root) { // FIXME: Do we need to set m_size here? - d->layoutSchedulingEnabled = true; + d->m_layoutSchedulingEnabled = true; return; } @@ -415,71 +483,60 @@ void FrameView::layout(bool allowSubtree) startTime = get_thread_msec(); #endif - d->nestedLayoutCount++; + d->m_nestedLayoutCount++; - ScrollbarMode hMode = d->hmode; - ScrollbarMode vMode = d->vmode; + ScrollbarMode hMode = d->m_hmode; + ScrollbarMode vMode = d->m_vmode; if (!subtree) { RenderObject* rootRenderer = document->documentElement() ? document->documentElement()->renderer() : 0; - if (document->isHTMLDocument()) { - Node* body = static_cast<HTMLDocument*>(document)->body(); - if (body && body->renderer()) { - if (body->hasTagName(framesetTag)) { + Node* body = document->body(); + if (body && body->renderer()) { + if (body->hasTagName(framesetTag)) { + body->renderer()->setChildNeedsLayout(true); + vMode = ScrollbarAlwaysOff; + hMode = ScrollbarAlwaysOff; + } else if (body->hasTagName(bodyTag)) { + if (!d->m_firstLayout && m_size.height() != visibleHeight() + && static_cast<RenderBox*>(body->renderer())->stretchesToViewHeight()) body->renderer()->setChildNeedsLayout(true); - vMode = ScrollbarAlwaysOff; - hMode = ScrollbarAlwaysOff; - } else if (body->hasTagName(bodyTag)) { - if (!d->firstLayout && m_size.height() != visibleHeight() - && static_cast<RenderBox*>(body->renderer())->stretchesToViewHeight()) - body->renderer()->setChildNeedsLayout(true); - // It's sufficient to just check the X overflow, - // since it's illegal to have visible in only one direction. - RenderObject* o = rootRenderer->style()->overflowX() == OVISIBLE - ? body->renderer() : rootRenderer; - applyOverflowToViewport(o, hMode, vMode); // Only applies to HTML UAs, not to XML/XHTML UAs - } + // It's sufficient to just check the X overflow, + // since it's illegal to have visible in only one direction. + RenderObject* o = rootRenderer->style()->overflowX() == OVISIBLE && document->documentElement()->hasTagName(htmlTag) ? body->renderer() : rootRenderer; + applyOverflowToViewport(o, hMode, vMode); } } else if (rootRenderer) - applyOverflowToViewport(rootRenderer, hMode, vMode); // XML/XHTML UAs use the root element. + applyOverflowToViewport(rootRenderer, hMode, vMode); #ifdef INSTRUMENT_LAYOUT_SCHEDULING - if (d->firstLayout && !document->ownerElement()) + if (d->m_firstLayout && !document->ownerElement()) printf("Elapsed time before first layout: %d\n", document->elapsedTime()); #endif } - d->doFullRepaint = !subtree && (d->firstLayout || static_cast<RenderView*>(root)->printing()); - ASSERT(d->nestedLayoutCount > 1 || d->repaintRects.isEmpty()); + d->m_doFullRepaint = !subtree && (d->m_firstLayout || static_cast<RenderView*>(root)->printing()); - bool didFirstLayout = false; if (!subtree) { // Now set our scrollbar state for the layout. - ScrollbarMode currentHMode = hScrollbarMode(); - ScrollbarMode currentVMode = vScrollbarMode(); - - if (d->firstLayout || (hMode != currentHMode || vMode != currentVMode)) { - suppressScrollbars(true); - if (d->firstLayout) { - d->firstLayout = false; - didFirstLayout = true; - d->lastLayoutSize = IntSize(width(), height()); - + ScrollbarMode currentHMode = horizontalScrollbarMode(); + ScrollbarMode currentVMode = verticalScrollbarMode(); + + if (d->m_firstLayout || (hMode != currentHMode || vMode != currentVMode)) { + setScrollbarsSuppressed(true); + if (d->m_firstLayout) { + d->m_firstLayout = false; + d->m_firstLayoutCallbackPending = true; + d->m_lastLayoutSize = IntSize(width(), height()); + d->m_lastZoomFactor = root->style()->zoom(); + // Set the initial vMode to AlwaysOn if we're auto. if (vMode == ScrollbarAuto) - ScrollView::setVScrollbarMode(ScrollbarAlwaysOn); // This causes a vertical scrollbar to appear. + setVerticalScrollbarMode(ScrollbarAlwaysOn); // This causes a vertical scrollbar to appear. // Set the initial hMode to AlwaysOff if we're auto. if (hMode == ScrollbarAuto) - ScrollView::setHScrollbarMode(ScrollbarAlwaysOff); // This causes a horizontal scrollbar to disappear. - } - - if (hMode == vMode) - ScrollView::setScrollbarsMode(hMode); - else { - ScrollView::setHScrollbarMode(hMode); - ScrollView::setVScrollbarMode(vMode); + setHorizontalScrollbarMode(ScrollbarAlwaysOff); // This causes a horizontal scrollbar to disappear. } - - suppressScrollbars(false, true); + setScrollbarModes(hMode, vMode); + setScrollbarsSuppressed(false, true); } IntSize oldSize = m_size; @@ -487,7 +544,7 @@ void FrameView::layout(bool allowSubtree) m_size = IntSize(visibleWidth(), visibleHeight()); if (oldSize != m_size) - d->doFullRepaint = true; + d->m_doFullRepaint = true; } RenderLayer* layer = root->enclosingLayer(); @@ -496,41 +553,39 @@ void FrameView::layout(bool allowSubtree) if (subtree) root->view()->pushLayoutState(root); - d->midLayout = true; + + d->m_midLayout = true; + beginDeferredRepaints(); root->layout(); - d->midLayout = false; + endDeferredRepaints(); + d->m_midLayout = false; + if (subtree) root->view()->popLayoutState(); - d->layoutRoot = 0; + d->m_layoutRoot = 0; m_frame->invalidateSelection(); - d->layoutSchedulingEnabled = true; + d->m_layoutSchedulingEnabled = true; if (!subtree && !static_cast<RenderView*>(root)->printing()) adjustViewSize(); // Now update the positions of all layers. - layer->updateLayerPositions(d->doFullRepaint); - - // FIXME: Could optimize this and have objects removed from this list - // if they ever do full repaints. - Vector<RenderObject::RepaintInfo>::iterator end = d->repaintRects.end(); - for (Vector<RenderObject::RepaintInfo>::iterator it = d->repaintRects.begin(); it != end; ++it) - it->m_object->repaintRectangle(it->m_repaintRect); - d->repaintRects.clear(); + beginDeferredRepaints(); + layer->updateLayerPositions(d->m_doFullRepaint); + endDeferredRepaints(); - d->layoutCount++; + d->m_layoutCount++; #if PLATFORM(MAC) if (AXObjectCache::accessibilityEnabled()) root->document()->axObjectCache()->postNotificationToElement(root, "AXLayoutComplete"); #endif +#if ENABLE(DASHBOARD_SUPPORT) updateDashboardRegions(); +#endif - if (didFirstLayout) - m_frame->loader()->didFirstLayout(); - #ifdef ANDROID_INSTRUMENT if (!m_frame->tree()->parent()) { uint32_t time = get_thread_msec() - startTime; @@ -542,13 +597,13 @@ void FrameView::layout(bool allowSubtree) #endif ASSERT(!root->needsLayout()); - setStaticBackground(useSlowRepaints()); + setCanBlitOnScroll(!useSlowRepaints()); if (document->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) updateOverflowStatus(visibleWidth() < contentsWidth(), visibleHeight() < contentsHeight()); - if (!d->postLayoutTasksTimer.isActive()) { + if (!d->m_postLayoutTasksTimer.isActive()) { // Calls resumeScheduledEvents() performPostLayoutTasks(); @@ -556,7 +611,7 @@ void FrameView::layout(bool allowSubtree) // Post-layout widget updates or an event handler made us need layout again. // Lay out again, but this time defer widget updates and event dispatch until after // we return. - d->postLayoutTasksTimer.startOneShot(0); + d->m_postLayoutTasksTimer.startOneShot(0); pauseScheduledEvents(); layout(); } @@ -565,7 +620,7 @@ void FrameView::layout(bool allowSubtree) ASSERT(d->m_enqueueEvents); } - d->nestedLayoutCount--; + d->m_nestedLayoutCount--; } void FrameView::addWidgetToUpdate(RenderPartObject* object) @@ -584,76 +639,6 @@ void FrameView::removeWidgetToUpdate(RenderPartObject* object) m_widgetUpdateSet->remove(object); } -// -// Event Handling -// -///////////////// - -bool FrameView::scrollTo(const IntRect& bounds) -{ - int x, y, xe, ye; - x = bounds.x(); - y = bounds.y(); - xe = bounds.right() - 1; - ye = bounds.bottom() - 1; - - int deltax; - int deltay; - - int curHeight = visibleHeight(); - int curWidth = visibleWidth(); - - if (ye - y>curHeight-d->borderY) - ye = y + curHeight - d->borderY; - - if (xe - x>curWidth-d->borderX) - xe = x + curWidth - d->borderX; - - // is xpos of target left of the view's border? - if (x < contentsX() + d->borderX) - deltax = x - contentsX() - d->borderX; - // is xpos of target right of the view's right border? - else if (xe + d->borderX > contentsX() + curWidth) - deltax = xe + d->borderX - (contentsX() + curWidth); - else - deltax = 0; - - // is ypos of target above upper border? - if (y < contentsY() + d->borderY) - deltay = y - contentsY() - d->borderY; - // is ypos of target below lower border? - else if (ye + d->borderY > contentsY() + curHeight) - deltay = ye + d->borderY - (contentsY() + curHeight); - else - deltay = 0; - - int maxx = curWidth - d->borderX; - int maxy = curHeight - d->borderY; - - int scrollX = deltax > 0 ? (deltax > maxx ? maxx : deltax) : deltax == 0 ? 0 : (deltax > -maxx ? deltax : -maxx); - int scrollY = deltay > 0 ? (deltay > maxy ? maxy : deltay) : deltay == 0 ? 0 : (deltay > -maxy ? deltay : -maxy); - - if (contentsX() + scrollX < 0) - scrollX = -contentsX(); - else if (contentsWidth() - visibleWidth() - contentsX() < scrollX) - scrollX = contentsWidth() - visibleWidth() - contentsX(); - - if (contentsY() + scrollY < 0) - scrollY = -contentsY(); - else if (contentsHeight() - visibleHeight() - contentsY() < scrollY) - scrollY = contentsHeight() - visibleHeight() - contentsY(); - - scrollBy(scrollX, scrollY); - - // generate abs(scroll.) - if (scrollX < 0) - scrollX = -scrollX; - if (scrollY < 0) - scrollY = -scrollY; - - return scrollX != maxx && scrollY != maxy; -} - void FrameView::setMediaType(const String& mediaType) { d->m_mediaType = mediaType; @@ -670,19 +655,19 @@ String FrameView::mediaType() const bool FrameView::useSlowRepaints() const { - return d->useSlowRepaints || d->m_slowRepaintObjectCount > 0; + return d->m_useSlowRepaints || d->m_slowRepaintObjectCount > 0; } void FrameView::setUseSlowRepaints() { - d->useSlowRepaints = true; - setStaticBackground(true); + d->m_useSlowRepaints = true; + setCanBlitOnScroll(false); } void FrameView::addSlowRepaintObject() { if (!d->m_slowRepaintObjectCount) - setStaticBackground(true); + setCanBlitOnScroll(false); d->m_slowRepaintObjectCount++; } @@ -691,57 +676,104 @@ void FrameView::removeSlowRepaintObject() ASSERT(d->m_slowRepaintObjectCount > 0); d->m_slowRepaintObjectCount--; if (!d->m_slowRepaintObjectCount) - setStaticBackground(d->useSlowRepaints); + setCanBlitOnScroll(!d->m_useSlowRepaints); } -void FrameView::setScrollbarsMode(ScrollbarMode mode) +void FrameView::restoreScrollbar() { - d->vmode = mode; - d->hmode = mode; - - ScrollView::setScrollbarsMode(mode); + setScrollbarsSuppressed(false); } -void FrameView::setVScrollbarMode(ScrollbarMode mode) +void FrameView::scrollRectIntoViewRecursively(const IntRect& r) { - d->vmode = mode; - ScrollView::setVScrollbarMode(mode); + bool wasInProgrammaticScroll = d->m_inProgrammaticScroll; + d->m_inProgrammaticScroll = true; + ScrollView::scrollRectIntoViewRecursively(r); + d->m_inProgrammaticScroll = wasInProgrammaticScroll; } -void FrameView::setHScrollbarMode(ScrollbarMode mode) +void FrameView::setScrollPosition(const IntPoint& scrollPoint) { - d->hmode = mode; - ScrollView::setHScrollbarMode(mode); + bool wasInProgrammaticScroll = d->m_inProgrammaticScroll; + d->m_inProgrammaticScroll = true; + ScrollView::setScrollPosition(scrollPoint); + d->m_inProgrammaticScroll = wasInProgrammaticScroll; } -void FrameView::restoreScrollbar() +HostWindow* FrameView::hostWindow() const { - suppressScrollbars(false); + Page* page = frame() ? frame()->page() : 0; + if (!page) + return 0; + return page->chrome(); } -void FrameView::scrollRectIntoViewRecursively(const IntRect& r) +const unsigned cRepaintRectUnionThreshold = 25; + +void FrameView::repaintContentRectangle(const IntRect& r, bool immediate) { - if (frame()->prohibitsScrolling()) + ASSERT(!m_frame->document()->ownerElement()); + + if (d->m_deferringRepaints && !immediate) { + IntRect visibleContent = visibleContentRect(); + visibleContent.intersect(r); + if (!visibleContent.isEmpty()) { + d->m_repaintCount++; + d->m_repaintRect.unite(r); + if (d->m_repaintCount == cRepaintRectUnionThreshold) + d->m_repaintRects.clear(); + else if (d->m_repaintCount < cRepaintRectUnionThreshold) + d->m_repaintRects.append(r); + } +#ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS + else + ScrollView::platformOffscreenContentRectangle(r); +#endif return; - bool wasInProgrammaticScroll = d->m_inProgrammaticScroll; - d->m_inProgrammaticScroll = true; - ScrollView::scrollRectIntoViewRecursively(r); - d->m_inProgrammaticScroll = wasInProgrammaticScroll; + } + + if (!immediate && isOffscreen() && !shouldUpdateWhileOffscreen()) + return; + + ScrollView::repaintContentRectangle(r, immediate); } -void FrameView::setContentsPos(int x, int y) +void FrameView::beginDeferredRepaints() { - if (frame()->prohibitsScrolling()) - return; - bool wasInProgrammaticScroll = d->m_inProgrammaticScroll; - d->m_inProgrammaticScroll = true; - ScrollView::setContentsPos(x, y); - d->m_inProgrammaticScroll = wasInProgrammaticScroll; + Page* page = m_frame->page(); + if (page->mainFrame() != m_frame) + return page->mainFrame()->view()->beginDeferredRepaints(); + + d->m_deferringRepaints++; +#ifdef ANDROID_FIX // This allows sub frames to accumulate deferred repaints + if (d->m_deferringRepaints == 1) { +#endif + d->m_repaintCount = 0; + d->m_repaintRect = IntRect(); + d->m_repaintRects.clear(); +#ifdef ANDROID_FIX + } +#endif } -void FrameView::repaintRectangle(const IntRect& r, bool immediate) + +void FrameView::endDeferredRepaints() { - updateContents(r, immediate); + Page* page = m_frame->page(); + if (page->mainFrame() != m_frame) + return page->mainFrame()->view()->endDeferredRepaints(); + + ASSERT(d->m_deferringRepaints > 0); + if (--d->m_deferringRepaints == 0) { + if (d->m_repaintCount >= cRepaintRectUnionThreshold) + repaintContentRectangle(d->m_repaintRect, false); + else { + unsigned size = d->m_repaintRects.size(); + for (unsigned i = 0; i < size; i++) + repaintContentRectangle(d->m_repaintRects[i], false); + d->m_repaintRects.clear(); + } + } } void FrameView::layoutTimerFired(Timer<FrameView>*) @@ -758,39 +790,39 @@ void FrameView::scheduleRelayout() ASSERT(!m_frame->document() || !m_frame->document()->inPageCache()); ASSERT(m_frame->view() == this); - if (d->layoutRoot) { - d->layoutRoot->markContainingBlocksForLayout(false); - d->layoutRoot = 0; + if (d->m_layoutRoot) { + d->m_layoutRoot->markContainingBlocksForLayout(false); + d->m_layoutRoot = 0; } - if (!d->layoutSchedulingEnabled) + if (!d->m_layoutSchedulingEnabled) return; if (!m_frame->document() || !m_frame->document()->shouldScheduleLayout()) return; +#if defined(FLATTEN_IFRAME) || defined(FLATTEN_FRAMESET) + if (m_frame->ownerRenderer()) + m_frame->ownerRenderer()->setNeedsLayoutAndPrefWidthsRecalc(); +#endif + #ifdef ANDROID_MOBILE int delay = m_frame->document()->minimumLayoutDelay() + m_frame->document()->extraLayoutDelay(); #else int delay = m_frame->document()->minimumLayoutDelay(); #endif - if (d->layoutTimer.isActive() && d->delayedLayout && !delay) + if (d->m_layoutTimer.isActive() && d->m_delayedLayout && !delay) unscheduleRelayout(); - if (d->layoutTimer.isActive()) + if (d->m_layoutTimer.isActive()) return; - d->delayedLayout = delay != 0; + d->m_delayedLayout = delay != 0; #ifdef INSTRUMENT_LAYOUT_SCHEDULING if (!m_frame->document()->ownerElement()) printf("Scheduling layout for %d\n", delay); #endif -#if defined(FLATTEN_IFRAME) || defined(FLATTEN_FRAMESET) - if (m_frame->ownerRenderer()) - m_frame->ownerRenderer()->setNeedsLayoutAndPrefWidthsRecalc(); -#endif - - d->layoutTimer.startOneShot(delay * 0.001); + d->m_layoutTimer.startOneShot(delay * 0.001); } static bool isObjectAncestorContainerOf(RenderObject* ancestor, RenderObject* descendant) @@ -802,33 +834,32 @@ static bool isObjectAncestorContainerOf(RenderObject* ancestor, RenderObject* de return false; } -void FrameView::scheduleRelayoutOfSubtree(RenderObject* o) +void FrameView::scheduleRelayoutOfSubtree(RenderObject* relayoutRoot) { ASSERT(m_frame->view() == this); - if (!d->layoutSchedulingEnabled || (m_frame->document() - && m_frame->document()->renderer() - && m_frame->document()->renderer()->needsLayout())) { - if (o) - o->markContainingBlocksForLayout(false); + if (!d->m_layoutSchedulingEnabled || (m_frame->contentRenderer() + && m_frame->contentRenderer()->needsLayout())) { + if (relayoutRoot) + relayoutRoot->markContainingBlocksForLayout(false); return; } if (layoutPending()) { - if (d->layoutRoot != o) { - if (isObjectAncestorContainerOf(d->layoutRoot, o)) { + if (d->m_layoutRoot != relayoutRoot) { + if (isObjectAncestorContainerOf(d->m_layoutRoot, relayoutRoot)) { // Keep the current root - o->markContainingBlocksForLayout(false, d->layoutRoot); - } else if (d->layoutRoot && isObjectAncestorContainerOf(o, d->layoutRoot)) { - // Re-root at o - d->layoutRoot->markContainingBlocksForLayout(false, o); - d->layoutRoot = o; + relayoutRoot->markContainingBlocksForLayout(false, d->m_layoutRoot); + } else if (d->m_layoutRoot && isObjectAncestorContainerOf(relayoutRoot, d->m_layoutRoot)) { + // Re-root at relayoutRoot + d->m_layoutRoot->markContainingBlocksForLayout(false, relayoutRoot); + d->m_layoutRoot = relayoutRoot; } else { // Just do a full relayout - if (d->layoutRoot) - d->layoutRoot->markContainingBlocksForLayout(false); - d->layoutRoot = 0; - o->markContainingBlocksForLayout(false); + if (d->m_layoutRoot) + d->m_layoutRoot->markContainingBlocksForLayout(false); + d->m_layoutRoot = 0; + relayoutRoot->markContainingBlocksForLayout(false); } } } else { @@ -837,15 +868,15 @@ void FrameView::scheduleRelayoutOfSubtree(RenderObject* o) #else int delay = m_frame->document()->minimumLayoutDelay(); #endif - d->layoutRoot = o; - d->delayedLayout = delay != 0; - d->layoutTimer.startOneShot(delay * 0.001); + d->m_layoutRoot = relayoutRoot; + d->m_delayedLayout = delay != 0; + d->m_layoutTimer.startOneShot(delay * 0.001); } } bool FrameView::layoutPending() const { - return d->layoutTimer.isActive(); + return d->m_layoutTimer.isActive(); } bool FrameView::needsLayout() const @@ -854,21 +885,22 @@ bool FrameView::needsLayout() const // then we are not allowed to schedule layouts yet, so we won't be pending layout. if (!m_frame) return false; - RenderView* root = static_cast<RenderView*>(m_frame->renderer()); + RenderView* root = m_frame->contentRenderer(); Document * doc = m_frame->document(); // doc->hasChangedChild() condition can occur when using WebKit ObjC interface - return layoutPending() || (root && root->needsLayout()) || d->layoutRoot || (doc && doc->hasChangedChild()) || m_frame->needsReapplyStyles(); + return layoutPending() || (root && root->needsLayout()) || d->m_layoutRoot || (doc && doc->hasChangedChild()) || m_frame->needsReapplyStyles(); } void FrameView::setNeedsLayout() { - if (m_frame->renderer()) - m_frame->renderer()->setNeedsLayout(true); + RenderView* root = m_frame->contentRenderer(); + if (root) + root->setNeedsLayout(true); } void FrameView::unscheduleRelayout() { - if (!d->layoutTimer.isActive()) + if (!d->m_layoutTimer.isActive()) return; #ifdef INSTRUMENT_LAYOUT_SCHEDULING @@ -876,44 +908,53 @@ void FrameView::unscheduleRelayout() printf("Layout timer unscheduled at %d\n", m_frame->document()->elapsedTime()); #endif - d->layoutTimer.stop(); - d->delayedLayout = false; + d->m_layoutTimer.stop(); + d->m_delayedLayout = false; } bool FrameView::isTransparent() const { - return d->isTransparent; + return d->m_isTransparent; } void FrameView::setTransparent(bool isTransparent) { - d->isTransparent = isTransparent; + d->m_isTransparent = isTransparent; } Color FrameView::baseBackgroundColor() const { - return d->baseBackgroundColor; + return d->m_baseBackgroundColor; } void FrameView::setBaseBackgroundColor(Color bc) { if (!bc.isValid()) bc = Color::white; - d->baseBackgroundColor = bc; + d->m_baseBackgroundColor = bc; +} + +bool FrameView::shouldUpdateWhileOffscreen() const +{ + return d->m_shouldUpdateWhileOffscreen; } -void FrameView::scheduleEvent(PassRefPtr<Event> event, PassRefPtr<EventTargetNode> eventTarget, bool tempEvent) +void FrameView::setShouldUpdateWhileOffscreen(bool shouldUpdateWhileOffscreen) +{ + d->m_shouldUpdateWhileOffscreen = shouldUpdateWhileOffscreen; +} + +void FrameView::scheduleEvent(PassRefPtr<Event> event, PassRefPtr<EventTargetNode> eventTarget) { if (!d->m_enqueueEvents) { ExceptionCode ec = 0; - eventTarget->dispatchEvent(event, ec, tempEvent); + eventTarget->dispatchEvent(event, ec); return; } ScheduledEvent* scheduledEvent = new ScheduledEvent; scheduledEvent->m_event = event; scheduledEvent->m_eventTarget = eventTarget; - scheduledEvent->m_tempEvent = tempEvent; d->m_scheduledEvents.append(scheduledEvent); } @@ -933,10 +974,15 @@ void FrameView::resumeScheduledEvents() void FrameView::performPostLayoutTasks() { - RenderView* root = static_cast<RenderView*>(m_frame->document()->renderer()); + if (d->m_firstLayoutCallbackPending) { + d->m_firstLayoutCallbackPending = false; + m_frame->loader()->didFirstLayout(); + } + + RenderView* root = m_frame->contentRenderer(); root->updateWidgetPositions(); - if (m_widgetUpdateSet && d->nestedLayoutCount <= 1) { + if (m_widgetUpdateSet && d->m_nestedLayoutCount <= 1) { Vector<RenderPartObject*> objectVector; copyToVector(*m_widgetUpdateSet, objectVector); size_t size = objectVector.size(); @@ -956,8 +1002,10 @@ void FrameView::performPostLayoutTasks() if (!root->printing()) { IntSize currentSize = IntSize(width(), height()); - bool resized = !d->firstLayout && currentSize != d->lastLayoutSize; - d->lastLayoutSize = currentSize; + float currentZoomFactor = root->style()->zoom(); + bool resized = !d->m_firstLayout && (currentSize != d->m_lastLayoutSize || currentZoomFactor != d->m_lastZoomFactor); + d->m_lastLayoutSize = currentSize; + d->m_lastZoomFactor = currentZoomFactor; if (resized) m_frame->sendResizeEvent(); } @@ -974,22 +1022,22 @@ void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverf return; if (d->m_overflowStatusDirty) { - d->horizontalOverflow = horizontalOverflow; + d->m_horizontalOverflow = horizontalOverflow; d->m_verticalOverflow = verticalOverflow; d->m_overflowStatusDirty = false; return; } - bool horizontalOverflowChanged = (d->horizontalOverflow != horizontalOverflow); + bool horizontalOverflowChanged = (d->m_horizontalOverflow != horizontalOverflow); bool verticalOverflowChanged = (d->m_verticalOverflow != verticalOverflow); if (horizontalOverflowChanged || verticalOverflowChanged) { - d->horizontalOverflow = horizontalOverflow; + d->m_horizontalOverflow = horizontalOverflow; d->m_verticalOverflow = verticalOverflow; - scheduleEvent(new OverflowEvent(horizontalOverflowChanged, horizontalOverflow, + scheduleEvent(OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow), - EventTargetNodeCast(d->m_viewportRenderer->element()), true); + EventTargetNodeCast(d->m_viewportRenderer->element())); } } @@ -1010,30 +1058,18 @@ void FrameView::dispatchScheduledEvents() // Only dispatch events to nodes that are in the document if (scheduledEvent->m_eventTarget->inDocument()) - scheduledEvent->m_eventTarget->dispatchEvent(scheduledEvent->m_event, - ec, scheduledEvent->m_tempEvent); + scheduledEvent->m_eventTarget->dispatchEvent(scheduledEvent->m_event, ec); delete scheduledEvent; } } -IntRect FrameView::windowClipRect() const -{ - return windowClipRect(true); -} - IntRect FrameView::windowClipRect(bool clipToContents) const { ASSERT(m_frame->view() == this); // Set our clip rect to be our contents. - IntRect clipRect; - if (clipToContents) - clipRect = enclosingIntRect(visibleContentRect()); - else - clipRect = IntRect(contentsX(), contentsY(), width(), height()); - clipRect = contentsToWindow(clipRect); - + IntRect clipRect = contentsToWindow(visibleContentRect(!clipToContents)); if (!m_frame || !m_frame->document() || !m_frame->document()->ownerElement()) return clipRect; @@ -1064,16 +1100,54 @@ IntRect FrameView::windowClipRectForLayer(const RenderLayer* layer, bool clipToL return intersection(clipRect, windowClipRect()); } +bool FrameView::isActive() const +{ + Page* page = frame()->page(); + return page && page->focusController()->isActive(); +} + +void FrameView::valueChanged(Scrollbar* bar) +{ + // Figure out if we really moved. + IntSize offset = scrollOffset(); + ScrollView::valueChanged(bar); + if (offset != scrollOffset()) + frame()->sendScrollEvent(); +} + +void FrameView::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +{ + // Add in our offset within the FrameView. + IntRect dirtyRect = rect; + dirtyRect.move(scrollbar->x(), scrollbar->y()); + invalidateRect(dirtyRect); +} + +IntRect FrameView::windowResizerRect() const +{ + Page* page = frame() ? frame()->page() : 0; + if (!page) + return IntRect(); + return page->chrome()->windowResizerRect(); +} + +#if ENABLE(DASHBOARD_SUPPORT) void FrameView::updateDashboardRegions() { - Document* doc = m_frame->document(); - if (doc->hasDashboardRegions()) { - Vector<DashboardRegionValue> newRegions; - doc->renderer()->collectDashboardRegions(newRegions); - doc->setDashboardRegions(newRegions); - m_frame.get()->dashboardRegionsChanged(); - } + Document* document = m_frame->document(); + if (!document->hasDashboardRegions()) + return; + Vector<DashboardRegionValue> newRegions; + document->renderer()->collectDashboardRegions(newRegions); + if (newRegions == document->dashboardRegions()) + return; + document->setDashboardRegions(newRegions); + Page* page = m_frame->page(); + if (!page) + return; + page->chrome()->client()->dashboardRegionsChanged(); } +#endif void FrameView::updateControlTints() { @@ -1086,17 +1160,16 @@ void FrameView::updateControlTints() if (!m_frame || m_frame->loader()->url().isEmpty()) return; - if (theme()->supportsControlTints() && m_frame->renderer()) { + if (theme()->supportsControlTints() && m_frame->contentRenderer()) { if (needsLayout()) layout(); PlatformGraphicsContext* const noContext = 0; GraphicsContext context(noContext); context.setUpdatingControlTints(true); -#if !PLATFORM(MAC) - ScrollView::paint(&context, frameGeometry()); -#else - m_frame->paint(&context, enclosingIntRect(visibleContentRect())); -#endif + if (platformWidget()) + paintContents(&context, visibleContentRect()); + else + paint(&context, frameRect()); } } @@ -1112,7 +1185,82 @@ void FrameView::setWasScrolledByUser(bool wasScrolledByUser) d->m_wasScrolledByUser = wasScrolledByUser; } -#if PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(QT) +void FrameView::paintContents(GraphicsContext* p, const IntRect& rect) +{ + if (!frame()) + return; + + Document* document = frame()->document(); + if (!document) + return; + +#ifndef NDEBUG + bool fillWithRed; + if (document || document->printing()) + fillWithRed = false; // Printing, don't fill with red (can't remember why). + else if (document->ownerElement()) + fillWithRed = false; // Subframe, don't fill with red. + else if (isTransparent()) + fillWithRed = false; // Transparent, don't fill with red. + else if (d->m_paintRestriction == PaintRestrictionSelectionOnly || d->m_paintRestriction == PaintRestrictionSelectionOnlyBlackText) + fillWithRed = false; // Selections are transparent, don't fill with red. + else if (d->m_nodeToDraw) + fillWithRed = false; // Element images are transparent, don't fill with red. + else + fillWithRed = true; + + if (fillWithRed) + p->fillRect(rect, Color(0xFF, 0, 0)); +#endif + + bool isTopLevelPainter = !sCurrentPaintTimeStamp; + if (isTopLevelPainter) + sCurrentPaintTimeStamp = currentTime(); + + RenderView* contentRenderer = frame()->contentRenderer(); + if (!contentRenderer) { + LOG_ERROR("called Frame::paint with nil renderer"); + return; + } + + ASSERT(!needsLayout()); + ASSERT(!d->m_isPainting); + + d->m_isPainting = true; + + // m_nodeToDraw is used to draw only one element (and its descendants) + RenderObject* eltRenderer = d->m_nodeToDraw ? d->m_nodeToDraw->renderer() : 0; + if (d->m_paintRestriction == PaintRestrictionNone) + document->invalidateRenderedRectsForMarkersInRect(rect); + contentRenderer->layer()->paint(p, rect, d->m_paintRestriction, eltRenderer); + + d->m_isPainting = false; + +#if ENABLE(DASHBOARD_SUPPORT) + // Regions may have changed as a result of the visibility/z-index of element changing. + if (document->dashboardRegionsDirty()) + updateDashboardRegions(); +#endif + + if (isTopLevelPainter) + sCurrentPaintTimeStamp = 0; +} + +void FrameView::setPaintRestriction(PaintRestriction pr) +{ + d->m_paintRestriction = pr; +} + +bool FrameView::isPainting() const +{ + return d->m_isPainting; +} + +void FrameView::setNodeToDraw(Node* node) +{ + d->m_nodeToDraw = node; +} + void FrameView::layoutIfNeededRecursive() { // We have to crawl our entire tree looking for any FrameViews that need @@ -1127,12 +1275,11 @@ void FrameView::layoutIfNeededRecursive() if (needsLayout()) layout(); - HashSet<Widget*>* viewChildren = children(); - HashSet<Widget*>::iterator end = viewChildren->end(); - for (HashSet<Widget*>::iterator current = viewChildren->begin(); current != end; ++current) + const HashSet<Widget*>* viewChildren = children(); + HashSet<Widget*>::const_iterator end = viewChildren->end(); + for (HashSet<Widget*>::const_iterator current = viewChildren->begin(); current != end; ++current) if ((*current)->isFrameView()) static_cast<FrameView*>(*current)->layoutIfNeededRecursive(); } -#endif } diff --git a/WebCore/page/FrameView.h b/WebCore/page/FrameView.h index 7577b10..84a829f 100644 --- a/WebCore/page/FrameView.h +++ b/WebCore/page/FrameView.h @@ -25,8 +25,9 @@ #ifndef FrameView_h #define FrameView_h -#include "ScrollView.h" #include "IntSize.h" +#include "RenderLayer.h" +#include "ScrollView.h" #include <wtf/Forward.h> #include <wtf/OwnPtr.h> @@ -49,16 +50,17 @@ template <typename T> class Timer; class FrameView : public ScrollView { public: - FrameView(Frame*); + friend class RenderView; - // On the Mac, FrameViews always get their size from the underlying NSView, - // so passing in a size is nonsensical. -#if !PLATFORM(MAC) + FrameView(Frame*); FrameView(Frame*, const IntSize& initialSize); -#endif virtual ~FrameView(); + virtual HostWindow* hostWindow() const; + + virtual void invalidateRect(const IntRect&); + Frame* frame() const { return m_frame.get(); } void clearFrame(); @@ -71,9 +73,7 @@ public: void setMarginWidth(int); void setMarginHeight(int); - virtual void setVScrollbarMode(ScrollbarMode); - virtual void setHScrollbarMode(ScrollbarMode); - virtual void setScrollbarsMode(ScrollbarMode); + virtual void setCanHaveScrollbars(bool); void layout(bool allowSubtree = true); bool didFirstLayout() const; @@ -91,8 +91,6 @@ public: void setNeedsLayout(); bool needsFullRepaint() const; - void repaintRectangle(const IntRect&, bool immediate); - void addRepaintInfo(RenderObject*, const IntRect&); void resetScrollbars(); @@ -104,15 +102,23 @@ public: Color baseBackgroundColor() const; void setBaseBackgroundColor(Color); + bool shouldUpdateWhileOffscreen() const; + void setShouldUpdateWhileOffscreen(bool); + void adjustViewSize(); void initScrollbars(); - virtual IntRect windowClipRect() const; - IntRect windowClipRect(bool clipToContents) const; + virtual IntRect windowClipRect(bool clipToContents = true) const; IntRect windowClipRectForLayer(const RenderLayer*, bool clipToLayerContents) const; + virtual bool isActive() const; + virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); + virtual void valueChanged(Scrollbar*); + + virtual IntRect windowResizerRect() const; + virtual void scrollRectIntoViewRecursively(const IntRect&); - virtual void setContentsPos(int x, int y); + virtual void setScrollPosition(const IntPoint&); String mediaType() const; void setMediaType(const String&); @@ -122,12 +128,17 @@ public: void addSlowRepaintObject(); void removeSlowRepaintObject(); + void beginDeferredRepaints(); + void endDeferredRepaints(); + +#if ENABLE(DASHBOARD_SUPPORT) void updateDashboardRegions(); +#endif void updateControlTints(); void restoreScrollbar(); - void scheduleEvent(PassRefPtr<Event>, PassRefPtr<EventTargetNode>, bool tempEvent); + void scheduleEvent(PassRefPtr<Event>, PassRefPtr<EventTargetNode>); void pauseScheduledEvents(); void resumeScheduledEvents(); void postLayoutTimerFired(Timer<FrameView>*); @@ -138,19 +149,20 @@ public: void addWidgetToUpdate(RenderPartObject*); void removeWidgetToUpdate(RenderPartObject*); - // FIXME: This method should be used by all platforms, but currently depends on ScrollView::children, - // which not all methods have. Once FrameView and ScrollView are merged, this #if should be removed. -#if PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(QT) + virtual void paintContents(GraphicsContext*, const IntRect& damageRect); + void setPaintRestriction(PaintRestriction); + bool isPainting() const; + void setNodeToDraw(Node*); + + static double currentPaintTimeStamp() { return sCurrentPaintTimeStamp; } // returns 0 if not painting + void layoutIfNeededRecursive(); -#endif private: void init(); virtual bool isFrameView() const; - bool scrollTo(const IntRect&); - bool useSlowRepaints() const; void applyOverflowToViewport(RenderObject*, ScrollbarMode& hMode, ScrollbarMode& vMode); @@ -160,6 +172,12 @@ private: void dispatchScheduledEvents(); void performPostLayoutTasks(); + virtual void repaintContentRectangle(const IntRect&, bool immediate); + virtual void contentsResized() { setNeedsLayout(); } + virtual void visibleContentsResized() { layout(); } + + static double sCurrentPaintTimeStamp; // used for detecting decoded resource thrash in the cache + unsigned m_refCount; IntSize m_size; IntSize m_margins; diff --git a/WebCore/page/Geolocation.cpp b/WebCore/page/Geolocation.cpp new file mode 100644 index 0000000..a0c9694 --- /dev/null +++ b/WebCore/page/Geolocation.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "Geolocation.h" + +#include "Document.h" +#include "Frame.h" +#include "PositionError.h" + +namespace WebCore { + +Geolocation::GeoNotifier::GeoNotifier(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PositionOptions* options) + : m_successCallback(successCallback) + , m_errorCallback(errorCallback) + , m_timer(this, &Geolocation::GeoNotifier::timerFired) +{ + if (m_errorCallback && options) + m_timer.startOneShot(options->timeout() / 1000.0); +} + +void Geolocation::GeoNotifier::timerFired(Timer<GeoNotifier>*) +{ + ASSERT(m_errorCallback); + + m_timer.stop(); + + RefPtr<PositionError> error = PositionError::create(PositionError::TIMEOUT_ERROR, "Timed out"); + m_errorCallback->handleEvent(error.get()); +} + +Geolocation::Geolocation(Frame* frame) + : m_frame(frame) + , m_service(GeolocationService::create(this)) +{ + ASSERT(m_frame->document()); + m_frame->document()->setUsingGeolocation(true); +} + +void Geolocation::disconnectFrame() +{ + m_service->stopUpdating(); + if (m_frame->document()) + m_frame->document()->setUsingGeolocation(false); + m_frame = 0; +} + +void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PositionOptions* options) +{ + RefPtr<GeoNotifier> notifier = GeoNotifier::create(successCallback, errorCallback, options); + + if (!m_service->startUpdating(options)) { + if (notifier->m_errorCallback) { + RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_ERROR, "Unable to Start"); + notifier->m_errorCallback->handleEvent(error.get()); + } + return; + } + + m_oneShots.add(notifier); +} + +int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PositionOptions* options) +{ + RefPtr<GeoNotifier> notifier = GeoNotifier::create(successCallback, errorCallback, options); + + if (!m_service->startUpdating(options)) { + if (notifier->m_errorCallback) { + RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_ERROR, "Unable to Start"); + notifier->m_errorCallback->handleEvent(error.get()); + } + return 0; + } + + static int sIdentifier = 0; + + m_watchers.set(++sIdentifier, notifier); + + return sIdentifier; +} + +void Geolocation::clearWatch(int watchId) +{ + m_watchers.remove(watchId); + + if (!hasListeners()) + m_service->stopUpdating(); +} + +void Geolocation::suspend() +{ + if (hasListeners()) + m_service->suspend(); +} + +void Geolocation::resume() +{ + if (hasListeners()) + m_service->resume(); +} + +void Geolocation::sendErrorToOneShots(PositionError* error) +{ + Vector<RefPtr<GeoNotifier> > copy; + copyToVector(m_oneShots, copy); + + Vector<RefPtr<GeoNotifier> >::const_iterator end = copy.end(); + for (Vector<RefPtr<GeoNotifier> >::const_iterator it = copy.begin(); it != end; ++it) { + RefPtr<GeoNotifier> notifier = *it; + + if (notifier->m_errorCallback) + notifier->m_errorCallback->handleEvent(error); + } +} + +void Geolocation::sendErrorToWatchers(PositionError* error) +{ + Vector<RefPtr<GeoNotifier> > copy; + copyValuesToVector(m_watchers, copy); + + Vector<RefPtr<GeoNotifier> >::const_iterator end = copy.end(); + for (Vector<RefPtr<GeoNotifier> >::const_iterator it = copy.begin(); it != end; ++it) { + RefPtr<GeoNotifier> notifier = *it; + + if (notifier->m_errorCallback) + notifier->m_errorCallback->handleEvent(error); + } +} + +void Geolocation::sendPositionToOneShots(Geoposition* position) +{ + Vector<RefPtr<GeoNotifier> > copy; + copyToVector(m_oneShots, copy); + + Vector<RefPtr<GeoNotifier> >::const_iterator end = copy.end(); + for (Vector<RefPtr<GeoNotifier> >::const_iterator it = copy.begin(); it != end; ++it) { + RefPtr<GeoNotifier> notifier = *it; + ASSERT(notifier->m_successCallback); + + notifier->m_timer.stop(); + bool shouldCallErrorCallback = false; + notifier->m_successCallback->handleEvent(position, shouldCallErrorCallback); + if (shouldCallErrorCallback) { + RefPtr<PositionError> error = PositionError::create(PositionError::UNKNOWN_ERROR, "An exception was thrown"); + handleError(error.get()); + } + } +} + +void Geolocation::sendPositionToWatchers(Geoposition* position) +{ + Vector<RefPtr<GeoNotifier> > copy; + copyValuesToVector(m_watchers, copy); + + Vector<RefPtr<GeoNotifier> >::const_iterator end = copy.end(); + for (Vector<RefPtr<GeoNotifier> >::const_iterator it = copy.begin(); it != end; ++it) { + RefPtr<GeoNotifier> notifier = *it; + ASSERT(notifier->m_successCallback); + + notifier->m_timer.stop(); + bool shouldCallErrorCallback = false; + notifier->m_successCallback->handleEvent(position, shouldCallErrorCallback); + if (shouldCallErrorCallback) { + RefPtr<PositionError> error = PositionError::create(PositionError::UNKNOWN_ERROR, "An exception was thrown"); + handleError(error.get()); + } + } +} + +void Geolocation::handleError(PositionError* error) +{ + ASSERT(error); + + sendErrorToOneShots(error); + sendErrorToWatchers(error); + + m_oneShots.clear(); +} + +void Geolocation::geolocationServicePositionChanged(GeolocationService* service) +{ + ASSERT(service->lastPosition()); + + sendPositionToOneShots(service->lastPosition()); + sendPositionToWatchers(service->lastPosition()); + + m_oneShots.clear(); + + if (!hasListeners()) + m_service->stopUpdating(); +} + +void Geolocation::geolocationServiceErrorOccurred(GeolocationService* service) +{ + ASSERT(service->lastError()); + + handleError(service->lastError()); +} + +} // namespace WebCore diff --git a/WebCore/page/Geolocation.h b/WebCore/page/Geolocation.h new file mode 100644 index 0000000..572cbd8 --- /dev/null +++ b/WebCore/page/Geolocation.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 Geolocation_h +#define Geolocation_h + +#include "GeolocationService.h" +#include "PositionCallback.h" +#include "PositionErrorCallback.h" +#include "PositionOptions.h" +#include "Timer.h" +#include <wtf/Platform.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Frame; +class Geoposition; + +class Geolocation : public RefCounted<Geolocation>, public GeolocationServiceClient { +public: + static PassRefPtr<Geolocation> create(Frame* frame) { return adoptRef(new Geolocation(frame)); } + + virtual ~Geolocation() {} + + void disconnectFrame(); + + Geoposition* lastPosition() const { return m_service->lastPosition(); } + + void getCurrentPosition(PassRefPtr<PositionCallback>, PassRefPtr<PositionErrorCallback>, PositionOptions*); + int watchPosition(PassRefPtr<PositionCallback>, PassRefPtr<PositionErrorCallback>, PositionOptions*); + void clearWatch(int watchId); + + void suspend(); + void resume(); + +private: + Geolocation(Frame*); + + class GeoNotifier : public RefCounted<GeoNotifier> { + public: + static PassRefPtr<GeoNotifier> create(PassRefPtr<PositionCallback> positionCallback, PassRefPtr<PositionErrorCallback> positionErrorCallback, PositionOptions* options) { return adoptRef(new GeoNotifier(positionCallback, positionErrorCallback, options)); } + + void timerFired(Timer<GeoNotifier>*); + + RefPtr<PositionCallback> m_successCallback; + RefPtr<PositionErrorCallback> m_errorCallback; + Timer<GeoNotifier> m_timer; + + private: + GeoNotifier(PassRefPtr<PositionCallback>, PassRefPtr<PositionErrorCallback>, PositionOptions*); + }; + + bool hasListeners() const { return !m_oneShots.isEmpty() || !m_watchers.isEmpty(); } + + void sendErrorToOneShots(PositionError*); + void sendErrorToWatchers(PositionError*); + void sendPositionToOneShots(Geoposition*); + void sendPositionToWatchers(Geoposition*); + + void handleError(PositionError*); + + virtual void geolocationServicePositionChanged(GeolocationService*); + virtual void geolocationServiceErrorOccurred(GeolocationService*); + + typedef HashSet<RefPtr<GeoNotifier> > GeoNotifierSet; + typedef HashMap<int, RefPtr<GeoNotifier> > GeoNotifierMap; + + GeoNotifierSet m_oneShots; + GeoNotifierMap m_watchers; + Frame* m_frame; + OwnPtr<GeolocationService> m_service; +}; + +} // namespace WebCore + +#endif // Geolocation_h diff --git a/WebCore/page/Geolocation.idl b/WebCore/page/Geolocation.idl new file mode 100644 index 0000000..e125118 --- /dev/null +++ b/WebCore/page/Geolocation.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 Geolocation { + readonly attribute Geoposition lastPosition; + + [Custom] void getCurrentPosition(in PositionCallback successCallback, in PositionErrorCallback errorCallback, in PositionOptions options); + + [Custom] long watchPosition(in PositionCallback successCallback, in PositionErrorCallback errorCallback, in PositionOptions options); + + void clearWatch(in long watchId); + }; + +} diff --git a/WebCore/page/Geoposition.cpp b/WebCore/page/Geoposition.cpp new file mode 100644 index 0000000..1792a1f --- /dev/null +++ b/WebCore/page/Geoposition.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "Geoposition.h" + +namespace WebCore { + +String Geoposition::toString() const +{ + return String::format("position(%.6lg, %.6lg, %.6lg, %.6lg, %.6lg, %.6lg, %.6lg, %.lld)", + m_latitude, m_longitude, m_altitude, m_accuracy, + m_altitudeAccuracy, m_heading, m_speed, m_timestamp); +} + +} // namespace WebCore diff --git a/WebCore/page/Geoposition.h b/WebCore/page/Geoposition.h new file mode 100644 index 0000000..9ce50e5 --- /dev/null +++ b/WebCore/page/Geoposition.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 Geoposition_h +#define Geoposition_h + +#include "Event.h" +#include "PlatformString.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + +typedef int ExceptionCode; + +class Geoposition : public RefCounted<Geoposition> { +public: + static PassRefPtr<Geoposition> create(double latitude, double longitude, double altitude, double accuracy, double altitudeAccuracy, double heading, double speed, DOMTimeStamp timestamp) { return adoptRef(new Geoposition(latitude, longitude, altitude, accuracy, altitudeAccuracy, heading, speed, timestamp)); } + + double latitude() const { return m_latitude; } + double longitude() const { return m_longitude; } + double altitude() const { return m_altitude; } + double accuracy() const { return m_accuracy; } + double altitudeAccuracy() const { return m_altitudeAccuracy; } + double heading() const { return m_heading; } + double speed() const { return m_speed; } + DOMTimeStamp timestamp() const { return m_timestamp; } + + String toString() const; + +private: + Geoposition(double latitude, double longitude, double altitude, double accuracy, double altitudeAccuracy, double heading, double speed, DOMTimeStamp timestamp) + : m_latitude(latitude) + , m_longitude(longitude) + , m_altitude(altitude) + , m_accuracy(accuracy) + , m_altitudeAccuracy(altitudeAccuracy) + , m_heading(heading) + , m_speed(speed) + , m_timestamp(timestamp) + { + } + + double m_latitude; + double m_longitude; + double m_altitude; + double m_accuracy; + double m_altitudeAccuracy; + double m_heading; + double m_speed; + DOMTimeStamp m_timestamp; +}; + +} // namespace WebCore + +#endif // Geoposition_h diff --git a/WebCore/page/Geoposition.idl b/WebCore/page/Geoposition.idl new file mode 100644 index 0000000..554bb30 --- /dev/null +++ b/WebCore/page/Geoposition.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 Geoposition { + readonly attribute double latitude; + readonly attribute double longitude; + readonly attribute double altitude; + readonly attribute double accuracy; + readonly attribute double altitudeAccuracy; + readonly attribute double heading; + readonly attribute double speed; + readonly attribute DOMTimeStamp timestamp; + +#if defined(LANGUAGE_JAVASCRIPT) + [DontEnum] DOMString toString(); +#endif + }; +} diff --git a/WebCore/page/History.h b/WebCore/page/History.h index 815a44d..f0df2de 100644 --- a/WebCore/page/History.h +++ b/WebCore/page/History.h @@ -26,6 +26,7 @@ #ifndef History_h #define History_h +#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -34,8 +35,8 @@ namespace WebCore { class History : public RefCounted<History> { public: - History(Frame*); - + static PassRefPtr<History> create(Frame* frame) { return adoptRef(new History(frame)); } + Frame* frame() const; void disconnectFrame(); @@ -45,6 +46,8 @@ namespace WebCore { void go(int distance); private: + History(Frame*); + Frame* m_frame; }; diff --git a/WebCore/page/InspectorController.cpp b/WebCore/page/InspectorController.cpp deleted file mode 100644 index df748a0..0000000 --- a/WebCore/page/InspectorController.cpp +++ /dev/null @@ -1,1646 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "InspectorController.h" - -#include "CString.h" -#include "CachedResource.h" -#include "DocLoader.h" -#include "Document.h" -#include "DocumentLoader.h" -#include "Element.h" -#include "FloatConversion.h" -#include "FloatRect.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "GraphicsContext.h" -#include "HTMLFrameOwnerElement.h" -#include "InspectorClient.h" -#include "JSRange.h" -#include "Page.h" -#include "Range.h" -#include "ResourceRequest.h" -#include "ResourceResponse.h" -#include "Settings.h" -#include "SharedBuffer.h" -#include "SystemTime.h" -#include "TextEncoding.h" -#include "TextIterator.h" -#include "kjs_dom.h" -#include "kjs_proxy.h" -#include "kjs_window.h" -#include <JavaScriptCore/APICast.h> -#include <JavaScriptCore/JSLock.h> -#include <JavaScriptCore/JSRetainPtr.h> -#include <JavaScriptCore/JSStringRef.h> -#include <wtf/RefCounted.h> - -#if ENABLE(DATABASE) -#include "Database.h" -#include "JSDatabase.h" -#endif - -namespace WebCore { - -static JSValueRef callSimpleFunction(JSContextRef context, JSObjectRef thisObject, const char* functionName) -{ - ASSERT_ARG(context, context); - ASSERT_ARG(thisObject, thisObject); - - JSRetainPtr<JSStringRef> functionNameString(Adopt, JSStringCreateWithUTF8CString(functionName)); - JSObjectRef function = JSValueToObject(context, JSObjectGetProperty(context, thisObject, functionNameString.get(), 0), 0); - - return JSObjectCallAsFunction(context, function, thisObject, 0, 0, 0); -} - -#pragma mark - -#pragma mark ConsoleMessage Struct - -struct ConsoleMessage { - ConsoleMessage(MessageSource s, MessageLevel l, const String& m, unsigned li, const String& u) - : source(s) - , level(l) - , message(m) - , line(li) - , url(u) - { - } - - MessageSource source; - MessageLevel level; - String message; - unsigned line; - String url; -}; - -#pragma mark - -#pragma mark InspectorResource Struct - -struct InspectorResource : public RefCounted<InspectorResource> { - // Keep these in sync with WebInspector.Resource.Type - enum Type { - Doc, - Stylesheet, - Image, - Font, - Script, - Other - }; - - InspectorResource(long long identifier, DocumentLoader* documentLoader, Frame* frame) - : identifier(identifier) - , loader(documentLoader) - , frame(frame) - , scriptContext(0) - , scriptObject(0) - , expectedContentLength(0) - , cached(false) - , finished(false) - , failed(false) - , length(0) - , responseStatusCode(0) - , startTime(-1.0) - , responseReceivedTime(-1.0) - , endTime(-1.0) - { - } - - ~InspectorResource() - { - setScriptObject(0, 0); - } - - Type type() const - { - if (requestURL == loader->requestURL()) - return Doc; - - if (loader->frameLoader() && requestURL == loader->frameLoader()->iconURL()) - return Image; - - CachedResource* cachedResource = frame->document()->docLoader()->cachedResource(requestURL.string()); - if (!cachedResource) - return Other; - - switch (cachedResource->type()) { - case CachedResource::ImageResource: - return Image; - case CachedResource::FontResource: - return Font; - case CachedResource::CSSStyleSheet: -#if ENABLE(XSLT) - case CachedResource::XSLStyleSheet: -#endif - return Stylesheet; - case CachedResource::Script: - return Script; - default: - return Other; - } - } - - void setScriptObject(JSContextRef context, JSObjectRef newScriptObject) - { - if (scriptContext && scriptObject) - JSValueUnprotect(scriptContext, scriptObject); - - scriptObject = newScriptObject; - scriptContext = context; - - ASSERT((context && newScriptObject) || (!context && !newScriptObject)); - if (context && newScriptObject) - JSValueProtect(context, newScriptObject); - } - - long long identifier; - RefPtr<DocumentLoader> loader; - RefPtr<Frame> frame; - KURL requestURL; - HTTPHeaderMap requestHeaderFields; - HTTPHeaderMap responseHeaderFields; - String mimeType; - String suggestedFilename; - JSContextRef scriptContext; - JSObjectRef scriptObject; - long long expectedContentLength; - bool cached; - bool finished; - bool failed; - int length; - int responseStatusCode; - double startTime; - double responseReceivedTime; - double endTime; -}; - -#pragma mark - -#pragma mark InspectorDatabaseResource Struct - -#if ENABLE(DATABASE) -struct InspectorDatabaseResource : public RefCounted<InspectorDatabaseResource> { - InspectorDatabaseResource(Database* database, String domain, String name, String version) - : database(database) - , domain(domain) - , name(name) - , version(version) - , scriptContext(0) - , scriptObject(0) - { - } - - InspectorDatabaseResource() - { - setScriptObject(0, 0); - } - - void setScriptObject(JSContextRef context, JSObjectRef newScriptObject) - { - if (scriptContext && scriptObject) - JSValueUnprotect(scriptContext, scriptObject); - - scriptObject = newScriptObject; - scriptContext = context; - - ASSERT((context && newScriptObject) || (!context && !newScriptObject)); - if (context && newScriptObject) - JSValueProtect(context, newScriptObject); - } - - RefPtr<Database> database; - String domain; - String name; - String version; - JSContextRef scriptContext; - JSObjectRef scriptObject; -}; -#endif - -#pragma mark - -#pragma mark JavaScript Callbacks - -static JSValueRef addSourceToFrame(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) -{ - JSValueRef undefined = JSValueMakeUndefined(ctx); - - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (argumentCount < 2 || !controller) - return undefined; - - JSValueRef identifierValue = arguments[0]; - if (!JSValueIsNumber(ctx, identifierValue)) - return undefined; - - unsigned long identifier = static_cast<unsigned long>(JSValueToNumber(ctx, identifierValue, 0)); - RefPtr<InspectorResource> resource = controller->resources().get(identifier); - ASSERT(resource); - if (!resource) - return undefined; - - RefPtr<SharedBuffer> buffer; - String textEncodingName; - if (resource->requestURL == resource->loader->requestURL()) { - buffer = resource->loader->mainResourceData(); - textEncodingName = resource->frame->document()->inputEncoding(); - } else { - CachedResource* cachedResource = resource->frame->document()->docLoader()->cachedResource(resource->requestURL.string()); - if (!cachedResource) - return undefined; - - buffer = cachedResource->data(); - textEncodingName = cachedResource->encoding(); - } - - if (!buffer) - return undefined; - - TextEncoding encoding(textEncodingName); - if (!encoding.isValid()) - encoding = WindowsLatin1Encoding(); - String sourceString = encoding.decode(buffer->data(), buffer->size()); - - Node* node = toNode(toJS(arguments[1])); - ASSERT(node); - if (!node) - return undefined; - - if (!node->attached()) { - ASSERT_NOT_REACHED(); - return undefined; - } - - ASSERT(node->isElementNode()); - if (!node->isElementNode()) - return undefined; - - Element* element = static_cast<Element*>(node); - ASSERT(element->isFrameOwnerElement()); - if (!element->isFrameOwnerElement()) - return undefined; - - HTMLFrameOwnerElement* frameOwner = static_cast<HTMLFrameOwnerElement*>(element); - ASSERT(frameOwner->contentFrame()); - if (!frameOwner->contentFrame()) - return undefined; - - FrameLoader* loader = frameOwner->contentFrame()->loader(); - - loader->setResponseMIMEType(resource->mimeType); - loader->begin(); - loader->write(sourceString); - loader->end(); - - return undefined; -} - -static JSValueRef getResourceDocumentNode(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) -{ - JSValueRef undefined = JSValueMakeUndefined(ctx); - - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!argumentCount || argumentCount > 1 || !controller) - return undefined; - - JSValueRef identifierValue = arguments[0]; - if (!JSValueIsNumber(ctx, identifierValue)) - return undefined; - - unsigned long identifier = static_cast<unsigned long>(JSValueToNumber(ctx, identifierValue, 0)); - RefPtr<InspectorResource> resource = controller->resources().get(identifier); - ASSERT(resource); - if (!resource) - return undefined; - - Document* document = resource->frame->document(); - if (!document) - return undefined; - - if (document->isPluginDocument() || document->isImageDocument()) - return undefined; - - KJS::JSLock lock; - JSValueRef documentValue = toRef(toJS(toJS(controller->scriptContext()), document)); - return documentValue; -} - -static JSValueRef highlightDOMNode(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) -{ - JSValueRef undefined = JSValueMakeUndefined(context); - - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (argumentCount < 1 || !controller) - return undefined; - - Node* node = toNode(toJS(arguments[0])); - if (!node) - return undefined; - - controller->highlight(node); - - return undefined; -} - -static JSValueRef hideDOMNodeHighlight(JSContextRef context, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) -{ - JSValueRef undefined = JSValueMakeUndefined(context); - - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (argumentCount || !controller) - return undefined; - - controller->hideHighlight(); - - return undefined; -} - -static JSValueRef loaded(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - controller->scriptObjectReady(); - return JSValueMakeUndefined(ctx); -} - -static JSValueRef unloading(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - controller->close(); - return JSValueMakeUndefined(ctx); -} - -static JSValueRef attach(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - controller->attachWindow(); - return JSValueMakeUndefined(ctx); -} - -static JSValueRef detach(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - controller->detachWindow(); - return JSValueMakeUndefined(ctx); -} - -static JSValueRef search(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - if (argumentCount < 2 || !JSValueIsString(ctx, arguments[1])) - return JSValueMakeUndefined(ctx); - - Node* node = toNode(toJS(arguments[0])); - if (!node) - return JSValueMakeUndefined(ctx); - - JSRetainPtr<JSStringRef> searchString(Adopt, JSValueToStringCopy(ctx, arguments[1], 0)); - String target(JSStringGetCharactersPtr(searchString.get()), JSStringGetLength(searchString.get())); - - JSObjectRef global = JSContextGetGlobalObject(ctx); - JSRetainPtr<JSStringRef> arrayString(Adopt, JSStringCreateWithUTF8CString("Array")); - JSObjectRef arrayConstructor = JSValueToObject(ctx, JSObjectGetProperty(ctx, global, arrayString.get(), 0), 0); - - JSObjectRef result = JSObjectCallAsConstructor(ctx, arrayConstructor, 0, 0, 0); - - JSRetainPtr<JSStringRef> pushString(Adopt, JSStringCreateWithUTF8CString("push")); - JSObjectRef pushFunction = JSValueToObject(ctx, JSObjectGetProperty(ctx, result, pushString.get(), 0), 0); - - RefPtr<Range> searchRange(rangeOfContents(node)); - - int exception = 0; - do { - RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, false)); - if (resultRange->collapsed(exception)) - break; - - // A non-collapsed result range can in some funky whitespace cases still not - // advance the range's start position (4509328). Break to avoid infinite loop. - VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM); - if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM)) - break; - - KJS::JSLock lock; - JSValueRef arg0 = toRef(toJS(toJS(ctx), resultRange.get())); - JSObjectCallAsFunction(ctx, pushFunction, result, 1, &arg0, 0); - - setStart(searchRange.get(), newStart); - } while (true); - - return result; -} - -#if ENABLE(DATABASE) -static JSValueRef databaseTableNames(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - if (argumentCount < 1) - return JSValueMakeUndefined(ctx); - - Database* database = toDatabase(toJS(arguments[0])); - if (!database) - return JSValueMakeUndefined(ctx); - - JSObjectRef global = JSContextGetGlobalObject(ctx); - JSRetainPtr<JSStringRef> arrayString(Adopt, JSStringCreateWithUTF8CString("Array")); - JSObjectRef arrayConstructor = JSValueToObject(ctx, JSObjectGetProperty(ctx, global, arrayString.get(), 0), 0); - - JSObjectRef result = JSObjectCallAsConstructor(ctx, arrayConstructor, 0, 0, 0); - - JSRetainPtr<JSStringRef> pushString(Adopt, JSStringCreateWithUTF8CString("push")); - JSObjectRef pushFunction = JSValueToObject(ctx, JSObjectGetProperty(ctx, result, pushString.get(), 0), 0); - - Vector<String> tableNames = database->tableNames(); - unsigned length = tableNames.size(); - for (unsigned i = 0; i < length; ++i) { - String tableName = tableNames[i]; - JSRetainPtr<JSStringRef> tableNameString(Adopt, JSStringCreateWithCharacters(tableName.characters(), tableName.length())); - JSValueRef tableNameValue = JSValueMakeString(ctx, tableNameString.get()); - - JSValueRef pushArguments[] = { tableNameValue }; - JSObjectCallAsFunction(ctx, pushFunction, result, 1, pushArguments, 0); - } - - return result; -} -#endif - -static JSValueRef inspectedWindow(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - return toRef(KJS::Window::retrieve(controller->inspectedPage()->mainFrame())); -} - -static JSValueRef localizedStrings(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - String url = controller->localizedStringsURL(); - if (url.isNull()) - return JSValueMakeNull(ctx); - - JSRetainPtr<JSStringRef> urlString(Adopt, JSStringCreateWithCharacters(url.characters(), url.length())); - return JSValueMakeString(ctx, urlString.get()); -} - -static JSValueRef platform(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t /*argumentCount*/, const JSValueRef[] /*arguments[]*/, JSValueRef* /*exception*/) -{ -#if PLATFORM(MAC) -#ifdef BUILDING_ON_TIGER - static const String platform = "mac-tiger"; -#else - static const String platform = "mac-leopard"; -#endif -#elif PLATFORM(WIN_OS) - static const String platform = "windows"; -#elif PLATFORM(QT) - static const String platform = "qt"; -#elif PLATFORM(GTK) - static const String platform = "gtk"; -#elif PLATFORM(WX) - static const String platform = "wx"; -#else - static const String platform = "unknown"; -#endif - - JSRetainPtr<JSStringRef> platformString(Adopt, JSStringCreateWithCharacters(platform.characters(), platform.length())); - JSValueRef platformValue = JSValueMakeString(ctx, platformString.get()); - - return platformValue; -} - -static JSValueRef moveByUnrestricted(JSContextRef ctx, JSObjectRef /*function*/, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* /*exception*/) -{ - InspectorController* controller = reinterpret_cast<InspectorController*>(JSObjectGetPrivate(thisObject)); - if (!controller) - return JSValueMakeUndefined(ctx); - - if (argumentCount < 2) - return JSValueMakeUndefined(ctx); - - controller->moveWindowBy(narrowPrecisionToFloat(JSValueToNumber(ctx, arguments[0], 0)), narrowPrecisionToFloat(JSValueToNumber(ctx, arguments[1], 0))); - - return JSValueMakeUndefined(ctx); -} - -#pragma mark - -#pragma mark InspectorController Class - -InspectorController::InspectorController(Page* page, InspectorClient* client) - : m_inspectedPage(page) - , m_client(client) - , m_page(0) - , m_scriptObject(0) - , m_controllerScriptObject(0) - , m_scriptContext(0) - , m_windowVisible(false) - , m_showAfterVisible(FocusedNodeDocumentPanel) - , m_nextIdentifier(-2) -{ - ASSERT_ARG(page, page); - ASSERT_ARG(client, client); -} - -InspectorController::~InspectorController() -{ - m_client->inspectorDestroyed(); - - if (m_scriptContext) { - JSObjectRef global = JSContextGetGlobalObject(m_scriptContext); - JSRetainPtr<JSStringRef> controllerProperty(Adopt, JSStringCreateWithUTF8CString("InspectorController")); - JSObjectRef controller = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, global, controllerProperty.get(), 0), 0); - if (controller) - JSObjectSetPrivate(controller, 0); - } - - if (m_page) - m_page->setParentInspectorController(0); - - deleteAllValues(m_frameResources); - deleteAllValues(m_consoleMessages); -} - -bool InspectorController::enabled() const -{ - return m_inspectedPage->settings()->developerExtrasEnabled(); -} - -String InspectorController::localizedStringsURL() -{ - if (!enabled()) - return String(); - return m_client->localizedStringsURL(); -} - -// Trying to inspect something in a frame with JavaScript disabled would later lead to -// crashes trying to create JavaScript wrappers. Some day we could fix this issue, but -// for now prevent crashes here by never targeting a node in such a frame. -static bool canPassNodeToJavaScript(Node* node) -{ - if (!node) - return false; - Frame* frame = node->document()->frame(); - return frame && frame->scriptProxy()->isEnabled(); -} - -void InspectorController::inspect(Node* node) -{ - if (!canPassNodeToJavaScript(node) || !enabled()) - return; - - show(); - - if (node->nodeType() != Node::ELEMENT_NODE && node->nodeType() != Node::DOCUMENT_NODE) - node = node->parentNode(); - m_nodeToFocus = node; - - if (!m_scriptObject) { - m_showAfterVisible = FocusedNodeDocumentPanel; - return; - } - - if (windowVisible()) - focusNode(); -} - -void InspectorController::focusNode() -{ - if (!enabled()) - return; - - ASSERT(m_scriptContext); - ASSERT(m_scriptObject); - ASSERT(m_nodeToFocus); - - JSValueRef arg0; - - { - KJS::JSLock lock; - arg0 = toRef(toJS(toJS(m_scriptContext), m_nodeToFocus.get())); - } - - m_nodeToFocus = 0; - - JSRetainPtr<JSStringRef> functionProperty(Adopt, JSStringCreateWithUTF8CString("updateFocusedNode")); - JSObjectRef function = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, functionProperty.get(), 0), 0); - ASSERT(function); - - JSObjectCallAsFunction(m_scriptContext, function, m_scriptObject, 1, &arg0, 0); -} - -void InspectorController::highlight(Node* node) -{ - if (!enabled()) - return; - ASSERT_ARG(node, node); - m_highlightedNode = node; - m_client->highlight(node); -} - -void InspectorController::hideHighlight() -{ - if (!enabled()) - return; - m_client->hideHighlight(); -} - -bool InspectorController::windowVisible() -{ - return m_windowVisible; -} - -void InspectorController::setWindowVisible(bool visible) -{ - if (visible == m_windowVisible) - return; - - m_windowVisible = visible; - - if (!m_scriptContext || !m_scriptObject) - return; - - if (m_windowVisible) { - populateScriptResources(); - if (m_nodeToFocus) - focusNode(); - if (m_showAfterVisible == ConsolePanel) - showConsole(); - else if (m_showAfterVisible == TimelinePanel) - showTimeline(); - } else { - clearScriptResources(); - clearScriptConsoleMessages(); - clearDatabaseScriptResources(); - clearNetworkTimeline(); - } - - m_showAfterVisible = FocusedNodeDocumentPanel; -} - -void InspectorController::addMessageToConsole(MessageSource source, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceID) -{ - if (!enabled()) - return; - - ConsoleMessage* consoleMessage = new ConsoleMessage(source, level, message, lineNumber, sourceID); - m_consoleMessages.append(consoleMessage); - - if (windowVisible()) - addScriptConsoleMessage(consoleMessage); -} - -void InspectorController::attachWindow() -{ - if (!enabled()) - return; - m_client->attachWindow(); -} - -void InspectorController::detachWindow() -{ - if (!enabled()) - return; - m_client->detachWindow(); -} - -void InspectorController::windowScriptObjectAvailable() -{ - if (!m_page || !enabled()) - return; - - m_scriptContext = toRef(m_page->mainFrame()->scriptProxy()->globalObject()->globalExec()); - - JSObjectRef global = JSContextGetGlobalObject(m_scriptContext); - ASSERT(global); - - static JSStaticFunction staticFunctions[] = { - { "addSourceToFrame", addSourceToFrame, kJSPropertyAttributeNone }, - { "getResourceDocumentNode", getResourceDocumentNode, kJSPropertyAttributeNone }, - { "highlightDOMNode", highlightDOMNode, kJSPropertyAttributeNone }, - { "hideDOMNodeHighlight", hideDOMNodeHighlight, kJSPropertyAttributeNone }, - { "loaded", loaded, kJSPropertyAttributeNone }, - { "windowUnloading", unloading, kJSPropertyAttributeNone }, - { "attach", attach, kJSPropertyAttributeNone }, - { "detach", detach, kJSPropertyAttributeNone }, - { "search", search, kJSPropertyAttributeNone }, -#if ENABLE(DATABASE) - { "databaseTableNames", databaseTableNames, kJSPropertyAttributeNone }, -#endif - { "inspectedWindow", inspectedWindow, kJSPropertyAttributeNone }, - { "localizedStringsURL", localizedStrings, kJSPropertyAttributeNone }, - { "platform", platform, kJSPropertyAttributeNone }, - { "moveByUnrestricted", moveByUnrestricted, kJSPropertyAttributeNone }, - { 0, 0, 0 } - }; - - JSClassDefinition inspectorControllerDefinition = { - 0, kJSClassAttributeNone, "InspectorController", 0, 0, staticFunctions, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - JSClassRef controllerClass = JSClassCreate(&inspectorControllerDefinition); - ASSERT(controllerClass); - - m_controllerScriptObject = JSObjectMake(m_scriptContext, controllerClass, reinterpret_cast<void*>(this)); - ASSERT(m_controllerScriptObject); - - JSRetainPtr<JSStringRef> controllerObjectString(Adopt, JSStringCreateWithUTF8CString("InspectorController")); - JSObjectSetProperty(m_scriptContext, global, controllerObjectString.get(), m_controllerScriptObject, kJSPropertyAttributeNone, 0); -} - -void InspectorController::scriptObjectReady() -{ - ASSERT(m_scriptContext); - if (!m_scriptContext) - return; - - JSObjectRef global = JSContextGetGlobalObject(m_scriptContext); - ASSERT(global); - - JSRetainPtr<JSStringRef> inspectorString(Adopt, JSStringCreateWithUTF8CString("WebInspector")); - JSValueRef inspectorValue = JSObjectGetProperty(m_scriptContext, global, inspectorString.get(), 0); - - ASSERT(inspectorValue); - if (!inspectorValue) - return; - - m_scriptObject = JSValueToObject(m_scriptContext, inspectorValue, 0); - ASSERT(m_scriptObject); - - JSValueProtect(m_scriptContext, m_scriptObject); - - // Make sure our window is visible now that the page loaded - m_client->showWindow(); -} - -void InspectorController::show() -{ - if (!enabled()) - return; - - if (!m_page) { - m_page = m_client->createPage(); - if (!m_page) - return; - m_page->setParentInspectorController(this); - - // m_client->showWindow() will be called after the page loads in scriptObjectReady() - return; - } - - m_client->showWindow(); -} - -void InspectorController::showConsole() -{ - if (!enabled()) - return; - - show(); - - if (!m_scriptObject) { - m_showAfterVisible = ConsolePanel; - return; - } - - callSimpleFunction(m_scriptContext, m_scriptObject, "showConsole"); -} - -void InspectorController::showTimeline() -{ - if (!enabled()) - return; - - show(); - - if (!m_scriptObject) { - m_showAfterVisible = TimelinePanel; - return; - } - - callSimpleFunction(m_scriptContext, m_scriptObject, "showTimeline"); -} - -void InspectorController::close() -{ - if (!enabled()) - return; - - m_client->closeWindow(); - if (m_page) - m_page->setParentInspectorController(0); - - ASSERT(m_scriptContext && m_scriptObject); - JSValueUnprotect(m_scriptContext, m_scriptObject); - - m_page = 0; - m_scriptObject = 0; - m_scriptContext = 0; -} - -static void addHeaders(JSContextRef context, JSObjectRef object, const HTTPHeaderMap& headers) -{ - ASSERT_ARG(context, context); - ASSERT_ARG(object, object); - - HTTPHeaderMap::const_iterator end = headers.end(); - for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) { - JSRetainPtr<JSStringRef> field(Adopt, JSStringCreateWithCharacters(it->first.characters(), it->first.length())); - JSRetainPtr<JSStringRef> valueString(Adopt, JSStringCreateWithCharacters(it->second.characters(), it->second.length())); - JSValueRef value = JSValueMakeString(context, valueString.get()); - JSObjectSetProperty(context, object, field.get(), value, kJSPropertyAttributeNone, 0); - } -} - -static JSObjectRef scriptObjectForRequest(JSContextRef context, const InspectorResource* resource) -{ - ASSERT_ARG(context, context); - - JSObjectRef object = JSObjectMake(context, 0, 0); - addHeaders(context, object, resource->requestHeaderFields); - - return object; -} - -static JSObjectRef scriptObjectForResponse(JSContextRef context, const InspectorResource* resource) -{ - ASSERT_ARG(context, context); - - JSObjectRef object = JSObjectMake(context, 0, 0); - addHeaders(context, object, resource->responseHeaderFields); - - return object; -} - -JSObjectRef InspectorController::addScriptResource(InspectorResource* resource) -{ - ASSERT_ARG(resource, resource); - - ASSERT(m_scriptContext); - ASSERT(m_scriptObject); - if (!m_scriptContext || !m_scriptObject) - return 0; - - if (!resource->scriptObject) { - JSRetainPtr<JSStringRef> resourceString(Adopt, JSStringCreateWithUTF8CString("Resource")); - JSObjectRef resourceConstructor = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, resourceString.get(), 0), 0); - - String urlString = resource->requestURL.string(); - JSRetainPtr<JSStringRef> url(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef urlValue = JSValueMakeString(m_scriptContext, url.get()); - - urlString = resource->requestURL.host(); - JSRetainPtr<JSStringRef> domain(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef domainValue = JSValueMakeString(m_scriptContext, domain.get()); - - urlString = resource->requestURL.path(); - JSRetainPtr<JSStringRef> path(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef pathValue = JSValueMakeString(m_scriptContext, path.get()); - - urlString = resource->requestURL.lastPathComponent(); - JSRetainPtr<JSStringRef> lastPathComponent(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef lastPathComponentValue = JSValueMakeString(m_scriptContext, lastPathComponent.get()); - - JSValueRef identifier = JSValueMakeNumber(m_scriptContext, resource->identifier); - JSValueRef mainResource = JSValueMakeBoolean(m_scriptContext, m_mainResource == resource); - JSValueRef cached = JSValueMakeBoolean(m_scriptContext, resource->cached); - - JSValueRef arguments[] = { scriptObjectForRequest(m_scriptContext, resource), urlValue, domainValue, pathValue, lastPathComponentValue, identifier, mainResource, cached }; - JSObjectRef result = JSObjectCallAsConstructor(m_scriptContext, resourceConstructor, 8, arguments, 0); - ASSERT(result); - - resource->setScriptObject(m_scriptContext, result); - } - - JSRetainPtr<JSStringRef> addResourceString(Adopt, JSStringCreateWithUTF8CString("addResource")); - JSObjectRef addResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, addResourceString.get(), 0), 0); - - JSValueRef addArguments[] = { resource->scriptObject }; - JSObjectCallAsFunction(m_scriptContext, addResourceFunction, m_scriptObject, 1, addArguments, 0); - - return resource->scriptObject; -} - -JSObjectRef InspectorController::addAndUpdateScriptResource(InspectorResource* resource) -{ - ASSERT_ARG(resource, resource); - - JSObjectRef scriptResource = addScriptResource(resource); - updateScriptResourceResponse(resource); - updateScriptResource(resource, resource->length); - updateScriptResource(resource, resource->startTime, resource->responseReceivedTime, resource->endTime); - updateScriptResource(resource, resource->finished, resource->failed); - return scriptResource; -} - -void InspectorController::removeScriptResource(InspectorResource* resource) -{ - ASSERT(m_scriptContext); - ASSERT(m_scriptObject); - if (!m_scriptContext || !m_scriptObject) - return; - - ASSERT(resource); - ASSERT(resource->scriptObject); - if (!resource || !resource->scriptObject) - return; - - JSRetainPtr<JSStringRef> removeResourceString(Adopt, JSStringCreateWithUTF8CString("removeResource")); - JSObjectRef removeResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, removeResourceString.get(), 0), 0); - - JSValueRef arguments[] = { resource->scriptObject }; - JSObjectCallAsFunction(m_scriptContext, removeResourceFunction, m_scriptObject, 1, arguments, 0); - - resource->setScriptObject(0, 0); -} - -static void updateResourceRequest(InspectorResource* resource, const ResourceRequest& request) -{ - resource->requestHeaderFields = request.httpHeaderFields(); - resource->requestURL = request.url(); -} - -static void updateResourceResponse(InspectorResource* resource, const ResourceResponse& response) -{ - resource->expectedContentLength = response.expectedContentLength(); - resource->mimeType = response.mimeType(); - resource->responseHeaderFields = response.httpHeaderFields(); - resource->responseStatusCode = response.httpStatusCode(); - resource->suggestedFilename = response.suggestedFilename(); -} - -void InspectorController::updateScriptResourceRequest(InspectorResource* resource) -{ - ASSERT(resource->scriptObject); - ASSERT(m_scriptContext); - if (!resource->scriptObject || !m_scriptContext) - return; - - String urlString = resource->requestURL.string(); - JSRetainPtr<JSStringRef> url(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef urlValue = JSValueMakeString(m_scriptContext, url.get()); - - urlString = resource->requestURL.host(); - JSRetainPtr<JSStringRef> domain(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef domainValue = JSValueMakeString(m_scriptContext, domain.get()); - - urlString = resource->requestURL.path(); - JSRetainPtr<JSStringRef> path(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef pathValue = JSValueMakeString(m_scriptContext, path.get()); - - urlString = resource->requestURL.lastPathComponent(); - JSRetainPtr<JSStringRef> lastPathComponent(Adopt, JSStringCreateWithCharacters(urlString.characters(), urlString.length())); - JSValueRef lastPathComponentValue = JSValueMakeString(m_scriptContext, lastPathComponent.get()); - - JSValueRef mainResourceValue = JSValueMakeBoolean(m_scriptContext, m_mainResource == resource); - - JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString("url")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), urlValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("domain")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), domainValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("path")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), pathValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("lastPathComponent")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), lastPathComponentValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("requestHeaders")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), scriptObjectForRequest(m_scriptContext, resource), kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("mainResource")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), mainResourceValue, kJSPropertyAttributeNone, 0); -} - -void InspectorController::updateScriptResourceResponse(InspectorResource* resource) -{ - ASSERT(resource->scriptObject); - ASSERT(m_scriptContext); - if (!resource->scriptObject || !m_scriptContext) - return; - - JSRetainPtr<JSStringRef> mimeType(Adopt, JSStringCreateWithCharacters(resource->mimeType.characters(), resource->mimeType.length())); - JSValueRef mimeTypeValue = JSValueMakeString(m_scriptContext, mimeType.get()); - - JSRetainPtr<JSStringRef> suggestedFilename(Adopt, JSStringCreateWithCharacters(resource->suggestedFilename.characters(), resource->suggestedFilename.length())); - JSValueRef suggestedFilenameValue = JSValueMakeString(m_scriptContext, suggestedFilename.get()); - - JSValueRef expectedContentLengthValue = JSValueMakeNumber(m_scriptContext, static_cast<double>(resource->expectedContentLength)); - JSValueRef statusCodeValue = JSValueMakeNumber(m_scriptContext, resource->responseStatusCode); - - JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString("mimeType")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), mimeTypeValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("suggestedFilename")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), suggestedFilenameValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("expectedContentLength")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), expectedContentLengthValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("statusCode")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), statusCodeValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("responseHeaders")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), scriptObjectForResponse(m_scriptContext, resource), kJSPropertyAttributeNone, 0); - - JSValueRef typeValue = JSValueMakeNumber(m_scriptContext, resource->type()); - propertyName.adopt(JSStringCreateWithUTF8CString("type")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), typeValue, kJSPropertyAttributeNone, 0); -} - -void InspectorController::updateScriptResource(InspectorResource* resource, int length) -{ - ASSERT(resource->scriptObject); - ASSERT(m_scriptContext); - if (!resource->scriptObject || !m_scriptContext) - return; - - JSValueRef lengthValue = JSValueMakeNumber(m_scriptContext, length); - - JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString("contentLength")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), lengthValue, kJSPropertyAttributeNone, 0); -} - -void InspectorController::updateScriptResource(InspectorResource* resource, bool finished, bool failed) -{ - ASSERT(resource->scriptObject); - ASSERT(m_scriptContext); - if (!resource->scriptObject || !m_scriptContext) - return; - - JSValueRef failedValue = JSValueMakeBoolean(m_scriptContext, failed); - JSValueRef finishedValue = JSValueMakeBoolean(m_scriptContext, finished); - - JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString("failed")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), failedValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("finished")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), finishedValue, kJSPropertyAttributeNone, 0); -} - -void InspectorController::updateScriptResource(InspectorResource* resource, double startTime, double responseReceivedTime, double endTime) -{ - ASSERT(resource->scriptObject); - ASSERT(m_scriptContext); - if (!resource->scriptObject || !m_scriptContext) - return; - - JSValueRef startTimeValue = JSValueMakeNumber(m_scriptContext, startTime); - JSValueRef responseReceivedTimeValue = JSValueMakeNumber(m_scriptContext, responseReceivedTime); - JSValueRef endTimeValue = JSValueMakeNumber(m_scriptContext, endTime); - - JSRetainPtr<JSStringRef> propertyName(Adopt, JSStringCreateWithUTF8CString("startTime")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), startTimeValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("responseReceivedTime")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), responseReceivedTimeValue, kJSPropertyAttributeNone, 0); - - propertyName.adopt(JSStringCreateWithUTF8CString("endTime")); - JSObjectSetProperty(m_scriptContext, resource->scriptObject, propertyName.get(), endTimeValue, kJSPropertyAttributeNone, 0); -} - -void InspectorController::populateScriptResources() -{ - ASSERT(m_scriptContext); - if (!m_scriptContext) - return; - - clearScriptResources(); - clearScriptConsoleMessages(); - clearDatabaseScriptResources(); - clearNetworkTimeline(); - - ResourcesMap::iterator resourcesEnd = m_resources.end(); - for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) - addAndUpdateScriptResource(it->second.get()); - - unsigned messageCount = m_consoleMessages.size(); - for (unsigned i = 0; i < messageCount; ++i) - addScriptConsoleMessage(m_consoleMessages[i]); - -#if ENABLE(DATABASE) - DatabaseResourcesSet::iterator databasesEnd = m_databaseResources.end(); - for (DatabaseResourcesSet::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it) - addDatabaseScriptResource((*it).get()); -#endif -} - -#if ENABLE(DATABASE) -JSObjectRef InspectorController::addDatabaseScriptResource(InspectorDatabaseResource* resource) -{ - ASSERT_ARG(resource, resource); - - if (resource->scriptObject) - return resource->scriptObject; - - ASSERT(m_scriptContext); - ASSERT(m_scriptObject); - if (!m_scriptContext || !m_scriptObject) - return 0; - - JSRetainPtr<JSStringRef> databaseString(Adopt, JSStringCreateWithUTF8CString("Database")); - JSObjectRef databaseConstructor = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, databaseString.get(), 0), 0); - - JSValueRef database; - - { - KJS::JSLock lock; - database = toRef(toJS(toJS(m_scriptContext), resource->database.get())); - } - - JSRetainPtr<JSStringRef> domain(Adopt, JSStringCreateWithCharacters(resource->domain.characters(), resource->domain.length())); - JSValueRef domainValue = JSValueMakeString(m_scriptContext, domain.get()); - - JSRetainPtr<JSStringRef> name(Adopt, JSStringCreateWithCharacters(resource->name.characters(), resource->name.length())); - JSValueRef nameValue = JSValueMakeString(m_scriptContext, name.get()); - - JSRetainPtr<JSStringRef> version(Adopt, JSStringCreateWithCharacters(resource->version.characters(), resource->version.length())); - JSValueRef versionValue = JSValueMakeString(m_scriptContext, version.get()); - - JSValueRef arguments[] = { database, domainValue, nameValue, versionValue }; - JSObjectRef result = JSObjectCallAsConstructor(m_scriptContext, databaseConstructor, 4, arguments, 0); - - resource->setScriptObject(m_scriptContext, result); - - ASSERT(result); - - JSRetainPtr<JSStringRef> addResourceString(Adopt, JSStringCreateWithUTF8CString("addResource")); - JSObjectRef addResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, addResourceString.get(), 0), 0); - - JSValueRef addArguments[] = { result }; - JSObjectCallAsFunction(m_scriptContext, addResourceFunction, m_scriptObject, 1, addArguments, 0); - - return result; -} - -void InspectorController::removeDatabaseScriptResource(InspectorDatabaseResource* resource) -{ - ASSERT(m_scriptContext); - ASSERT(m_scriptObject); - if (!m_scriptContext || !m_scriptObject) - return; - - ASSERT(resource); - ASSERT(resource->scriptObject); - if (!resource || !resource->scriptObject) - return; - - JSRetainPtr<JSStringRef> removeResourceString(Adopt, JSStringCreateWithUTF8CString("removeResource")); - JSObjectRef removeResourceFunction = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, removeResourceString.get(), 0), 0); - - JSValueRef arguments[] = { resource->scriptObject }; - JSObjectCallAsFunction(m_scriptContext, removeResourceFunction, m_scriptObject, 1, arguments, 0); - - resource->setScriptObject(0, 0); -} -#endif - -void InspectorController::addScriptConsoleMessage(const ConsoleMessage* message) -{ - ASSERT_ARG(message, message); - - JSRetainPtr<JSStringRef> messageConstructorString(Adopt, JSStringCreateWithUTF8CString("ConsoleMessage")); - JSObjectRef messageConstructor = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, messageConstructorString.get(), 0), 0); - - JSRetainPtr<JSStringRef> addMessageString(Adopt, JSStringCreateWithUTF8CString("addMessageToConsole")); - JSObjectRef addMessage = JSValueToObject(m_scriptContext, JSObjectGetProperty(m_scriptContext, m_scriptObject, addMessageString.get(), 0), 0); - - JSValueRef sourceValue = JSValueMakeNumber(m_scriptContext, message->source); - JSValueRef levelValue = JSValueMakeNumber(m_scriptContext, message->level); - JSRetainPtr<JSStringRef> messageString(Adopt, JSStringCreateWithCharacters(message->message.characters(), message->message.length())); - JSValueRef messageValue = JSValueMakeString(m_scriptContext, messageString.get()); - JSValueRef lineValue = JSValueMakeNumber(m_scriptContext, message->line); - JSRetainPtr<JSStringRef> urlString(Adopt, JSStringCreateWithCharacters(message->url.characters(), message->url.length())); - JSValueRef urlValue = JSValueMakeString(m_scriptContext, urlString.get()); - - JSValueRef args[] = { sourceValue, levelValue, messageValue, lineValue, urlValue }; - JSObjectRef messageObject = JSObjectCallAsConstructor(m_scriptContext, messageConstructor, 5, args, 0); - - JSObjectCallAsFunction(m_scriptContext, addMessage, m_scriptObject, 1, &messageObject, 0); -} - -void InspectorController::clearScriptResources() -{ - if (!m_scriptContext || !m_scriptObject) - return; - - ResourcesMap::iterator resourcesEnd = m_resources.end(); - for (ResourcesMap::iterator it = m_resources.begin(); it != resourcesEnd; ++it) { - InspectorResource* resource = it->second.get(); - resource->setScriptObject(0, 0); - } - - callSimpleFunction(m_scriptContext, m_scriptObject, "clearResources"); -} - -void InspectorController::clearDatabaseScriptResources() -{ -#if ENABLE(DATABASE) - if (!m_scriptContext || !m_scriptObject) - return; - - DatabaseResourcesSet::iterator databasesEnd = m_databaseResources.end(); - for (DatabaseResourcesSet::iterator it = m_databaseResources.begin(); it != databasesEnd; ++it) { - InspectorDatabaseResource* resource = (*it).get(); - resource->setScriptObject(0, 0); - } - - callSimpleFunction(m_scriptContext, m_scriptObject, "clearDatabaseResources"); -#endif -} - -void InspectorController::clearScriptConsoleMessages() -{ - if (!m_scriptContext || !m_scriptObject) - return; - - callSimpleFunction(m_scriptContext, m_scriptObject, "clearConsoleMessages"); -} - -void InspectorController::clearNetworkTimeline() -{ - if (!m_scriptContext || !m_scriptObject) - return; - - callSimpleFunction(m_scriptContext, m_scriptObject, "clearNetworkTimeline"); -} - -void InspectorController::pruneResources(ResourcesMap* resourceMap, DocumentLoader* loaderToKeep) -{ - ASSERT_ARG(resourceMap, resourceMap); - - ResourcesMap mapCopy(*resourceMap); - ResourcesMap::iterator end = mapCopy.end(); - for (ResourcesMap::iterator it = mapCopy.begin(); it != end; ++it) { - InspectorResource* resource = (*it).second.get(); - if (resource == m_mainResource) - continue; - - if (!loaderToKeep || resource->loader != loaderToKeep) { - removeResource(resource); - if (windowVisible() && resource->scriptObject) - removeScriptResource(resource); - } - } -} - -void InspectorController::didCommitLoad(DocumentLoader* loader) -{ - if (!enabled()) - return; - - if (loader->frame() == m_inspectedPage->mainFrame()) { - m_client->inspectedURLChanged(loader->url().string()); - - deleteAllValues(m_consoleMessages); - m_consoleMessages.clear(); - -#if ENABLE(DATABASE) - m_databaseResources.clear(); -#endif - - if (windowVisible()) { - clearScriptConsoleMessages(); -#if ENABLE(DATABASE) - clearDatabaseScriptResources(); -#endif - clearNetworkTimeline(); - - if (!loader->isLoadingFromCachedPage()) { - ASSERT(m_mainResource && m_mainResource->loader == loader); - // We don't add the main resource until its load is committed. This is - // needed to keep the load for a user-entered URL from showing up in the - // list of resources for the page they are navigating away from. - addAndUpdateScriptResource(m_mainResource.get()); - } else { - // Pages loaded from the page cache are committed before - // m_mainResource is the right resource for this load, so we - // clear it here. It will be re-assigned in - // identifierForInitialRequest. - m_mainResource = 0; - } - } - } - - for (Frame* frame = loader->frame(); frame; frame = frame->tree()->traverseNext(loader->frame())) - if (ResourcesMap* resourceMap = m_frameResources.get(frame)) - pruneResources(resourceMap, loader); -} - -void InspectorController::frameDetachedFromParent(Frame* frame) -{ - if (!enabled()) - return; - if (ResourcesMap* resourceMap = m_frameResources.get(frame)) - removeAllResources(resourceMap); -} - -void InspectorController::addResource(InspectorResource* resource) -{ - m_resources.set(resource->identifier, resource); - - Frame* frame = resource->frame.get(); - ResourcesMap* resourceMap = m_frameResources.get(frame); - if (resourceMap) - resourceMap->set(resource->identifier, resource); - else { - resourceMap = new ResourcesMap; - resourceMap->set(resource->identifier, resource); - m_frameResources.set(frame, resourceMap); - } -} - -void InspectorController::removeResource(InspectorResource* resource) -{ - m_resources.remove(resource->identifier); - - Frame* frame = resource->frame.get(); - ResourcesMap* resourceMap = m_frameResources.get(frame); - if (!resourceMap) { - ASSERT_NOT_REACHED(); - return; - } - - resourceMap->remove(resource->identifier); - if (resourceMap->isEmpty()) { - m_frameResources.remove(frame); - delete resourceMap; - } -} - -void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader* loader, const ResourceRequest& request, const ResourceResponse& response, int length) -{ - if (!enabled()) - return; - - InspectorResource* resource = new InspectorResource(m_nextIdentifier--, loader, loader->frame()); - resource->finished = true; - - updateResourceRequest(resource, request); - updateResourceResponse(resource, response); - - resource->length = length; - resource->cached = true; - resource->startTime = currentTime(); - resource->responseReceivedTime = resource->startTime; - resource->endTime = resource->startTime; - - if (loader->frame() == m_inspectedPage->mainFrame() && request.url() == loader->requestURL()) - m_mainResource = resource; - - addResource(resource); - - if (windowVisible()) - addAndUpdateScriptResource(resource); -} - -void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request) -{ - if (!enabled()) - return; - - InspectorResource* resource = new InspectorResource(identifier, loader, loader->frame()); - - updateResourceRequest(resource, request); - - if (loader->frame() == m_inspectedPage->mainFrame() && request.url() == loader->requestURL()) - m_mainResource = resource; - - addResource(resource); - - if (windowVisible() && loader->isLoadingFromCachedPage() && resource == m_mainResource) - addAndUpdateScriptResource(resource); -} - -void InspectorController::willSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse) -{ - if (!enabled()) - return; - - InspectorResource* resource = m_resources.get(identifier).get(); - if (!resource) - return; - - resource->startTime = currentTime(); - - if (!redirectResponse.isNull()) { - updateResourceRequest(resource, request); - updateResourceResponse(resource, redirectResponse); - } - - if (resource != m_mainResource && windowVisible()) { - if (!resource->scriptObject) - addScriptResource(resource); - else - updateScriptResourceRequest(resource); - - updateScriptResource(resource, resource->startTime, resource->responseReceivedTime, resource->endTime); - - if (!redirectResponse.isNull()) - updateScriptResourceResponse(resource); - } -} - -void InspectorController::didReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse& response) -{ - if (!enabled()) - return; - - InspectorResource* resource = m_resources.get(identifier).get(); - if (!resource) - return; - - updateResourceResponse(resource, response); - - resource->responseReceivedTime = currentTime(); - - if (windowVisible() && resource->scriptObject) { - updateScriptResourceResponse(resource); - updateScriptResource(resource, resource->startTime, resource->responseReceivedTime, resource->endTime); - } -} - -void InspectorController::didReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived) -{ - if (!enabled()) - return; - - InspectorResource* resource = m_resources.get(identifier).get(); - if (!resource) - return; - - resource->length += lengthReceived; - - if (windowVisible() && resource->scriptObject) - updateScriptResource(resource, resource->length); -} - -void InspectorController::didFinishLoading(DocumentLoader* loader, unsigned long identifier) -{ - if (!enabled()) - return; - - RefPtr<InspectorResource> resource = m_resources.get(identifier); - if (!resource) - return; - - removeResource(resource.get()); - - resource->finished = true; - resource->endTime = currentTime(); - - addResource(resource.get()); - - if (windowVisible() && resource->scriptObject) { - updateScriptResource(resource.get(), resource->startTime, resource->responseReceivedTime, resource->endTime); - updateScriptResource(resource.get(), resource->finished); - } -} - -void InspectorController::didFailLoading(DocumentLoader* loader, unsigned long identifier, const ResourceError& /*error*/) -{ - if (!enabled()) - return; - - RefPtr<InspectorResource> resource = m_resources.get(identifier); - if (!resource) - return; - - removeResource(resource.get()); - - resource->finished = true; - resource->failed = true; - resource->endTime = currentTime(); - - addResource(resource.get()); - - if (windowVisible() && resource->scriptObject) { - updateScriptResource(resource.get(), resource->startTime, resource->responseReceivedTime, resource->endTime); - updateScriptResource(resource.get(), resource->finished, resource->failed); - } -} - -#if ENABLE(DATABASE) -void InspectorController::didOpenDatabase(Database* database, const String& domain, const String& name, const String& version) -{ - if (!enabled()) - return; - - InspectorDatabaseResource* resource = new InspectorDatabaseResource(database, domain, name, version); - - m_databaseResources.add(resource); - - if (windowVisible()) - addDatabaseScriptResource(resource); -} -#endif - -void InspectorController::moveWindowBy(float x, float y) const -{ - if (!m_page || !enabled()) - return; - - FloatRect frameRect = m_page->chrome()->windowRect(); - frameRect.move(x, y); - m_page->chrome()->setWindowRect(frameRect); -} - -void InspectorController::drawNodeHighlight(GraphicsContext& context) const -{ - static const Color overlayFillColor(0, 0, 0, 128); - static const int outlineThickness = 1; - - if (!m_highlightedNode) - return; - - RenderObject* renderer = m_highlightedNode->renderer(); - if (!renderer) - return; - IntRect nodeRect(renderer->absoluteBoundingBoxRect()); - - Vector<IntRect> rects; - if (renderer->isInline() || (renderer->isText() && !m_highlightedNode->isSVGElement())) - renderer->addLineBoxRects(rects); - if (rects.isEmpty()) - rects.append(nodeRect); - - FrameView* view = m_inspectedPage->mainFrame()->view(); - FloatRect overlayRect = static_cast<ScrollView*>(view)->visibleContentRect(); - - if (!overlayRect.contains(nodeRect) && !nodeRect.contains(enclosingIntRect(overlayRect))) { - Element* element; - if (m_highlightedNode->isElementNode()) - element = static_cast<Element*>(m_highlightedNode.get()); - else - element = static_cast<Element*>(m_highlightedNode->parent()); - element->scrollIntoViewIfNeeded(); - overlayRect = static_cast<ScrollView*>(view)->visibleContentRect(); - } - - context.translate(-overlayRect.x(), -overlayRect.y()); - - // Draw translucent gray fill, out of which we will cut holes. - context.fillRect(overlayRect, overlayFillColor); - - // Draw white frames around holes in first pass, so they will be erased in - // places where holes overlap or abut. - for (size_t i = 0; i < rects.size(); ++i) { - IntRect rect = rects[i]; - rect.inflate(outlineThickness); - context.fillRect(rect, Color::white); - } - - // Erase holes in second pass. - for (size_t i = 0; i < rects.size(); ++i) - context.clearRect(rects[i]); -} - -} // namespace WebCore diff --git a/WebCore/page/InspectorController.h b/WebCore/page/InspectorController.h deleted file mode 100644 index 46a5df3..0000000 --- a/WebCore/page/InspectorController.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 InspectorController_h -#define InspectorController_h - -#include "Chrome.h" -#include <JavaScriptCore/JSContextRef.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class Database; -class DocumentLoader; -class GraphicsContext; -class InspectorClient; -class Node; -class ResourceResponse; -class ResourceError; - -struct ConsoleMessage; -struct InspectorDatabaseResource; -struct InspectorResource; -class ResourceRequest; - -class InspectorController { -public: - typedef HashMap<long long, RefPtr<InspectorResource> > ResourcesMap; - typedef HashMap<RefPtr<Frame>, ResourcesMap*> FrameResourcesMap; - typedef HashSet<RefPtr<InspectorDatabaseResource> > DatabaseResourcesSet; - - typedef enum { - FocusedNodeDocumentPanel, - ConsolePanel, - TimelinePanel - } SpecialPanels; - - InspectorController(Page*, InspectorClient*); - ~InspectorController(); - - void pageDestroyed() { m_page = 0; } - - bool enabled() const; - - Page* inspectedPage() const { return m_inspectedPage; } - - String localizedStringsURL(); - - void inspect(Node*); - void highlight(Node*); - void hideHighlight(); - - void show(); - void showConsole(); - void showTimeline(); - void close(); - - bool windowVisible(); - void setWindowVisible(bool visible = true); - - void addMessageToConsole(MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String& sourceID); - - void attachWindow(); - void detachWindow(); - - JSContextRef scriptContext() const { return m_scriptContext; }; - void setScriptContext(JSContextRef context) { m_scriptContext = context; }; - - void windowScriptObjectAvailable(); - - void scriptObjectReady(); - - void populateScriptResources(); - void clearScriptResources(); - - void didCommitLoad(DocumentLoader*); - void frameDetachedFromParent(Frame*); - - void didLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length); - - void identifierForInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&); - void willSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); - void didReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&); - void didReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived); - void didFinishLoading(DocumentLoader*, unsigned long identifier); - void didFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&); - -#if ENABLE(DATABASE) - void didOpenDatabase(Database*, const String& domain, const String& name, const String& version); -#endif - - const ResourcesMap& resources() const { return m_resources; } - - void moveWindowBy(float x, float y) const; - - void drawNodeHighlight(GraphicsContext&) const; - -private: - void focusNode(); - - void addScriptConsoleMessage(const ConsoleMessage*); - void clearScriptConsoleMessages(); - - void clearNetworkTimeline(); - void clearDatabaseScriptResources(); - - void addResource(InspectorResource*); - void removeResource(InspectorResource*); - - JSObjectRef addScriptResource(InspectorResource*); - void removeScriptResource(InspectorResource*); - - JSObjectRef addAndUpdateScriptResource(InspectorResource*); - void updateScriptResourceRequest(InspectorResource*); - void updateScriptResourceResponse(InspectorResource*); - void updateScriptResource(InspectorResource*, int length); - void updateScriptResource(InspectorResource*, bool finished, bool failed = false); - void updateScriptResource(InspectorResource*, double startTime, double responseReceivedTime, double endTime); - - void pruneResources(ResourcesMap*, DocumentLoader* loaderToKeep = 0); - void removeAllResources(ResourcesMap* map) { pruneResources(map); } - -#if ENABLE(DATABASE) - JSObjectRef addDatabaseScriptResource(InspectorDatabaseResource*); - void removeDatabaseScriptResource(InspectorDatabaseResource*); -#endif - - Page* m_inspectedPage; - InspectorClient* m_client; - Page* m_page; - RefPtr<Node> m_nodeToFocus; - RefPtr<InspectorResource> m_mainResource; - ResourcesMap m_resources; - FrameResourcesMap m_frameResources; - Vector<ConsoleMessage*> m_consoleMessages; -#if ENABLE(DATABASE) - DatabaseResourcesSet m_databaseResources; -#endif - JSObjectRef m_scriptObject; - JSObjectRef m_controllerScriptObject; - JSContextRef m_scriptContext; - bool m_windowVisible; - SpecialPanels m_showAfterVisible; - long long m_nextIdentifier; - RefPtr<Node> m_highlightedNode; -}; - -} // namespace WebCore - -#endif // !defined(InspectorController_h) diff --git a/WebCore/page/Location.cpp b/WebCore/page/Location.cpp new file mode 100644 index 0000000..454aa78 --- /dev/null +++ b/WebCore/page/Location.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "Location.h" + +#include "Frame.h" +#include "FrameLoader.h" +#include "KURL.h" +#include "PlatformString.h" + +namespace WebCore { + +Location::Location(Frame* frame) + : m_frame(frame) +{ +} + +void Location::disconnectFrame() +{ + m_frame = 0; +} + +inline const KURL& Location::url() const +{ + ASSERT(m_frame); + return m_frame->loader()->url(); +} + +String Location::href() const +{ + if (!m_frame) + return String(); + + const KURL& url = this->url(); + return url.hasPath() ? url.prettyURL() : url.prettyURL() + "/"; +} + +String Location::protocol() const +{ + if (!m_frame) + return String(); + + return url().protocol() + ":"; +} + +String Location::host() const +{ + if (!m_frame) + return String(); + + // Note: this is the IE spec. The NS spec swaps the two, it says + // "The hostname property is the concatenation of the host and port properties, separated by a colon." + const KURL& url = this->url(); + return url.port() ? url.host() + ":" + String::number((static_cast<int>(url.port()))) : url.host(); +} + +String Location::hostname() const +{ + if (!m_frame) + return String(); + + return url().host(); +} + +String Location::port() const +{ + if (!m_frame) + return String(); + + const KURL& url = this->url(); + return url.port() ? String::number(static_cast<int>(url.port())) : ""; +} + +String Location::pathname() const +{ + if (!m_frame) + return String(); + + const KURL& url = this->url(); + return url.path().isEmpty() ? "/" : url.path(); +} + +String Location::search() const +{ + if (!m_frame) + return String(); + + return url().query(); +} + +String Location::hash() const +{ + if (!m_frame) + return String(); + + const KURL& url = this->url(); + return url.ref().isNull() ? "" : "#" + url.ref(); +} + +String Location::toString() const +{ + if (!m_frame) + return String(); + + const KURL& url = this->url(); + return url.hasPath() ? url.prettyURL() : url.prettyURL() + "/"; +} + +} // namespace WebCore diff --git a/WebCore/page/Location.h b/WebCore/page/Location.h new file mode 100644 index 0000000..065bde1 --- /dev/null +++ b/WebCore/page/Location.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 Location_h +#define Location_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Frame; + class KURL; + class String; + + class Location : public RefCounted<Location> { + public: + static PassRefPtr<Location> create(Frame* frame) { return adoptRef(new Location(frame)); } + + Frame* frame() const { return m_frame; } + void disconnectFrame(); + + String href() const; + + // URI decomposition attributes + String protocol() const; + String host() const; + String hostname() const; + String port() const; + String pathname() const; + String search() const; + String hash() const; + + String toString() const; + + private: + Location(Frame*); + + const KURL& url() const; + + Frame* m_frame; + }; + +} // namespace WebCore + +#endif // Location_h diff --git a/WebCore/page/Location.idl b/WebCore/page/Location.idl new file mode 100644 index 0000000..91822ab --- /dev/null +++ b/WebCore/page/Location.idl @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 window { + + interface [ + CustomGetOwnPropertySlot, + CustomPutFunction, + CustomDeleteProperty, + CustomGetPropertyNames + ] Location { + attribute [CustomSetter] DOMString href; + + [Custom] void assign(in DOMString url); + [Custom] void replace(in DOMString url); + [Custom] void reload(); + + // URI decomposition attributes + attribute [CustomSetter] DOMString protocol; + attribute [CustomSetter] DOMString host; + attribute [CustomSetter] DOMString hostname; + attribute [CustomSetter] DOMString port; + attribute [CustomSetter] DOMString pathname; + attribute [CustomSetter] DOMString search; + attribute [CustomSetter] DOMString hash; + +#if defined(LANGUAGE_JAVASCRIPT) + [DontEnum, Custom] DOMString toString(); +#endif + }; + +} diff --git a/WebCore/page/MouseEventWithHitTestResults.cpp b/WebCore/page/MouseEventWithHitTestResults.cpp index d7596ce..0fc8c17 100644 --- a/WebCore/page/MouseEventWithHitTestResults.cpp +++ b/WebCore/page/MouseEventWithHitTestResults.cpp @@ -61,7 +61,7 @@ const IntPoint MouseEventWithHitTestResults::localPoint() const return m_hitTestResult.localPoint(); } -PlatformScrollbar* MouseEventWithHitTestResults::scrollbar() const +Scrollbar* MouseEventWithHitTestResults::scrollbar() const { return m_hitTestResult.scrollbar(); } diff --git a/WebCore/page/MouseEventWithHitTestResults.h b/WebCore/page/MouseEventWithHitTestResults.h index 8392702..c4e419c 100644 --- a/WebCore/page/MouseEventWithHitTestResults.h +++ b/WebCore/page/MouseEventWithHitTestResults.h @@ -26,7 +26,7 @@ namespace WebCore { -class PlatformScrollbar; +class Scrollbar; // FIXME: Why doesn't this class just cache a HitTestResult instead of copying all of HitTestResult's fields over? class MouseEventWithHitTestResults { @@ -37,8 +37,9 @@ public: const HitTestResult& hitTestResult() const { return m_hitTestResult; } Node* targetNode() const; const IntPoint localPoint() const; - PlatformScrollbar* scrollbar() const; + Scrollbar* scrollbar() const; bool isOverLink() const; + bool isOverWidget() const { return m_hitTestResult.isOverWidget(); } private: PlatformMouseEvent m_event; diff --git a/WebCore/page/Navigator.cpp b/WebCore/page/Navigator.cpp new file mode 100644 index 0000000..6045062 --- /dev/null +++ b/WebCore/page/Navigator.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2000 Harri Porten (porten@kde.org) + * Copyright (c) 2000 Daniel Molkentin (molkentin@kde.org) + * Copyright (c) 2000 Stefan Schimanski (schimmi@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "Navigator.h" + +#include "CookieJar.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "Geolocation.h" +#include "Language.h" +#include "MimeTypeArray.h" +#include "NetworkStateNotifier.h" +#include "Page.h" +#include "PlatformString.h" +#include "PluginArray.h" +#include "PluginData.h" +#include "ScriptController.h" +#include "Settings.h" + +#ifndef WEBCORE_NAVIGATOR_PLATFORM +#if PLATFORM(MAC) && PLATFORM(PPC) +#define WEBCORE_NAVIGATOR_PLATFORM "MacPPC" +#elif PLATFORM(MAC) && PLATFORM(X86) +#define WEBCORE_NAVIGATOR_PLATFORM "MacIntel" +#elif PLATFORM(WIN_OS) +#define WEBCORE_NAVIGATOR_PLATFORM "Win32" +#elif PLATFORM(ANDROID) +#define WEBCORE_NAVIGATOR_PLATFORM "Android" +#else +#define WEBCORE_NAVIGATOR_PLATFORM "" +#endif +#endif // ifndef WEBCORE_NAVIGATOR_PLATFORM + +#ifndef WEBCORE_NAVIGATOR_PRODUCT +#define WEBCORE_NAVIGATOR_PRODUCT "Gecko" +#endif // ifndef WEBCORE_NAVIGATOR_PRODUCT + +#ifndef WEBCORE_NAVIGATOR_PRODUCT_SUB +#define WEBCORE_NAVIGATOR_PRODUCT_SUB "20030107" +#endif // ifndef WEBCORE_NAVIGATOR_PRODUCT_SUB + +#ifndef WEBCORE_NAVIGATOR_VENDOR +#define WEBCORE_NAVIGATOR_VENDOR "Apple Computer, Inc." +#endif // ifndef WEBCORE_NAVIGATOR_VENDOR + +#ifndef WEBCORE_NAVIGATOR_VENDOR_SUB +#define WEBCORE_NAVIGATOR_VENDOR_SUB "" +#endif // ifndef WEBCORE_NAVIGATOR_VENDOR_SUB + + +namespace WebCore { + +Navigator::Navigator(Frame* frame) + : m_frame(frame) +{ +} + +Navigator::~Navigator() +{ + disconnectFrame(); +} + +void Navigator::disconnectFrame() +{ + if (m_plugins) { + m_plugins->disconnectFrame(); + m_plugins = 0; + } + if (m_mimeTypes) { + m_mimeTypes->disconnectFrame(); + m_mimeTypes = 0; + } + if (m_geolocation) { + m_geolocation->disconnectFrame(); + m_geolocation = 0; + } + m_frame = 0; +} + +String Navigator::appCodeName() const +{ + return "Mozilla"; +} + +String Navigator::appName() const +{ + return "Netscape"; +} + +// If this function returns true, we need to hide the substring "4." that would otherwise +// appear in the appVersion string. This is to avoid problems with old versions of a +// library called OpenCube QuickMenu, which as of this writing is still being used on +// sites such as nwa.com -- the library thinks Safari is Netscape 4 if we don't do this! +static bool shouldHideFourDot(Frame* frame) +{ + const String* sourceURL = frame->script()->sourceURL(); + if (!sourceURL) + return false; + if (!(sourceURL->endsWith("/dqm_script.js") || sourceURL->endsWith("/dqm_loader.js"))) + return false; + Settings* settings = frame->settings(); + if (!settings) + return false; + return settings->needsSiteSpecificQuirks(); +} + +String Navigator::appVersion() const +{ + if (!m_frame) + return String(); + // Version is everything in the user agent string past the "Mozilla/" prefix. + const String& userAgent = m_frame->loader()->userAgent(m_frame->document() ? m_frame->document()->url() : KURL()); + String appVersion = userAgent.substring(userAgent.find('/') + 1); + if (shouldHideFourDot(m_frame)) + appVersion.replace("4.", "4_"); + return appVersion; +} + +String Navigator::language() const +{ + return defaultLanguage(); +} + +String Navigator::userAgent() const +{ + if (!m_frame) + return String(); + return m_frame->loader()->userAgent(m_frame->document() ? m_frame->document()->url() : KURL()); +} + +String Navigator::platform() const +{ + return WEBCORE_NAVIGATOR_PLATFORM; +} + +PluginArray* Navigator::plugins() const +{ + if (!m_plugins) + m_plugins = PluginArray::create(m_frame); + return m_plugins.get(); +} + +MimeTypeArray* Navigator::mimeTypes() const +{ + if (!m_mimeTypes) + m_mimeTypes = MimeTypeArray::create(m_frame); + return m_mimeTypes.get(); +} + +String Navigator::product() const +{ + return WEBCORE_NAVIGATOR_PRODUCT; +} + +String Navigator::productSub() const +{ + return WEBCORE_NAVIGATOR_PRODUCT_SUB; +} + +String Navigator::vendor() const +{ + return WEBCORE_NAVIGATOR_VENDOR; +} + +String Navigator::vendorSub() const +{ + return WEBCORE_NAVIGATOR_VENDOR_SUB; +} + +bool Navigator::cookieEnabled() const +{ + if (m_frame->page() && !m_frame->page()->cookieEnabled()) + return false; + + return cookiesEnabled(m_frame->document()); +} + +bool Navigator::javaEnabled() const +{ + if (!m_frame) + return false; + return m_frame->settings()->isJavaEnabled(); +} + +bool Navigator::onLine() const +{ + return networkStateNotifier().onLine(); +} + +Geolocation* Navigator::geolocation() const +{ + if (!m_geolocation) + m_geolocation = Geolocation::create(m_frame); + return m_geolocation.get(); +} + +} // namespace WebCore diff --git a/WebCore/page/Navigator.h b/WebCore/page/Navigator.h new file mode 100644 index 0000000..ea5d780 --- /dev/null +++ b/WebCore/page/Navigator.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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 Navigator_h +#define Navigator_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class Frame; + class Geolocation; + class MimeTypeArray; + class PluginData; + class PluginArray; + class String; + + class Navigator : public RefCounted<Navigator> { + public: + static PassRefPtr<Navigator> create(Frame* frame) { return adoptRef(new Navigator(frame)); } + ~Navigator(); + + void disconnectFrame(); + Frame* frame() const { return m_frame; } + + String appCodeName() const; + String appName() const; + String appVersion() const; + String language() const; + String userAgent() const; + String platform() const; + PluginArray* plugins() const; + MimeTypeArray* mimeTypes() const; + String product() const; + String productSub() const; + String vendor() const; + String vendorSub() const; + bool cookieEnabled() const; + bool javaEnabled() const; + + bool onLine() const; + Geolocation* geolocation() const; + // This is used for GC marking. + Geolocation* optionalGeolocation() const { return m_geolocation.get(); } + + private: + Navigator(Frame*); + Frame* m_frame; + mutable RefPtr<PluginArray> m_plugins; + mutable RefPtr<MimeTypeArray> m_mimeTypes; + mutable RefPtr<Geolocation> m_geolocation; + }; + +} + +#endif diff --git a/WebCore/page/Navigator.idl b/WebCore/page/Navigator.idl new file mode 100644 index 0000000..905159c --- /dev/null +++ b/WebCore/page/Navigator.idl @@ -0,0 +1,46 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + + 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. +*/ + +module window { + + interface [ + CustomMarkFunction + ] Navigator { + readonly attribute DOMString appCodeName; + readonly attribute DOMString appName; + readonly attribute [CustomGetter] DOMString appVersion; + readonly attribute DOMString language; + readonly attribute DOMString userAgent; + readonly attribute DOMString platform; + readonly attribute PluginArray plugins; + readonly attribute MimeTypeArray mimeTypes; + readonly attribute DOMString product; + readonly attribute DOMString productSub; + readonly attribute DOMString vendor; + readonly attribute DOMString vendorSub; + readonly attribute boolean cookieEnabled; + boolean javaEnabled(); + + readonly attribute boolean onLine; +#if ENABLE_GEOLOCATION + readonly attribute Geolocation geolocation; +#endif + }; + +} diff --git a/WebCore/page/Page.cpp b/WebCore/page/Page.cpp index efae3bf..3c573d6 100644 --- a/WebCore/page/Page.cpp +++ b/WebCore/page/Page.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All Rights Reserved. + * Copyright (C) 2006, 2007, 2008 Apple 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 @@ -24,8 +24,11 @@ #include "ChromeClient.h" #include "ContextMenuClient.h" #include "ContextMenuController.h" +#include "CSSStyleSelector.h" #include "EditorClient.h" +#include "DOMWindow.h" #include "DragController.h" +#include "EventNames.h" #include "FileSystem.h" #include "FocusController.h" #include "Frame.h" @@ -35,6 +38,10 @@ #include "HistoryItem.h" #include "InspectorController.h" #include "Logging.h" +#include "NetworkStateNotifier.h" +#include "Navigator.h" +#include "PageGroup.h" +#include "PluginData.h" #include "ProgressTracker.h" #include "RenderWidget.h" #include "SelectionController.h" @@ -42,32 +49,58 @@ #include "StringHash.h" #include "TextResourceDecoder.h" #include "Widget.h" +#include "ScriptController.h" #include <kjs/collector.h> -#include <kjs/JSLock.h> +#include <runtime/JSLock.h> #include <wtf/HashMap.h> +#include <wtf/RefCountedLeakCounter.h> -using namespace KJS; +#if ENABLE(DOM_STORAGE) +#include "LocalStorage.h" +#include "SessionStorage.h" +#include "StorageArea.h" +#endif + +#if ENABLE(JAVASCRIPT_DEBUGGER) +#include "JavaScriptDebugServer.h" +#endif namespace WebCore { static HashSet<Page*>* allPages; -static HashMap<String, HashSet<Page*>*>* frameNamespaces; #ifndef NDEBUG -WTFLogChannel LogWebCorePageLeaks = { 0x00000000, "", WTFLogChannelOn }; - -struct PageCounter { - static int count; - ~PageCounter() - { - if (count) - LOG(WebCorePageLeaks, "LEAK: %d Page\n", count); - } -}; -int PageCounter::count = 0; -static PageCounter pageCounter; +static WTF::RefCountedLeakCounter pageCounter("Page"); #endif +static void networkStateChanged() +{ + Vector<RefPtr<Frame> > frames; + + // Get all the frames of all the pages in all the page groups + HashSet<Page*>::iterator end = allPages->end(); + for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) { + for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) + frames.append(frame); + } + + AtomicString eventName = networkStateNotifier().onLine() ? eventNames().onlineEvent : eventNames().offlineEvent; + + for (unsigned i = 0; i < frames.size(); i++) { + Document* document = frames[i]->document(); + + if (!document) + continue; + + // If the document does not have a body the event should be dispatched to the document + EventTargetNode* eventTarget = document->body(); + if (!eventTarget) + eventTarget = document; + + eventTarget->dispatchEventForType(eventName, false, false); + } +} + Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, EditorClient* editorClient, DragClient* dragClient, InspectorClient* inspectorClient) : m_chrome(new Chrome(this, chromeClient)) , m_dragCaretController(new SelectionController(0, true)) @@ -77,26 +110,38 @@ Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, Edi , m_inspectorController(new InspectorController(this, inspectorClient)) , m_settings(new Settings(this)) , m_progress(new ProgressTracker) - , m_backForwardList(new BackForwardList(this)) + , m_backForwardList(BackForwardList::create(this)) , m_editorClient(editorClient) , m_frameCount(0) , m_tabKeyCyclesThroughElements(true) , m_defersLoading(false) , m_inLowQualityInterpolationMode(false) + , m_cookieEnabled(true) , m_parentInspectorController(0) , m_didLoadUserStyleSheet(false) , m_userStyleSheetModificationTime(0) + , m_group(0) + , m_debugger(0) + , m_pendingUnloadEventCount(0) + , m_pendingBeforeUnloadEventCount(0) + , m_customHTMLTokenizerTimeDelay(-1) + , m_customHTMLTokenizerChunkSize(-1) { if (!allPages) { allPages = new HashSet<Page*>; - setFocusRingColorChangeFunction(setNeedsReapplyStyles); + + networkStateNotifier().setNetworkStateChangedFunction(networkStateChanged); } ASSERT(!allPages->contains(this)); allPages->add(this); +#if ENABLE(JAVASCRIPT_DEBUGGER) + JavaScriptDebugServer::shared().pageCreated(this); +#endif + #ifndef NDEBUG - ++PageCounter::count; + pageCounter.increment(); #endif } @@ -106,15 +151,20 @@ Page::~Page() setGroupName(String()); allPages->remove(this); - for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) + for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (frame->document()) + frame->document()->documentWillBecomeInactive(); frame->pageDestroyed(); + } m_editorClient->pageDestroyed(); - m_inspectorController->pageDestroyed(); + if (m_parentInspectorController) + m_parentInspectorController->pageDestroyed(); + m_inspectorController->inspectedPageDestroyed(); m_backForwardList->close(); #ifndef NDEBUG - --PageCounter::count; + pageCounter.decrement(); // Cancel keepAlive timers, to ensure we release all Frames before exiting. // It's safe to do this because we prohibit closing a Page while JavaScript @@ -165,37 +215,33 @@ void Page::goToItem(HistoryItem* item, FrameLoadType type) void Page::setGroupName(const String& name) { - if (frameNamespaces && !m_groupName.isEmpty()) { - HashSet<Page*>* oldNamespace = frameNamespaces->get(m_groupName); - if (oldNamespace) { - oldNamespace->remove(this); - if (oldNamespace->isEmpty()) { - frameNamespaces->remove(m_groupName); - delete oldNamespace; - } - } + if (m_group && !m_group->name().isEmpty()) { + ASSERT(m_group != m_singlePageGroup.get()); + ASSERT(!m_singlePageGroup); + m_group->removePage(this); } - m_groupName = name; - if (!name.isEmpty()) { - if (!frameNamespaces) - frameNamespaces = new HashMap<String, HashSet<Page*>*>; - HashSet<Page*>* newNamespace = frameNamespaces->get(name); - if (!newNamespace) { - newNamespace = new HashSet<Page*>; - frameNamespaces->add(name, newNamespace); - } - newNamespace->add(this); + + if (name.isEmpty()) + m_group = 0; + else { + m_singlePageGroup.clear(); + m_group = PageGroup::pageGroup(name); + m_group->addPage(this); } } -const HashSet<Page*>* Page::frameNamespace() const +const String& Page::groupName() const { - return (frameNamespaces && !m_groupName.isEmpty()) ? frameNamespaces->get(m_groupName) : 0; + static String nullString; + return m_group ? m_group->name() : nullString; } -const HashSet<Page*>* Page::frameNamespace(const String& groupName) +void Page::initGroup() { - return (frameNamespaces && !groupName.isEmpty()) ? frameNamespaces->get(groupName) : 0; + ASSERT(!m_singlePageGroup); + ASSERT(!m_group); + m_singlePageGroup.set(new PageGroup(this)); + m_group = m_singlePageGroup.get(); } void Page::setNeedsReapplyStyles() @@ -208,6 +254,38 @@ void Page::setNeedsReapplyStyles() frame->setNeedsReapplyStyles(); } +void Page::refreshPlugins(bool reload) +{ + if (!allPages) + return; + + PluginData::refresh(); + + Vector<RefPtr<Frame> > framesNeedingReload; + + HashSet<Page*>::iterator end = allPages->end(); + for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) { + (*it)->m_pluginData = 0; + + if (reload) { + for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (frame->loader()->containsPlugins()) + framesNeedingReload.append(frame); + } + } + } + + for (size_t i = 0; i < framesNeedingReload.size(); ++i) + framesNeedingReload[i]->loader()->reload(); +} + +PluginData* Page::pluginData() const +{ + if (!m_pluginData) + m_pluginData = PluginData::create(this); + return m_pluginData.get(); +} + static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag) { return forward @@ -225,7 +303,7 @@ bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, do { if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) { if (frame != startFrame) - startFrame->selectionController()->clear(); + startFrame->selection()->clear(); focusController()->setFocusedFrame(frame); return true; } @@ -234,7 +312,7 @@ bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, // Search contents of startFrame, on the other side of the selection that we did earlier. // We cheat a bit and just research with wrap on - if (shouldWrap && !startFrame->selectionController()->isNone()) { + if (shouldWrap && !startFrame->selection()->isNone()) { bool found = startFrame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true); focusController()->setFocusedFrame(frame); return found; @@ -275,7 +353,7 @@ void Page::unmarkAllTextMatches() const Selection& Page::selection() const { - return focusController()->focusedOrMainFrame()->selectionController()->selection(); + return focusController()->focusedOrMainFrame()->selection()->selection(); } void Page::setDefersLoading(bool defers) @@ -355,9 +433,150 @@ const String& Page::userStyleSheet() const if (!data) return m_userStyleSheet; - m_userStyleSheet = TextResourceDecoder("text/css").decode(data->data(), data->size()); + m_userStyleSheet = TextResourceDecoder::create("text/css")->decode(data->data(), data->size()); return m_userStyleSheet; } +void Page::removeAllVisitedLinks() +{ + if (!allPages) + return; + HashSet<PageGroup*> groups; + HashSet<Page*>::iterator pagesEnd = allPages->end(); + for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { + if (PageGroup* group = (*it)->groupPtr()) + groups.add(group); + } + HashSet<PageGroup*>::iterator groupsEnd = groups.end(); + for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it) + (*it)->removeVisitedLinks(); +} + +void Page::allVisitedStateChanged(PageGroup* group) +{ + ASSERT(group); + ASSERT(allPages); + HashSet<Page*>::iterator pagesEnd = allPages->end(); + for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { + Page* page = *it; + if (page->m_group != group) + continue; + for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) { + if (CSSStyleSelector* styleSelector = frame->document()->styleSelector()) + styleSelector->allVisitedStateChanged(); + } + } +} + +void Page::visitedStateChanged(PageGroup* group, unsigned visitedLinkHash) +{ + ASSERT(group); + ASSERT(allPages); + HashSet<Page*>::iterator pagesEnd = allPages->end(); + for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { + Page* page = *it; + if (page->m_group != group) + continue; + for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) { + if (CSSStyleSelector* styleSelector = frame->document()->styleSelector()) + styleSelector->visitedStateChanged(visitedLinkHash); + } + } +} + +void Page::setDebuggerForAllPages(JSC::Debugger* debugger) +{ + ASSERT(allPages); + + HashSet<Page*>::iterator end = allPages->end(); + for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) + (*it)->setDebugger(debugger); +} + +void Page::setDebugger(JSC::Debugger* debugger) +{ + if (m_debugger == debugger) + return; + + m_debugger = debugger; + + for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) + frame->script()->attachDebugger(m_debugger); +} + +#if ENABLE(DOM_STORAGE) +SessionStorage* Page::sessionStorage(bool optionalCreate) +{ + if (!m_sessionStorage && optionalCreate) + m_sessionStorage = SessionStorage::create(this); + + return m_sessionStorage.get(); +} + +void Page::setSessionStorage(PassRefPtr<SessionStorage> newStorage) +{ + ASSERT(newStorage->page() == this); + m_sessionStorage = newStorage; +} +#endif + +unsigned Page::pendingUnloadEventCount() +{ + return m_pendingUnloadEventCount; +} + +void Page::changePendingUnloadEventCount(int delta) +{ + if (!delta) + return; + ASSERT( (delta + (int)m_pendingUnloadEventCount) >= 0 ); + + if (m_pendingUnloadEventCount == 0) + m_chrome->disableSuddenTermination(); + else if ((m_pendingUnloadEventCount + delta) == 0) + m_chrome->enableSuddenTermination(); + + m_pendingUnloadEventCount += delta; + return; +} + +unsigned Page::pendingBeforeUnloadEventCount() +{ + return m_pendingBeforeUnloadEventCount; +} + +void Page::changePendingBeforeUnloadEventCount(int delta) +{ + if (!delta) + return; + ASSERT( (delta + (int)m_pendingBeforeUnloadEventCount) >= 0 ); + + if (m_pendingBeforeUnloadEventCount == 0) + m_chrome->disableSuddenTermination(); + else if ((m_pendingBeforeUnloadEventCount + delta) == 0) + m_chrome->enableSuddenTermination(); + + m_pendingBeforeUnloadEventCount += delta; + return; +} + +void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay) +{ + if (customHTMLTokenizerTimeDelay < 0) { + m_customHTMLTokenizerTimeDelay = -1; + return; + } + m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay; +} + +void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize) +{ + if (customHTMLTokenizerChunkSize < 0) { + m_customHTMLTokenizerChunkSize = -1; + return; + } + m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize; +} + } // namespace WebCore diff --git a/WebCore/page/Page.h b/WebCore/page/Page.h index 501890f..9c00ba7 100644 --- a/WebCore/page/Page.h +++ b/WebCore/page/Page.h @@ -1,6 +1,5 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006, 2008 Apple 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 @@ -26,34 +25,19 @@ #include "ContextMenuController.h" #include "FrameLoaderTypes.h" #include "PlatformString.h" +#if PLATFORM(MAC) +#include "SchedulePair.h" +#endif #include <wtf/HashSet.h> #include <wtf/OwnPtr.h> -#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS)) +#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS)) || (PLATFORM(QT) && defined(Q_WS_WIN)) typedef struct HINSTANCE__* HINSTANCE; #endif -#ifdef ANDROID_FIX -enum TextCaseSensitivity { - TextCaseSensitive, - TextCaseInsensitive -}; - -enum FindDirection { - FindDirectionForward, - FindDirectionBackward -}; -#else -typedef enum TextCaseSensitivity { - TextCaseSensitive, - TextCaseInsensitive -}; - -typedef enum FindDirection { - FindDirectionForward, - FindDirectionBackward -}; -#endif +namespace JSC { + class Debugger; +} namespace WebCore { @@ -61,6 +45,7 @@ namespace WebCore { class ChromeClient; class ContextMenuClient; class ContextMenuController; + class Document; class DragClient; class DragController; class EditorClient; @@ -69,19 +54,29 @@ namespace WebCore { class InspectorClient; class InspectorController; class Node; + class PageGroup; + class PluginData; class ProgressTracker; class Selection; class SelectionController; +#if ENABLE(DOM_STORAGE) + class SessionStorage; +#endif class Settings; + enum TextCaseSensitivity { TextCaseSensitive, TextCaseInsensitive }; + enum FindDirection { FindDirectionForward, FindDirectionBackward }; + class Page : Noncopyable { public: static void setNeedsReapplyStyles(); - static const HashSet<Page*>* frameNamespace(const String&); Page(ChromeClient*, ContextMenuClient*, EditorClient*, DragClient*, InspectorClient*); ~Page(); + static void refreshPlugins(bool reload); + PluginData* pluginData() const; + EditorClient* editorClient() const { return m_editorClient; } void setMainFrame(PassRefPtr<Frame>); @@ -98,9 +93,10 @@ namespace WebCore { void goToItem(HistoryItem*, FrameLoadType); void setGroupName(const String&); - String groupName() const { return m_groupName; } + const String& groupName() const; - const HashSet<Page*>* frameNamespace() const; + PageGroup& group() { if (!m_group) initGroup(); return *m_group; } + PageGroup* groupPtr() { return m_group; } // can return 0 void incrementFrameCount() { ++m_frameCount; } void decrementFrameCount() { --m_frameCount; } @@ -125,6 +121,14 @@ namespace WebCore { unsigned int markAllMatchesForText(const String&, TextCaseSensitivity, bool shouldHighlight, unsigned); void unmarkAllTextMatches(); +#if PLATFORM(MAC) + void addSchedulePair(PassRefPtr<SchedulePair>); + void removeSchedulePair(PassRefPtr<SchedulePair>); + SchedulePairHashSet* scheduledRunLoopPairs() { return m_scheduledRunLoopPairs.get(); } + + OwnPtr<SchedulePairHashSet> m_scheduledRunLoopPairs; +#endif + const Selection& selection() const; void setDefersLoading(bool); @@ -135,16 +139,48 @@ namespace WebCore { bool inLowQualityImageInterpolationMode() const; void setInLowQualityImageInterpolationMode(bool = true); + bool cookieEnabled() const { return m_cookieEnabled; } + void setCookieEnabled(bool enabled) { m_cookieEnabled = enabled; } + void userStyleSheetLocationChanged(); const String& userStyleSheet() const; + + void changePendingUnloadEventCount(int delta); + unsigned pendingUnloadEventCount(); + void changePendingBeforeUnloadEventCount(int delta); + unsigned pendingBeforeUnloadEventCount(); + + static void setDebuggerForAllPages(JSC::Debugger*); + void setDebugger(JSC::Debugger*); + JSC::Debugger* debugger() const { return m_debugger; } -#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS)) +#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS)) || (PLATFORM(QT) && defined(Q_WS_WIN)) // The global DLL or application instance used for all windows. static void setInstanceHandle(HINSTANCE instanceHandle) { s_instanceHandle = instanceHandle; } static HINSTANCE instanceHandle() { return s_instanceHandle; } #endif + static void removeAllVisitedLinks(); + + static void allVisitedStateChanged(PageGroup*); + static void visitedStateChanged(PageGroup*, unsigned visitedHash); + +#if ENABLE(DOM_STORAGE) + SessionStorage* sessionStorage(bool optionalCreate = true); + void setSessionStorage(PassRefPtr<SessionStorage>); +#endif + + void setCustomHTMLTokenizerTimeDelay(double); + bool hasCustomHTMLTokenizerTimeDelay() const { return m_customHTMLTokenizerTimeDelay != -1; } + double customHTMLTokenizerTimeDelay() const { ASSERT(m_customHTMLTokenizerTimeDelay != -1); return m_customHTMLTokenizerTimeDelay; } + + void setCustomHTMLTokenizerChunkSize(int); + bool hasCustomHTMLTokenizerChunkSize() const { return m_customHTMLTokenizerChunkSize != -1; } + int customHTMLTokenizerChunkSize() const { ASSERT(m_customHTMLTokenizerChunkSize != -1); return m_customHTMLTokenizerChunkSize; } + private: + void initGroup(); + OwnPtr<Chrome> m_chrome; OwnPtr<SelectionController> m_dragCaretController; OwnPtr<DragController> m_dragController; @@ -156,7 +192,8 @@ namespace WebCore { RefPtr<BackForwardList> m_backForwardList; RefPtr<Frame> m_mainFrame; - RefPtr<Node> m_focusedNode; + + mutable RefPtr<PluginData> m_pluginData; EditorClient* m_editorClient; @@ -167,6 +204,7 @@ namespace WebCore { bool m_defersLoading; bool m_inLowQualityInterpolationMode; + bool m_cookieEnabled; InspectorController* m_parentInspectorController; @@ -175,7 +213,21 @@ namespace WebCore { mutable bool m_didLoadUserStyleSheet; mutable time_t m_userStyleSheetModificationTime; -#if PLATFORM(WIN) || (PLATFORM(WX) && defined(__WXMSW__)) + OwnPtr<PageGroup> m_singlePageGroup; + PageGroup* m_group; + + JSC::Debugger* m_debugger; + + unsigned m_pendingUnloadEventCount; + unsigned m_pendingBeforeUnloadEventCount; + + double m_customHTMLTokenizerTimeDelay; + int m_customHTMLTokenizerChunkSize; + +#if ENABLE(DOM_STORAGE) + RefPtr<SessionStorage> m_sessionStorage; +#endif +#if PLATFORM(WIN) || (PLATFORM(WX) && defined(__WXMSW__)) || (PLATFORM(QT) && defined(Q_WS_WIN)) static HINSTANCE s_instanceHandle; #endif }; diff --git a/WebCore/page/PageGroup.cpp b/WebCore/page/PageGroup.cpp new file mode 100644 index 0000000..6b72be2 --- /dev/null +++ b/WebCore/page/PageGroup.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "PageGroup.h" + +#include "ChromeClient.h" +#include "Document.h" +#include "Page.h" +#include "Settings.h" + +#if ENABLE(DOM_STORAGE) +#include "LocalStorage.h" +#include "StorageArea.h" +#endif + +namespace WebCore { + +static unsigned getUniqueIdentifier() +{ + static unsigned currentIdentifier = 0; + return ++currentIdentifier; +} + +// -------- + +static bool shouldTrackVisitedLinks; + +PageGroup::PageGroup(const String& name) + : m_name(name) + , m_visitedLinksPopulated(false) + , m_identifier(getUniqueIdentifier()) +{ +} + +PageGroup::PageGroup(Page* page) + : m_visitedLinksPopulated(false) + , m_identifier(getUniqueIdentifier()) +{ + ASSERT(page); + addPage(page); +} + +typedef HashMap<String, PageGroup*> PageGroupMap; +static PageGroupMap* pageGroups = 0; + +PageGroup* PageGroup::pageGroup(const String& groupName) +{ + ASSERT(!groupName.isEmpty()); + + if (!pageGroups) + pageGroups = new PageGroupMap; + + pair<PageGroupMap::iterator, bool> result = pageGroups->add(groupName, 0); + + if (result.second) { + ASSERT(!result.first->second); + result.first->second = new PageGroup(groupName); + } + + ASSERT(result.first->second); + return result.first->second; +} + +void PageGroup::closeLocalStorage() +{ +#if ENABLE(DOM_STORAGE) + if (!pageGroups) + return; + + PageGroupMap::iterator end = pageGroups->end(); + + for (PageGroupMap::iterator it = pageGroups->begin(); it != end; ++it) { + if (LocalStorage* localStorage = it->second->localStorage()) + localStorage->close(); + } +#endif +} + +void PageGroup::addPage(Page* page) +{ + ASSERT(page); + ASSERT(!m_pages.contains(page)); + m_pages.add(page); +#if ENABLE(DOM_STORAGE) + if (!m_localStorage) + m_localStorage = LocalStorage::localStorage(page->settings()->localStorageDatabasePath()); +#endif +} + +void PageGroup::removePage(Page* page) +{ + ASSERT(page); + ASSERT(m_pages.contains(page)); + m_pages.remove(page); +} + +bool PageGroup::isLinkVisited(unsigned visitedLinkHash) +{ + if (!m_visitedLinksPopulated) { + m_visitedLinksPopulated = true; + ASSERT(!m_pages.isEmpty()); + (*m_pages.begin())->chrome()->client()->populateVisitedLinks(); + } + return m_visitedLinkHashes.contains(visitedLinkHash); +} + +inline void PageGroup::addVisitedLink(unsigned stringHash) +{ + ASSERT(shouldTrackVisitedLinks); + unsigned visitedLinkHash = AlreadyHashed::avoidDeletedValue(stringHash); + if (!m_visitedLinkHashes.add(visitedLinkHash).second) + return; + Page::visitedStateChanged(this, visitedLinkHash); +} + +void PageGroup::addVisitedLink(const KURL& url) +{ + if (!shouldTrackVisitedLinks) + return; + ASSERT(!url.isEmpty()); + addVisitedLink(url.string().impl()->hash()); +} + +void PageGroup::addVisitedLink(const UChar* characters, size_t length) +{ + if (!shouldTrackVisitedLinks) + return; + addVisitedLink(StringImpl::computeHash(characters, length)); +} + +void PageGroup::removeVisitedLinks() +{ + m_visitedLinksPopulated = false; + if (m_visitedLinkHashes.isEmpty()) + return; + m_visitedLinkHashes.clear(); + Page::allVisitedStateChanged(this); +} + +void PageGroup::removeAllVisitedLinks() +{ + Page::removeAllVisitedLinks(); +} + +void PageGroup::setShouldTrackVisitedLinks(bool shouldTrack) +{ + if (shouldTrackVisitedLinks == shouldTrack) + return; + shouldTrackVisitedLinks = shouldTrack; + if (!shouldTrackVisitedLinks) + removeAllVisitedLinks(); +} + +#if ENABLE(DOM_STORAGE) +LocalStorage* PageGroup::localStorage() +{ + return m_localStorage.get(); +} +#endif + +} // namespace WebCore diff --git a/WebCore/page/PageGroup.h b/WebCore/page/PageGroup.h new file mode 100644 index 0000000..b25892d --- /dev/null +++ b/WebCore/page/PageGroup.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 PageGroup_h +#define PageGroup_h + +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include "StringHash.h" + +namespace WebCore { + + class KURL; + class LocalStorage; + class Page; + + class PageGroup : Noncopyable { + public: + PageGroup(const String& name); + PageGroup(Page*); + + static PageGroup* pageGroup(const String& groupName); + static void closeLocalStorage(); + + const HashSet<Page*>& pages() const { return m_pages; } + + void addPage(Page*); + void removePage(Page*); + + bool isLinkVisited(unsigned visitedLinkHash); + + void addVisitedLink(const KURL&); + void addVisitedLink(const UChar*, size_t); + void removeVisitedLinks(); + + static void setShouldTrackVisitedLinks(bool); + static void removeAllVisitedLinks(); + + const String& name() { return m_name; } + unsigned identifier() { return m_identifier; } + +#if ENABLE(DOM_STORAGE) + LocalStorage* localStorage(); +#endif + + private: + void addVisitedLink(unsigned stringHash); + + String m_name; + + HashSet<Page*> m_pages; + HashSet<unsigned, AlreadyHashed> m_visitedLinkHashes; + bool m_visitedLinksPopulated; + + unsigned m_identifier; +#if ENABLE(DOM_STORAGE) + RefPtr<LocalStorage> m_localStorage; +#endif + }; + +} // namespace WebCore + +#endif // PageGroup_h diff --git a/WebCore/page/PositionCallback.h b/WebCore/page/PositionCallback.h new file mode 100644 index 0000000..5f32c75 --- /dev/null +++ b/WebCore/page/PositionCallback.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 PositionCallback_h +#define PositionCallback_h + +#include <wtf/Platform.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Geoposition; + + class PositionCallback : public RefCounted<PositionCallback> { + public: + virtual ~PositionCallback() { } + virtual void handleEvent(Geoposition* position, bool& raisedException) = 0; + }; + +} // namespace WebCore + +#endif // PositionCallback_h diff --git a/WebCore/page/PositionCallback.idl b/WebCore/page/PositionCallback.idl new file mode 100644 index 0000000..e862538 --- /dev/null +++ b/WebCore/page/PositionCallback.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 [ + GenerateConstructor + ] PositionCallback { + void handleEvent(in Geoposition position); + }; + +} diff --git a/WebCore/page/PositionError.h b/WebCore/page/PositionError.h new file mode 100644 index 0000000..1d68bde --- /dev/null +++ b/WebCore/page/PositionError.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 PositionError_h +#define PositionError_h + +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class PositionError : public RefCounted<PositionError> { +public: + enum ErrorCode { + PERMISSION_ERROR = 1, + LOCATION_PROVIDER_ERROR, + POSITION_NOT_FOUND_ERROR, + TIMEOUT_ERROR, + UNKNOWN_ERROR + }; + + static PassRefPtr<PositionError> create(ErrorCode code, const String& message) { return adoptRef(new PositionError(code, message)); } + + ErrorCode code() const { return m_code; } + const String& message() const { return m_message; } + +private: + PositionError(ErrorCode code, const String& message) + : m_code(code) + , m_message(message) + { + } + + ErrorCode m_code; + String m_message; +}; + +} // namespace WebCore + +#endif // PositionError_h diff --git a/WebCore/page/PositionError.idl b/WebCore/page/PositionError.idl new file mode 100644 index 0000000..8c8c335 --- /dev/null +++ b/WebCore/page/PositionError.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 [ + GenerateConstructor + ] PositionError { + readonly attribute long code; + readonly attribute DOMString message; + }; + +} diff --git a/WebCore/page/PositionErrorCallback.h b/WebCore/page/PositionErrorCallback.h new file mode 100644 index 0000000..c23e883 --- /dev/null +++ b/WebCore/page/PositionErrorCallback.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 PositionErrorCallback_h +#define PositionErrorCallback_h + +#include <wtf/Platform.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class PositionError; + + class PositionErrorCallback : public RefCounted<PositionErrorCallback> { + public: + virtual ~PositionErrorCallback() { } + virtual void handleEvent(PositionError*) = 0; + }; + +} // namespace WebCore + +#endif // PositionErrorCallback_h diff --git a/WebCore/page/PositionErrorCallback.idl b/WebCore/page/PositionErrorCallback.idl new file mode 100644 index 0000000..07edfa6 --- /dev/null +++ b/WebCore/page/PositionErrorCallback.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 [ + GenerateConstructor + ] PositionErrorCallback { + void handleEvent(in PositionError error); + }; + +} diff --git a/WebCore/page/PositionOptions.h b/WebCore/page/PositionOptions.h new file mode 100644 index 0000000..dc9c167 --- /dev/null +++ b/WebCore/page/PositionOptions.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 PositionOptions_h +#define PositionOptions_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class PositionOptions : public RefCounted<PositionOptions> { +public: + static PassRefPtr<PositionOptions> create(bool highAccuracy, unsigned timeout) { return adoptRef(new PositionOptions(highAccuracy, timeout)); } + + bool enableHighAccuracy() const { return m_highAccuracy; } + void setEnableHighAccuracy(bool enable) { m_highAccuracy = enable; } + unsigned timeout() const { return m_timeout; } + void setTimeout(unsigned t) { m_timeout = t; } + +private: + PositionOptions(bool highAccuracy, unsigned timeout) + : m_highAccuracy(highAccuracy) + , m_timeout(timeout) + { + } + + bool m_highAccuracy; + unsigned m_timeout; +}; + +} // namespace WebCore + +#endif // PositionOptions_h diff --git a/WebCore/page/PositionOptions.idl b/WebCore/page/PositionOptions.idl new file mode 100644 index 0000000..29253df --- /dev/null +++ b/WebCore/page/PositionOptions.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 [ + GenerateConstructor + ] PositionOptions { + attribute boolean enableHighAccuracy; + attribute unsigned long timeout; + }; + +} diff --git a/WebCore/page/PrintContext.cpp b/WebCore/page/PrintContext.cpp new file mode 100644 index 0000000..79672a3 --- /dev/null +++ b/WebCore/page/PrintContext.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2007 Apple Inc. + * + * 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 "PrintContext.h" + +#include "GraphicsContext.h" +#include "Frame.h" +#include "FrameView.h" +#include "RenderView.h" + +using namespace WebCore; + +namespace WebCore { + +PrintContext::PrintContext(Frame* frame) + : m_frame(frame) +{ +} + +PrintContext::~PrintContext() +{ + m_pageRects.clear(); +} + +int PrintContext::pageCount() const +{ + return m_pageRects.size(); +} + +void PrintContext::computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight) +{ + m_pageRects.clear(); + outPageHeight = 0; + + if (!m_frame->document() || !m_frame->view() || !m_frame->document()->renderer()) + return; + + RenderView* root = static_cast<RenderView*>(m_frame->document()->renderer()); + + if (!root) { + LOG_ERROR("document to be printed has no renderer"); + return; + } + + if (userScaleFactor <= 0) { + LOG_ERROR("userScaleFactor has bad value %.2f", userScaleFactor); + return; + } + + float ratio = printRect.height() / printRect.width(); + + float pageWidth = (float)root->docWidth(); + float pageHeight = pageWidth * ratio; + outPageHeight = pageHeight; // this is the height of the page adjusted by margins + pageHeight -= headerHeight + footerHeight; + + if (pageHeight <= 0) { + LOG_ERROR("pageHeight has bad value %.2f", pageHeight); + return; + } + + float currPageHeight = pageHeight / userScaleFactor; + float docHeight = root->layer()->height(); + float currPageWidth = pageWidth / userScaleFactor; + + // always return at least one page, since empty files should print a blank page + float printedPagesHeight = 0.0; + do { + float proposedBottom = std::min(docHeight, printedPagesHeight + pageHeight); + m_frame->adjustPageHeight(&proposedBottom, printedPagesHeight, proposedBottom, printedPagesHeight); + currPageHeight = max(1.0f, proposedBottom - printedPagesHeight); + + m_pageRects.append(IntRect(0, (int)printedPagesHeight, (int)currPageWidth, (int)currPageHeight)); + printedPagesHeight += currPageHeight; + } while (printedPagesHeight < docHeight); +} + +void PrintContext::begin(float width) +{ + // By imaging to a width a little wider than the available pixels, + // thin pages will be scaled down a little, matching the way they + // print in IE and Camino. This lets them use fewer sheets than they + // would otherwise, which is presumably why other browsers do this. + // Wide pages will be scaled down more than this. + const float PrintingMinimumShrinkFactor = 1.25f; + + // This number determines how small we are willing to reduce the page content + // in order to accommodate the widest line. If the page would have to be + // reduced smaller to make the widest line fit, we just clip instead (this + // behavior matches MacIE and Mozilla, at least) + const float PrintingMaximumShrinkFactor = 2.0f; + + float minLayoutWidth = width * PrintingMinimumShrinkFactor; + float maxLayoutWidth = width * PrintingMaximumShrinkFactor; + + // FIXME: This will modify the rendering of the on-screen frame. + // Could lead to flicker during printing. + m_frame->setPrinting(true, minLayoutWidth, maxLayoutWidth, true); +} + +void PrintContext::spoolPage(GraphicsContext& ctx, int pageNumber, float width) +{ + IntRect pageRect = m_pageRects[pageNumber]; + float scale = width / pageRect.width(); + + ctx.save(); + ctx.scale(FloatSize(scale, scale)); + ctx.translate(-pageRect.x(), -pageRect.y()); + ctx.clip(pageRect); + m_frame->view()->paintContents(&ctx, pageRect); + ctx.restore(); +} + +void PrintContext::end() +{ + m_frame->setPrinting(false, 0, 0, true); +} + +} diff --git a/WebCore/page/PrintContext.h b/WebCore/page/PrintContext.h new file mode 100644 index 0000000..0b3b303 --- /dev/null +++ b/WebCore/page/PrintContext.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2007 Apple Inc. + * + * 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 PrintContext_h +#define PrintContext_h + +#include <wtf/Vector.h> + +namespace WebCore { + +class Frame; +class FloatRect; +class GraphicsContext; +class IntRect; + +class PrintContext { +public: + PrintContext(Frame*); + ~PrintContext(); + + int pageCount() const; + + void computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight); + + // TODO: eliminate width param + void begin(float width); + + // TODO: eliminate width param + void spoolPage(GraphicsContext& ctx, int pageNumber, float width); + + void end(); + +protected: + Frame* m_frame; + Vector<IntRect> m_pageRects; +}; + +} + +#endif diff --git a/WebCore/page/Screen.h b/WebCore/page/Screen.h index 510cefd..a740ccb 100644 --- a/WebCore/page/Screen.h +++ b/WebCore/page/Screen.h @@ -30,6 +30,7 @@ #ifndef Screen_h #define Screen_h +#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -38,7 +39,7 @@ namespace WebCore { class Screen : public RefCounted<Screen> { public: - Screen(Frame*); + static PassRefPtr<Screen> create(Frame *frame) { return adoptRef(new Screen(frame)); } void disconnectFrame(); unsigned height() const; @@ -54,6 +55,8 @@ namespace WebCore { #endif private: + Screen(Frame*); + Frame* m_frame; }; diff --git a/WebCore/page/SecurityOrigin.cpp b/WebCore/page/SecurityOrigin.cpp new file mode 100644 index 0000000..6de7508 --- /dev/null +++ b/WebCore/page/SecurityOrigin.cpp @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "SecurityOrigin.h" + +#include "CString.h" +#include "FrameLoader.h" +#include "KURL.h" +#include "PlatformString.h" + +namespace WebCore { + +static bool isDefaultPortForProtocol(unsigned short port, const String& protocol) +{ + if (protocol.isEmpty()) + return false; + + static HashMap<String, unsigned> defaultPorts; + if (defaultPorts.isEmpty()) { + defaultPorts.set("http", 80); + defaultPorts.set("https", 443); + defaultPorts.set("ftp", 21); + defaultPorts.set("ftps", 990); + } + return defaultPorts.get(protocol) == port; +} + +SecurityOrigin::SecurityOrigin(const KURL& url) + : m_protocol(url.protocol().isNull() ? "" : url.protocol().lower()) + , m_host(url.host().isNull() ? "" : url.host().lower()) + , m_port(url.port()) + , m_noAccess(false) + , m_domainWasSetInDOM(false) +{ + // These protocols do not create security origins; the owner frame provides the origin + if (m_protocol == "about" || m_protocol == "javascript") + m_protocol = ""; + + // data: URLs are not allowed access to anything other than themselves. + if (m_protocol == "data") + m_noAccess = true; + + // document.domain starts as m_host, but can be set by the DOM. + m_domain = m_host; + + // By default, only local SecurityOrigins can load local resources. + m_canLoadLocalResources = isLocal(); + + if (isDefaultPortForProtocol(m_port, m_protocol)) + m_port = 0; +} + +SecurityOrigin::SecurityOrigin(const SecurityOrigin* other) + : m_protocol(other->m_protocol.copy()) + , m_host(other->m_host.copy()) + , m_domain(other->m_domain.copy()) + , m_port(other->m_port) + , m_noAccess(other->m_noAccess) + , m_domainWasSetInDOM(other->m_domainWasSetInDOM) + , m_canLoadLocalResources(other->m_canLoadLocalResources) +{ +} + +bool SecurityOrigin::isEmpty() const +{ + return m_protocol.isEmpty(); +} + +PassRefPtr<SecurityOrigin> SecurityOrigin::create(const KURL& url) +{ + return adoptRef(new SecurityOrigin(url)); +} + +PassRefPtr<SecurityOrigin> SecurityOrigin::createEmpty() +{ + return create(KURL()); +} + +PassRefPtr<SecurityOrigin> SecurityOrigin::copy() +{ + return adoptRef(new SecurityOrigin(this)); +} + +void SecurityOrigin::setDomainFromDOM(const String& newDomain) +{ + m_domainWasSetInDOM = true; + m_domain = newDomain.lower(); +} + +bool SecurityOrigin::canAccess(const SecurityOrigin* other) const +{ + if (isLocal()) + return true; + + if (m_noAccess || other->m_noAccess) + return false; + + // Here are two cases where we should permit access: + // + // 1) Neither document has set document.domain. In this case, we insist + // that the scheme, host, and port of the URLs match. + // + // 2) Both documents have set document.domain. In this case, we insist + // that the documents have set document.domain to the same value and + // that the scheme of the URLs match. + // + // This matches the behavior of Firefox 2 and Internet Explorer 6. + // + // Internet Explorer 7 and Opera 9 are more strict in that they require + // the port numbers to match when both pages have document.domain set. + // + // FIXME: Evaluate whether we can tighten this policy to require matched + // port numbers. + // + // Opera 9 allows access when only one page has set document.domain, but + // this is a security vulnerability. + + if (m_protocol == other->m_protocol) { + if (!m_domainWasSetInDOM && !other->m_domainWasSetInDOM) { + if (m_host == other->m_host && m_port == other->m_port) + return true; + } else if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) { + if (m_domain == other->m_domain) + return true; + } + } + + return false; +} + +bool SecurityOrigin::canRequest(const KURL& url) const +{ + if (isLocal()) + return true; + + if (m_noAccess) + return false; + + RefPtr<SecurityOrigin> targetOrigin = SecurityOrigin::create(url); + + // We call isSameSchemeHostPort here instead of canAccess because we want + // to ignore document.domain effects. + return isSameSchemeHostPort(targetOrigin.get()); +} + +void SecurityOrigin::grantLoadLocalResources() +{ + // This method exists only to support backwards compatibility with older + // versions of WebKit. Granting privileges to some, but not all, documents + // in a SecurityOrigin is a security hazard because the documents without + // the privilege can obtain the privilege by injecting script into the + // documents that have been granted the privilege. + ASSERT(FrameLoader::allowSubstituteDataAccessToLocal()); + m_canLoadLocalResources = true; +} + +bool SecurityOrigin::isLocal() const +{ + return FrameLoader::shouldTreatSchemeAsLocal(m_protocol); +} + +bool SecurityOrigin::isSecureTransitionTo(const KURL& url) const +{ + // New window created by the application + if (isEmpty()) + return true; + + RefPtr<SecurityOrigin> other = SecurityOrigin::create(url); + return canAccess(other.get()); +} + +String SecurityOrigin::toString() const +{ + if (isEmpty()) + return "null"; + + if (m_noAccess) + return "null"; + + if (m_protocol == "file") + return String("file://"); + + Vector<UChar> result; + result.reserveCapacity(m_protocol.length() + m_host.length() + 10); + append(result, m_protocol); + append(result, "://"); + append(result, m_host); + + if (m_port) { + append(result, ":"); + append(result, String::number(m_port)); + } + + return String::adopt(result); +} + +PassRefPtr<SecurityOrigin> SecurityOrigin::createFromString(const String& originString) +{ + return SecurityOrigin::create(KURL(originString)); +} + +static const char SeparatorCharacter = '_'; + +PassRefPtr<SecurityOrigin> SecurityOrigin::createFromDatabaseIdentifier(const String& databaseIdentifier) +{ + // Make sure there's a first separator + int separator1 = databaseIdentifier.find(SeparatorCharacter); + if (separator1 == -1) + return create(KURL()); + + // Make sure there's a second separator + int separator2 = databaseIdentifier.find(SeparatorCharacter, separator1 + 1); + if (separator2 == -1) + return create(KURL()); + + // Make sure there's not a third separator + if (databaseIdentifier.reverseFind(SeparatorCharacter) != separator2) + return create(KURL()); + + // Make sure the port section is a valid port number or doesn't exist + bool portOkay; + int port = databaseIdentifier.right(databaseIdentifier.length() - separator2 - 1).toInt(&portOkay); + if (!portOkay && separator2 + 1 == static_cast<int>(databaseIdentifier.length())) + return create(KURL()); + + if (port < 0 || port > 65535) + return create(KURL()); + + // Split out the 3 sections of data + String protocol = databaseIdentifier.substring(0, separator1); + String host = databaseIdentifier.substring(separator1 + 1, separator2 - separator1 - 1); + return create(KURL(protocol + "://" + host + ":" + String::number(port))); +} + +String SecurityOrigin::databaseIdentifier() const +{ + static String separatorString = String(&SeparatorCharacter, 1); + return m_protocol + separatorString + m_host + separatorString + String::number(m_port); +} + +bool SecurityOrigin::equal(const SecurityOrigin* other) const +{ + if (!isSameSchemeHostPort(other)) + return false; + + if (m_domainWasSetInDOM != other->m_domainWasSetInDOM) + return false; + + if (m_domainWasSetInDOM && m_domain != other->m_domain) + return false; + + return true; +} + +bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin* other) const +{ + if (m_host != other->m_host) + return false; + + if (m_protocol != other->m_protocol) + return false; + + if (m_port != other->m_port) + return false; + + return true; +} + +} // namespace WebCore diff --git a/WebCore/page/SecurityOrigin.h b/WebCore/page/SecurityOrigin.h new file mode 100644 index 0000000..1f2624e --- /dev/null +++ b/WebCore/page/SecurityOrigin.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2007,2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 SecurityOrigin_h +#define SecurityOrigin_h + +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +#include "PlatformString.h" + +namespace WebCore { + + class KURL; + + class SecurityOrigin : public ThreadSafeShared<SecurityOrigin> { + public: + static PassRefPtr<SecurityOrigin> createFromDatabaseIdentifier(const String&); + static PassRefPtr<SecurityOrigin> createFromString(const String&); + static PassRefPtr<SecurityOrigin> create(const KURL&); + static PassRefPtr<SecurityOrigin> createEmpty(); + + // Create a deep copy of this SecurityOrigin. This method is useful + // when marshalling a SecurityOrigin to another thread. + PassRefPtr<SecurityOrigin> copy(); + + // Set the domain property of this security origin to newDomain. This + // function does not check whether newDomain is a suffix of the current + // domain. The caller is responsible for validating newDomain. + void setDomainFromDOM(const String& newDomain); + bool domainWasSetInDOM() const { return m_domainWasSetInDOM; } + + String protocol() const { return m_protocol; } + String host() const { return m_host; } + String domain() const { return m_domain; } + unsigned short port() const { return m_port; } + + // Returns true if this SecurityOrigin can script objects in the given + // SecurityOrigin. For example, call this function before allowing + // script from one security origin to read or write objects from + // another SecurityOrigin. + bool canAccess(const SecurityOrigin*) const; + + // Returns true if this SecurityOrigin can read content retrieved from + // the given URL. For example, call this function before issuing + // XMLHttpRequests. + bool canRequest(const KURL&) const; + + // Returns true if this SecurityOrigin can load local resources, such + // as images, iframes, and style sheets, and can link to local URLs. + // For example, call this function before creating an iframe to a + // file:// URL. + // + // Note: A SecurityOrigin might be allowed to load local resources + // without being able to issue an XMLHttpRequest for a local URL. + // To determine whether the SecurityOrigin can issue an + // XMLHttpRequest for a URL, call canRequest(url). + bool canLoadLocalResources() const { return m_canLoadLocalResources; } + + // Explicitly grant the ability to load local resources to this + // SecurityOrigin. + // + // Note: This method exists only to support backwards compatibility + // with older versions of WebKit. + void grantLoadLocalResources(); + + bool isSecureTransitionTo(const KURL&) const; + + // The local SecurityOrigin is the most privileged SecurityOrigin. + // The local SecurityOrigin can script any document, navigate to local + // resources, and can set arbitrary headers on XMLHttpRequests. + bool isLocal() const; + + // The empty SecurityOrigin is the least privileged SecurityOrigin. + bool isEmpty() const; + + // Convert this SecurityOrigin into a string. The string + // representation of a SecurityOrigin is similar to a URL, except it + // lacks a path component. The string representation does not encode + // the value of the SecurityOrigin's domain property. The empty + // SecurityOrigin is represented with the string "null". + String toString() const; + + // Serialize the security origin for storage in the database. This format is + // deprecated and should be used only for compatibility with old databases; + // use toString() and createFromString() instead. + String databaseIdentifier() const; + + // This method checks for equality between SecurityOrigins, not whether + // one origin can access another. It is used for hash table keys. + // For access checks, use canAccess(). + // FIXME: If this method is really only useful for hash table keys, it + // should be refactored into SecurityOriginHash. + bool equal(const SecurityOrigin*) const; + + // This method checks for equality, ignoring the value of document.domain + // (and whether it was set) but considering the host. It is used for postMessage. + bool isSameSchemeHostPort(const SecurityOrigin*) const; + + private: + explicit SecurityOrigin(const KURL&); + explicit SecurityOrigin(const SecurityOrigin*); + + String m_protocol; + String m_host; + String m_domain; + unsigned short m_port; + bool m_noAccess; + bool m_domainWasSetInDOM; + bool m_canLoadLocalResources; + }; + +} // namespace WebCore + +#endif // SecurityOrigin_h diff --git a/WebCore/page/SecurityOriginHash.h b/WebCore/page/SecurityOriginHash.h new file mode 100644 index 0000000..1915fc7 --- /dev/null +++ b/WebCore/page/SecurityOriginHash.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 SecurityOriginHash_h +#define SecurityOriginHash_h + +#include "KURL.h" +#include "SecurityOrigin.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +struct SecurityOriginHash { + static unsigned hash(SecurityOrigin* origin) + { + unsigned hashCodes[3] = { + origin->protocol().impl() ? origin->protocol().impl()->hash() : 0, + origin->host().impl() ? origin->host().impl()->hash() : 0, + origin->port() + }; + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); + } + static unsigned hash(const RefPtr<SecurityOrigin>& origin) + { + return hash(origin.get()); + } + + static bool equal(SecurityOrigin* a, SecurityOrigin* b) + { + // FIXME: The hash function above compares three specific fields. + // This code to compare those three specific fields should be moved here from + // SecurityOrigin as mentioned in SecurityOrigin.h so we don't accidentally change + // equal without changing hash to match it. + if (!a || !b) + return a == b; + return a->equal(b); + } + static bool equal(SecurityOrigin* a, const RefPtr<SecurityOrigin>& b) + { + return equal(a, b.get()); + } + static bool equal(const RefPtr<SecurityOrigin>& a, SecurityOrigin* b) + { + return equal(a.get(), b); + } + static bool equal(const RefPtr<SecurityOrigin>& a, const RefPtr<SecurityOrigin>& b) + { + return equal(a.get(), b.get()); + } + + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +} // namespace WebCore + +#endif diff --git a/WebCore/page/Settings.cpp b/WebCore/page/Settings.cpp index 9a8adb0..b9b3925 100644 --- a/WebCore/page/Settings.cpp +++ b/WebCore/page/Settings.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,9 +28,10 @@ #include "Frame.h" #include "FrameTree.h" +#include "HistoryItem.h" #include "Page.h" #include "PageCache.h" -#include "HistoryItem.h" +#include <limits> #if ENABLE(DATABASE) #include "DatabaseTracker.h" @@ -44,6 +45,10 @@ static void setNeedsReapplyStylesInAllFrames(Page* page) frame->setNeedsReapplyStyles(); } +#if USE(SAFARI_THEME) +bool Settings::gShouldPaintNativeControls = false; +#endif + Settings::Settings(Page* page) : m_page(page) #ifdef ANDROID_LAYOUT @@ -71,7 +76,9 @@ Settings::Settings(Page* page) , m_javaScriptCanOpenWindowsAutomatically(false) , m_shouldPrintBackgrounds(false) , m_textAreasAreResizable(false) +#if ENABLE(DASHBOARD_SUPPORT) , m_usesDashboardBackwardCompatibilityMode(false) +#endif , m_needsAdobeFrameReloadingQuirk(false) , m_needsKeyboardEventDisambiguationQuirks(false) , m_isDOMPasteAllowed(false) @@ -83,6 +90,14 @@ Settings::Settings(Page* page) , m_authorAndUserStylesEnabled(true) , m_needsSiteSpecificQuirks(false) , m_fontRenderingMode(0) + , m_webArchiveDebugModeEnabled(false) + , m_inApplicationChromeMode(false) + , m_offlineWebApplicationCacheEnabled(false) + , m_rangeMutationDisabledForOldAppleMail(false) + , m_shouldPaintCustomScrollbars(false) + , m_zoomsTextOnly(false) + , m_enforceCSSMIMETypeInStrictMode(true) + , m_maximumDecodedImageSize(std::numeric_limits<size_t>::max()) { // A Frame may not have been created yet, so we initialize the AtomicString // hash before trying to use it. @@ -261,10 +276,12 @@ void Settings::setEditableLinkBehavior(EditableLinkBehavior editableLinkBehavior m_editableLinkBehavior = editableLinkBehavior; } +#if ENABLE(DASHBOARD_SUPPORT) void Settings::setUsesDashboardBackwardCompatibilityMode(bool usesDashboardBackwardCompatibilityMode) { m_usesDashboardBackwardCompatibilityMode = usesDashboardBackwardCompatibilityMode; } +#endif // FIXME: This quirk is needed because of Radar 4674537 and 5211271. We need to phase it out once Adobe // can fix the bug from their end. @@ -441,4 +458,55 @@ void Settings::setNeedsSiteSpecificQuirks(bool needsQuirks) m_needsSiteSpecificQuirks = needsQuirks; } +void Settings::setWebArchiveDebugModeEnabled(bool enabled) +{ + m_webArchiveDebugModeEnabled = enabled; +} + +void Settings::setLocalStorageDatabasePath(const String& path) +{ + m_localStorageDatabasePath = path; +} + +void Settings::disableRangeMutationForOldAppleMail(bool disable) +{ + m_rangeMutationDisabledForOldAppleMail = disable; +} + +void Settings::setApplicationChromeMode(bool mode) +{ + m_inApplicationChromeMode = mode; +} + +void Settings::setOfflineWebApplicationCacheEnabled(bool enabled) +{ + m_offlineWebApplicationCacheEnabled = enabled; +} + +void Settings::setShouldPaintCustomScrollbars(bool shouldPaintCustomScrollbars) +{ + m_shouldPaintCustomScrollbars = shouldPaintCustomScrollbars; +} + +void Settings::setZoomsTextOnly(bool zoomsTextOnly) +{ + if (zoomsTextOnly == m_zoomsTextOnly) + return; + + m_zoomsTextOnly = zoomsTextOnly; + setNeedsReapplyStylesInAllFrames(m_page); +} + +void Settings::setEnforceCSSMIMETypeInStrictMode(bool enforceCSSMIMETypeInStrictMode) +{ + m_enforceCSSMIMETypeInStrictMode = enforceCSSMIMETypeInStrictMode; +} + +#if USE(SAFARI_THEME) +void Settings::setShouldPaintNativeControls(bool shouldPaintNativeControls) +{ + gShouldPaintNativeControls = shouldPaintNativeControls; +} +#endif + } // namespace WebCore diff --git a/WebCore/page/Settings.h b/WebCore/page/Settings.h index 28b92c3..8acc998 100644 --- a/WebCore/page/Settings.h +++ b/WebCore/page/Settings.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. * (C) 2006 Graham Dennis (graham.dennis@gmail.com) * * Redistribution and use in source and binary forms, with or without @@ -28,8 +28,8 @@ #define Settings_h #include "AtomicString.h" -#include "KURL.h" #include "FontDescription.h" +#include "KURL.h" namespace WebCore { @@ -43,8 +43,7 @@ namespace WebCore { EditableLinkNeverLive }; - class Settings - { + class Settings { public: Settings(Page*); @@ -139,8 +138,10 @@ namespace WebCore { void setEditableLinkBehavior(EditableLinkBehavior); EditableLinkBehavior editableLinkBehavior() const { return m_editableLinkBehavior; } +#if ENABLE(DASHBOARD_SUPPORT) void setUsesDashboardBackwardCompatibilityMode(bool); bool usesDashboardBackwardCompatibilityMode() const { return m_usesDashboardBackwardCompatibilityMode; } +#endif void setNeedsAdobeFrameReloadingQuirk(bool); bool needsAcrobatFrameReloadingQuirk() const { return m_needsAdobeFrameReloadingQuirk; } @@ -195,6 +196,39 @@ namespace WebCore { void setNeedsSiteSpecificQuirks(bool); bool needsSiteSpecificQuirks() const { return m_needsSiteSpecificQuirks; } + + void setWebArchiveDebugModeEnabled(bool); + bool webArchiveDebugModeEnabled() const { return m_webArchiveDebugModeEnabled; } + + void setLocalStorageDatabasePath(const String&); + const String& localStorageDatabasePath() const { return m_localStorageDatabasePath; } + + void disableRangeMutationForOldAppleMail(bool); + bool rangeMutationDisabledForOldAppleMail() const { return m_rangeMutationDisabledForOldAppleMail; } + + void setApplicationChromeMode(bool); + bool inApplicationChromeMode() const { return m_inApplicationChromeMode; } + + void setOfflineWebApplicationCacheEnabled(bool); + bool offlineWebApplicationCacheEnabled() const { return m_offlineWebApplicationCacheEnabled; } + + void setShouldPaintCustomScrollbars(bool); + bool shouldPaintCustomScrollbars() const { return m_shouldPaintCustomScrollbars; } + + void setZoomsTextOnly(bool); + bool zoomsTextOnly() const { return m_zoomsTextOnly; } + + void setEnforceCSSMIMETypeInStrictMode(bool); + bool enforceCSSMIMETypeInStrictMode() { return m_enforceCSSMIMETypeInStrictMode; } + + void setMaximumDecodedImageSize(size_t size) { m_maximumDecodedImageSize = size; } + size_t maximumDecodedImageSize() const { return m_maximumDecodedImageSize; } + +#if USE(SAFARI_THEME) + // Windows debugging pref (global) for switching between the Aqua look and a native windows look. + static void setShouldPaintNativeControls(bool); + static bool shouldPaintNativeControls() { return gShouldPaintNativeControls; } +#endif private: Page* m_page; @@ -204,6 +238,7 @@ namespace WebCore { #ifdef ANDROID_PLUGINS String m_pluginsPath; #endif + String m_localStorageDatabasePath; KURL m_userStyleSheetLocation; AtomicString m_standardFontFamily; AtomicString m_fixedFontFamily; @@ -258,7 +293,9 @@ namespace WebCore { bool m_javaScriptCanOpenWindowsAutomatically : 1; bool m_shouldPrintBackgrounds : 1; bool m_textAreasAreResizable : 1; +#if ENABLE(DASHBOARD_SUPPORT) bool m_usesDashboardBackwardCompatibilityMode : 1; +#endif bool m_needsAdobeFrameReloadingQuirk : 1; bool m_needsKeyboardEventDisambiguationQuirks : 1; bool m_isDOMPasteAllowed : 1; @@ -270,6 +307,18 @@ namespace WebCore { bool m_authorAndUserStylesEnabled : 1; bool m_needsSiteSpecificQuirks : 1; unsigned m_fontRenderingMode : 1; + bool m_webArchiveDebugModeEnabled : 1; + bool m_inApplicationChromeMode : 1; + bool m_offlineWebApplicationCacheEnabled : 1; + bool m_rangeMutationDisabledForOldAppleMail : 1; + bool m_shouldPaintCustomScrollbars : 1; + bool m_zoomsTextOnly : 1; + bool m_enforceCSSMIMETypeInStrictMode : 1; + size_t m_maximumDecodedImageSize; + +#if USE(SAFARI_THEME) + static bool gShouldPaintNativeControls; +#endif }; } // namespace WebCore diff --git a/WebCore/page/android/EventHandlerAndroid.cpp b/WebCore/page/android/EventHandlerAndroid.cpp index ca74a79..aa522a3 100644 --- a/WebCore/page/android/EventHandlerAndroid.cpp +++ b/WebCore/page/android/EventHandlerAndroid.cpp @@ -22,6 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#define LOG_TAG "WebCore" #include "config.h" #include "EventHandler.h" @@ -33,20 +34,17 @@ #include "FrameView.h" #include "KeyboardEvent.h" #include "MouseEventWithHitTestResults.h" +#include "NotImplemented.h" #include "Page.h" -#include "PlatformScrollBar.h" +#include "PlatformKeyboardEvent.h" #include "PlatformWheelEvent.h" #include "RenderWidget.h" -#define LOG_TAG "WebCore" -#undef LOG -#include <utils/Log.h> - namespace WebCore { -using namespace EventNames; +// using namespace EventNames; -#define notImplemented() { LOGV("%s: Not Implemented", __FUNCTION__); } +unsigned EventHandler::s_accessKeyModifiers = PlatformKeyboardEvent::AltKey; bool EventHandler::tabsToAllControls(KeyboardEvent* ) const { @@ -78,7 +76,7 @@ bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget) // work around for the Mac platform which does not support double clicks, but browsers do. bool EventHandler::passMouseDownEventToWidget(Widget* widget) { - notImplemented(); + // return false so the normal propogation handles the event return false; } @@ -124,14 +122,11 @@ bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& return passSubframeEventToSubframe(mev, subframe); } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults&, PlatformScrollbar* scrollbar) -{ - notImplemented(); - return false; -} - // functions new to Jun-07 tip of tree merge: -Clipboard* EventHandler::createDraggingClipboard() const { return NULL; } +class Clipboard : public RefCounted<Clipboard> {}; +PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const { return PassRefPtr<Clipboard>(NULL); } + // new as of SVN change 36269, Sept 8, 2008 +const double EventHandler::TextDragDelay = 0.0; } diff --git a/WebCore/page/android/FrameAndroid.cpp b/WebCore/page/android/FrameAndroid.cpp deleted file mode 100644 index ca86bb5..0000000 --- a/WebCore/page/android/FrameAndroid.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include "config.h" -#include "FrameAndroid.h" - -#include <JavaScriptCore/bindings/runtime_root.h> -#include <JavaScriptCore/bindings/runtime_object.h> -#include <JavaScriptCore/bindings/jni/jni_utility.h> -#include "jni.h" -#include "kjs_proxy.h" -#include "kjs_window.h" - -#include "CacheBuilder.h" -#include "CachedImage.h" -#include "Document.h" -#include "EventNames.h" -#include "FrameLoader.h" -#include "FramePrivate.h" -#include "FrameView.h" -#include "HTMLAreaElement.h" -#include "HTMLImageElement.h" -#include "HTMLNames.h" -#include "Image.h" -#include "Page.h" -#include "PlatformKeyboardEvent.h" -#include "PlatformString.h" -#ifdef ANDROID_PLUGINS -#include "Plugin.h" -#include "PluginViewAndroid.h" -#endif -#include "RenderImage.h" -#include "RenderTable.h" -#include "RenderTextControl.h" -#include "RenderTheme.h" -#include "RenderView.h" -#include "RenderWidget.h" -#include "SelectionController.h" -#include "Settings.h" -#include "SkScalar.h" -#include "Text.h" -#include "WebCoreFrameBridge.h" -#include "WebCoreViewBridge.h" - -#define LOG_TAG "WebCore" -#undef LOG -#include <utils/Log.h> - -#ifdef ANDROID_INSTRUMENT -#include "CString.h" -#include "Cache.h" -#endif - -using KJS::JSLock; -using KJS::JSValue; - -namespace WebCore { - -#ifdef ANDROID_INSTRUMENT -// The following code should be inside Frame.cpp. But android LOG is conflict -// with webcore LOG -void Frame::resetTimeCounter() { - KJS::JSGlobalObject::resetTimeCounter(); - resetLayoutTimeCounter(); - resetPaintTimeCounter(); - resetCSSTimeCounter(); - resetParsingTimeCounter(); - resetCalculateStyleTimeCounter(); - resetFramebridgeTimeCounter(); - resetSharedTimerTimeCounter(); - resetResourceLoadTimeCounter(); - resetWebViewCoreTimeCounter(); - LOG(LOG_DEBUG, "WebCore", "*-* Start browser instrument\n"); -} - -void Frame::reportTimeCounter(String url, int total, int totalThreadTime) -{ - LOG(LOG_DEBUG, "WebCore", - "*-* Total load time: %d ms, thread time: %d ms for %s\n", - total, totalThreadTime, url.utf8().data()); - KJS::JSGlobalObject::reportTimeCounter(); - reportLayoutTimeCounter(); - reportPaintTimeCounter(); - reportCSSTimeCounter(); - reportParsingTimeCounter(); - reportCalculateStyleTimeCounter(); - reportFramebridgeTimeCounter(); - reportSharedTimerTimeCounter(); - reportResourceLoadTimeCounter(); - reportWebViewCoreTimeCounter(); - LOG(LOG_DEBUG, "WebCore", "Current cache has %d bytes live and %d bytes dead", cache()->getLiveSize(), cache()->getDeadSize()); -} -#endif - -#ifdef ANDROID_PLUGINS -KJS::Bindings::Instance* Frame::createScriptInstanceForWidget(Widget* widget) -{ - if (widget->isFrameView()) - return 0; - - return static_cast<PluginViewAndroid*>(widget)->bindingInstance(); -} -#endif - -FrameAndroid::FrameAndroid(Page* page, HTMLFrameOwnerElement* element, FrameLoaderClient* client) - : Frame(page, element, client), - m_bindingRoot(NULL), m_bridge(NULL) -{ -} - -FrameAndroid::~FrameAndroid() -{ - if (view() != NULL) - view()->getWebCoreViewBridge()->removeFrameGeneration(this); - Release(m_bridge); - setView(0); - loader()->clearRecordedFormValues(); -} - -void FrameAndroid::select(int selectionStart, int selectionEnd) -{ - if (selectionStart > selectionEnd) { - int temp = selectionStart; - selectionStart = selectionEnd; - selectionEnd = temp; - } - Document* doc = document(); - if (!doc) - return; - Node* focus = doc->focusedNode(); - if (!focus) - return; - RenderObject* renderer = focus->renderer(); - if (renderer && (renderer->isTextField() || renderer->isTextArea())) { - RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer); - rtc->setSelectionRange(selectionStart, selectionEnd); - Frame::revealSelection(); - } -} - -void FrameAndroid::cleanupForFullLayout(RenderObject* obj) -{ - recursiveCleanupForFullLayout(obj); -} - -void FrameAndroid::recursiveCleanupForFullLayout(RenderObject* obj) -{ - obj->setNeedsLayout(true, false); -#ifdef ANDROID_LAYOUT - if (obj->isTable()) - (static_cast<RenderTable *>(obj))->clearSingleColumn(); -#endif - for (RenderObject* n = obj->firstChild(); n; n = n->nextSibling()) - recursiveCleanupForFullLayout(n); -} - -KJS::Bindings::RootObject* FrameAndroid::bindingRootObject() -{ - if (!m_bindingRoot) - m_bindingRoot = KJS::Bindings::RootObject::create(0, scriptProxy()->globalObject()); // The root gets deleted by JavaScriptCore. - ASSERT(settings()->isJavaScriptEnabled()); - // The root gets deleted by JavaScriptCore. - m_bindingRoot = KJS::Bindings::RootObject::create(0, scriptProxy()->globalObject()); - return m_bindingRoot.get(); -} - -/* -* This function provides the ability to add a Java class to Javascript and -* expose it through the Window object. -* The code to access the object would look something like: window.<objectName>.<class method> -*/ -void FrameAndroid::addJavascriptInterface(void *javaVM, void* objectInstance, const char* objectNameStr) -{ - // Obtain the window object from KJS - KJS::Bindings::RootObject *root = bindingRootObject(); - KJS::JSObject *rootObject = root->globalObject(); - KJS::ExecState *exec = root->globalObject()->globalExec(); - KJS::JSObject *window = rootObject->get(exec, KJS::Identifier("window"))->getObject(); - if (!window) { - LOGE("Warning: couldn't get window object"); - return; - } - - KJS::Bindings::setJavaVM((JavaVM*)javaVM); - - // Add the binding to JS environment - KJS::JSObject *addedObject = - KJS::Bindings::Instance::createRuntimeObject(KJS::Bindings::Instance::JavaLanguage, - (jobject)objectInstance, root); - - // Add the binding name to the window's table of child objects. - window->put(exec, KJS::Identifier(objectNameStr), addedObject); -} - -/* -* This function executes the provided javascript string in the context of -* the frame's document. The code is based on the implementation of the same -* function in WebCoreFrameBridge.mm -*/ -String FrameAndroid::stringByEvaluatingJavaScriptFromString(const char* script) -{ - ASSERT(document()); - JSValue* result = loader()->executeScript(String(script), true); - if (!result) - return String(); - JSLock lock; - // note: result->getString() returns a UString. - return String(result->isString() ? result->getString() : - result->toString(scriptProxy()->globalObject()->globalExec())); -} - -#if 0 // disabled for now by <reed> -// experimental function to allow portable code to query our bg-ness -bool android_should_draw_background(RenderObject* obj) -{ - Document* doc = obj->document(); - if (doc) { - Frame* frame = doc->frame(); - if (frame) { - Page* page = frame->page(); - if (page) { - frame = page->mainFrame(); - if (frame) { - return AsAndroidFrame(frame)->shouldDrawBackgroundPhase(); - } - } - } - } - return false; -} -#endif - -///////////////!!!!!!!!!!!! MUST COMPLETE THESE - -#define verifiedOk() { } // not a problem that it's not implemented - -// These two functions are called by JavaScript Window.focus() and Window.blur() methods. If -// a window has the window focus method called on it, it should be moved to the top of the -// browser window stack. If blur is called, it is moved to the bottom of the stack. -void FrameAndroid::focusWindow() { verifiedOk(); notImplemented(); } -void FrameAndroid::unfocusWindow() { verifiedOk(); notImplemented(); } - -// This function is called by JavaScript when window.print() is called. It would normally map -// straight to the menu print item. -// It is ok that we don't support this at this time. -void FrameAndroid::print() { verifiedOk(); } - -// These functions are used to interact with the platform's clipboard. It is ok that we don't -// support these at this time as we currently don't support sites that would use this -// functionality. -void FrameAndroid::issueCutCommand() { verifiedOk(); } -void FrameAndroid::issueCopyCommand() { verifiedOk(); } -void FrameAndroid::issuePasteCommand() { verifiedOk(); } -void FrameAndroid::issuePasteAndMatchStyleCommand() { verifiedOk(); } - -// This function seems to switch the two characters around the cursor. -// See WebHTMLView.mm:transpose. -// We are currently not implementing it. -void FrameAndroid::issueTransposeCommand() { verifiedOk(); } - -// These functions are called when JavaScript tries to access the script interface for -// these objects. For Object and Embed, the interface expected from the 'plugin' is -// well defined here: -// http://www.mozilla.org/projects/plugins/npruntime.html -KJS::Bindings::Instance* FrameAndroid::getObjectInstanceForWidget(Widget *) { ASSERT(0); notImplemented(); return 0; } -KJS::Bindings::Instance* FrameAndroid::getEmbedInstanceForWidget(Widget *) { ASSERT(0); notImplemented(); return 0; } -KJS::Bindings::Instance* FrameAndroid::getAppletInstanceForWidget(Widget*) { ASSERT(0); notImplemented(); return 0; } - -// These two functions are used to handle spell checking and context menus on selected text. -// It seems that if the text is selected, and the user tries to change the selection, the -// shouldChange function indicates if it is allowed or not. The second function is used for -// spell checking - check FrameMac.mm implementation for details. -bool FrameAndroid::shouldChangeSelection(Selection const&,Selection const&,EAffinity,bool) const { notImplemented(); return true; } -void FrameAndroid::respondToChangedSelection(Selection const&,bool) { notImplemented(); } - -// This function is called when an textbox needs to find out if it's contents -// is marked. It is used in conjunction with the set method which uses -// NSArray, a Mac specific type. The set method is called whenever text is -// added (not removed) via a single key press (paste does not count) -Range* FrameAndroid::markedTextRange() const { return 0; } - -// This function is called when determining the correct mimetype for a file that is going -// to be POSTed. That is when the form element <input type="file"> is used, and the file is -// being sent the server, the client should set the mimetype of the file. -String FrameAndroid::mimeTypeForFileName(String const&) const { ASSERT(0); notImplemented(); return String(); } - -}; /* WebCore namespace */ diff --git a/WebCore/page/android/FrameAndroid.h b/WebCore/page/android/FrameAndroid.h deleted file mode 100644 index c0948df..0000000 --- a/WebCore/page/android/FrameAndroid.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef FrameAndroid_H -#define FrameAndroid_H - -#include "CacheBuilder.h" -#include "Frame.h" -#include "FrameLoaderTypes.h" -#include "PlatformGraphicsContext.h" -#include "Plugin.h" -#include "StringHash.h" -#include "WebCoreFrameBridge.h" - -class WebCoreResourceHandleClientBridge; - -namespace KJS { - namespace Bindings { - class RootObject; - } -} - -namespace WebCore { - -class EditCommand; -class MouseEventWithHitTestResults; -class Node; -class ResourceHandle; -class ResourceRequest; -struct LinkArray; -struct LinkArray; - -enum KWQSelectionDirection { - KWQSelectingNext, - KWQSelectingPrevious -}; - -class FrameAndroid : public Frame -{ -public: - FrameAndroid(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*); - virtual ~FrameAndroid(); - - /* WebCoreFrameBridge setter and getter */ - inline void setBridge(android::WebCoreFrameBridge* bridge) - { - Release(m_bridge); - m_bridge = bridge; - Retain(m_bridge); - } - inline android::WebCoreFrameBridge* bridge() const { return m_bridge; } - - virtual void focusWindow(); - virtual void unfocusWindow(); - - virtual Range* markedTextRange() const; - - virtual String mimeTypeForFileName(const String&) const; - - virtual KJS::Bindings::Instance* getEmbedInstanceForWidget(Widget*); - virtual KJS::Bindings::Instance* getObjectInstanceForWidget(Widget*); - virtual KJS::Bindings::Instance* getAppletInstanceForWidget(Widget*); - virtual KJS::Bindings::RootObject* bindingRootObject(); - - virtual void issueCutCommand(); - virtual void issueCopyCommand(); - virtual void issuePasteCommand(); - virtual void issuePasteAndMatchStyleCommand(); - virtual void issueTransposeCommand(); - virtual void respondToChangedSelection(const Selection& oldSelection, bool closeTyping); - virtual bool shouldChangeSelection(const Selection& oldSelection, - const Selection& newSelection, - EAffinity affinity, - bool stillSelecting) const; - - virtual void print(); - - void addJavascriptInterface(void* javaVM, void* objectInstance, const char* objectNameStr); - String stringByEvaluatingJavaScriptFromString(const char* script); - - /* FrameAndroid specific */ - CacheBuilder& getCacheBuilder() { return m_cacheBuilder; } - void select(int, int); - - void cleanupForFullLayout(RenderObject *); - -private: - static void recursiveCleanupForFullLayout(RenderObject *); - - RefPtr<KJS::Bindings::RootObject> m_bindingRoot; // The root object used for objects - // bound outside the context of a plugin. - /* End FrameAndroid specific */ - - android::WebCoreFrameBridge* m_bridge; - CacheBuilder m_cacheBuilder; - friend class CacheBuilder; -}; - -inline FrameAndroid* Android(Frame* frame) { return static_cast<FrameAndroid*>(frame); } -inline const FrameAndroid* Android(const Frame* frame) { return static_cast<const FrameAndroid*>(frame); } - -inline FrameAndroid* AsAndroidFrame(Frame* frame) { return static_cast<FrameAndroid*>(frame); } - -} - -#endif diff --git a/WebCore/page/android/InspectorControllerAndroid.cpp b/WebCore/page/android/InspectorControllerAndroid.cpp index b39d32b..c3b88b7 100644 --- a/WebCore/page/android/InspectorControllerAndroid.cpp +++ b/WebCore/page/android/InspectorControllerAndroid.cpp @@ -19,6 +19,7 @@ #include "InspectorController.h" #include "Frame.h" #include "Node.h" +#include "Profile.h" /* // This stub file was created to avoid building and linking in all the @@ -53,7 +54,8 @@ struct InspectorResource : public RefCounted<InspectorResource> { struct InspectorDatabaseResource : public RefCounted<InspectorDatabaseResource> { }; -InspectorController::InspectorController(Page*, InspectorClient*) {} +InspectorController::InspectorController(Page*, InspectorClient*) : + m_startProfiling(this, NULL) {} InspectorController::~InspectorController() {} void InspectorController::windowScriptObjectAvailable() {} @@ -66,12 +68,30 @@ void InspectorController::didFinishLoading(DocumentLoader*, unsigned long) {} void InspectorController::didLoadResourceFromMemoryCache(DocumentLoader*, ResourceRequest const&, ResourceResponse const&, int) {} void InspectorController::frameDetachedFromParent(Frame*) {} -void InspectorController::addMessageToConsole(MessageSource, MessageLevel, String const&, unsigned int, String const&) {} +void InspectorController::addMessageToConsole(MessageSource, MessageLevel, JSC::ExecState*, JSC::ArgList const&, unsigned int, String const&) {} +void InspectorController::addMessageToConsole(MessageSource, MessageLevel, const String& message, unsigned lineNumber, const String& sourceID) {} #if ENABLE(DATABASE) void InspectorController::didOpenDatabase(Database*, String const&, String const&, String const&) {} #endif bool InspectorController::enabled() const { return false; } void InspectorController::inspect(Node*) {} bool InspectorController::windowVisible() { return false; } +void InspectorController::addProfile(PassRefPtr<JSC::Profile>, unsigned int, const JSC::UString&) {} +void InspectorController::inspectedPageDestroyed() {} +void InspectorController::resourceRetrievedByXMLHttpRequest(unsigned long identifier, JSC::UString& sourceString) {} + // new as of SVN change 36269, Sept 8, 2008 +void InspectorController::inspectedWindowScriptObjectCleared(Frame* frame) {} +void InspectorController::startGroup(MessageSource source, JSC::ExecState* exec, const JSC::ArgList& arguments, unsigned lineNumber, const String& sourceURL) {} +void InspectorController::endGroup(MessageSource source, unsigned lineNumber, const String& sourceURL) {} +void InspectorController::startTiming(const JSC::UString& title) {} +bool InspectorController::stopTiming(const JSC::UString& title, double& elapsed) { return false; } +void InspectorController::count(const JSC::UString& title, unsigned lineNumber, const String& sourceID) {} + + // new as of SVN change 38068, Nov 5, 2008 +void InspectorController::mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags) {} +void InspectorController::handleMousePressOnNode(Node*) {} +void InspectorController::failedToParseSource(JSC::ExecState* exec, const JSC::SourceCode& source, int errorLine, const JSC::UString& errorMessage) {} +void InspectorController::didParseSource(JSC::ExecState* exec, const JSC::SourceCode& source) {} +void InspectorController::didPause() {} } diff --git a/WebCore/page/animation/AnimationBase.cpp b/WebCore/page/animation/AnimationBase.cpp new file mode 100644 index 0000000..fc28469 --- /dev/null +++ b/WebCore/page/animation/AnimationBase.cpp @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AnimationBase.h" + +#include "AnimationController.h" +#include "CSSPropertyNames.h" +#include "CString.h" +#include "CompositeAnimation.h" +#include "Document.h" +#include "EventNames.h" +#include "FloatConversion.h" +#include "Frame.h" +#include "IdentityTransformOperation.h" +#include "ImplicitAnimation.h" +#include "KeyframeAnimation.h" +#include "MatrixTransformOperation.h" +#include "RenderObject.h" +#include "RenderStyle.h" +#include "SystemTime.h" +#include "UnitBezier.h" + +namespace WebCore { + +static const double cAnimationTimerDelay = 0.025; + +// The epsilon value we pass to UnitBezier::solve given that the animation is going to run over |dur| seconds. The longer the +// animation, the more precision we need in the timing function result to avoid ugly discontinuities. +static inline double solveEpsilon(double duration) +{ + return 1.0 / (200.0 * duration); +} + +static inline double solveCubicBezierFunction(double p1x, double p1y, double p2x, double p2y, double t, double duration) +{ + // Convert from input time to parametric value in curve, then from + // that to output time. + UnitBezier bezier(p1x, p1y, p2x, p2y); + return bezier.solve(t, solveEpsilon(duration)); +} + +void AnimationTimerCallback::timerFired(Timer<AnimationTimerBase>*) +{ + m_anim->animationTimerCallbackFired(m_eventType, m_elapsedTime); +} + +static inline int blendFunc(const AnimationBase* anim, int from, int to, double progress) +{ + return int(from + (to - from) * progress); +} + +static inline double blendFunc(const AnimationBase* anim, double from, double to, double progress) +{ + return from + (to - from) * progress; +} + +static inline float blendFunc(const AnimationBase* anim, float from, float to, double progress) +{ + return narrowPrecisionToFloat(from + (to - from) * progress); +} + +static inline Color blendFunc(const AnimationBase* anim, const Color& from, const Color& to, double progress) +{ + return Color(blendFunc(anim, from.red(), to.red(), progress), + blendFunc(anim, from.green(), to.green(), progress), + blendFunc(anim, from.blue(), to.blue(), progress), + blendFunc(anim, from.alpha(), to.alpha(), progress)); +} + +static inline Length blendFunc(const AnimationBase* anim, const Length& from, const Length& to, double progress) +{ + return to.blend(from, progress); +} + +static inline IntSize blendFunc(const AnimationBase* anim, const IntSize& from, const IntSize& to, double progress) +{ + return IntSize(blendFunc(anim, from.width(), to.width(), progress), + blendFunc(anim, from.height(), to.height(), progress)); +} + +static inline ShadowData* blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress) +{ + ASSERT(from && to); + return new ShadowData(blendFunc(anim, from->x, to->x, progress), blendFunc(anim, from->y, to->y, progress), + blendFunc(anim, from->blur, to->blur, progress), blendFunc(anim, from->color, to->color, progress)); +} + +static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress) +{ + TransformOperations result; + + // If we have a transform function list, use that to do a per-function animation. Otherwise do a Matrix animation + if (anim->isTransformFunctionListValid()) { + unsigned fromSize = from.operations().size(); + unsigned toSize = to.operations().size(); + unsigned size = max(fromSize, toSize); + for (unsigned i = 0; i < size; i++) { + RefPtr<TransformOperation> fromOp = (i < fromSize) ? from.operations()[i].get() : 0; + RefPtr<TransformOperation> toOp = (i < toSize) ? to.operations()[i].get() : 0; + RefPtr<TransformOperation> blendedOp = toOp ? toOp->blend(fromOp.get(), progress) : (fromOp ? fromOp->blend(0, progress, true) : 0); + if (blendedOp) + result.operations().append(blendedOp); + else { + RefPtr<TransformOperation> identityOp = IdentityTransformOperation::create(); + if (progress > 0.5) + result.operations().append(toOp ? toOp : identityOp); + else + result.operations().append(fromOp ? fromOp : identityOp); + } + } + } else { + // Convert the TransformOperations into matrices + IntSize size = anim->renderer()->borderBox().size(); + AffineTransform fromT; + AffineTransform toT; + from.apply(size, fromT); + to.apply(size, toT); + + toT.blend(fromT, progress); + + // Append the result + result.operations().append(MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f())); + } + return result; +} + +static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, EVisibility to, double progress) +{ + // Any non-zero result means we consider the object to be visible. Only at 0 do we consider the object to be + // invisible. The invisible value we use (HIDDEN vs. COLLAPSE) depends on the specified from/to values. + double fromVal = from == VISIBLE ? 1. : 0.; + double toVal = to == VISIBLE ? 1. : 0.; + if (fromVal == toVal) + return to; + double result = blendFunc(anim, fromVal, toVal, progress); + return result > 0. ? VISIBLE : (to != VISIBLE ? to : from); +} + +class PropertyWrapperBase { +public: + PropertyWrapperBase(int prop) + : m_prop(prop) + { + } + + virtual ~PropertyWrapperBase() { } + virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0; + virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const = 0; + + int property() const { return m_prop; } + +private: + int m_prop; +}; + +template <typename T> +class PropertyWrapperGetter : public PropertyWrapperBase { +public: + PropertyWrapperGetter(int prop, T (RenderStyle::*getter)() const) + : PropertyWrapperBase(prop) + , m_getter(getter) + { + } + + virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + { + // If the style pointers are the same, don't bother doing the test. + // If either is null, return false. If both are null, return true. + if (!a && !b || a == b) + return true; + if (!a || !b) + return false; + return (a->*m_getter)() == (b->*m_getter)(); + } + +protected: + T (RenderStyle::*m_getter)() const; +}; + +template <typename T> +class PropertyWrapper : public PropertyWrapperGetter<T> { +public: + PropertyWrapper(int prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) + : PropertyWrapperGetter<T>(prop, getter) + , m_setter(setter) + { + } + + virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + { + (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress)); + } + +protected: + void (RenderStyle::*m_setter)(T); +}; + +class PropertyWrapperShadow : public PropertyWrapperGetter<ShadowData*> { +public: + PropertyWrapperShadow(int prop, ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(ShadowData*, bool)) + : PropertyWrapperGetter<ShadowData*>(prop, getter) + , m_setter(setter) + { + } + + virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + { + ShadowData* shadowA = (a->*m_getter)(); + ShadowData* shadowB = (b->*m_getter)(); + + if (!shadowA && shadowB || shadowA && !shadowB) + return false; + if (shadowA && shadowB && (*shadowA != *shadowB)) + return false; + return true; + } + + virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + { + ShadowData* shadowA = (a->*m_getter)(); + ShadowData* shadowB = (b->*m_getter)(); + ShadowData defaultShadowData(0, 0, 0, Color::transparent); + + if (!shadowA) + shadowA = &defaultShadowData; + if (!shadowB) + shadowB = &defaultShadowData; + + (dst->*m_setter)(blendFunc(anim, shadowA, shadowB, progress), false); + } + +private: + void (RenderStyle::*m_setter)(ShadowData*, bool); +}; + +class PropertyWrapperMaybeInvalidColor : public PropertyWrapperBase { +public: + PropertyWrapperMaybeInvalidColor(int prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) + : PropertyWrapperBase(prop) + , m_getter(getter) + , m_setter(setter) + { + } + + virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + { + Color fromColor = (a->*m_getter)(); + Color toColor = (b->*m_getter)(); + if (!fromColor.isValid()) + fromColor = a->color(); + if (!toColor.isValid()) + toColor = b->color(); + + return fromColor == toColor; + } + + virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + { + Color fromColor = (a->*m_getter)(); + Color toColor = (b->*m_getter)(); + if (!fromColor.isValid()) + fromColor = a->color(); + if (!toColor.isValid()) + toColor = b->color(); + (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress)); + } + +private: + const Color& (RenderStyle::*m_getter)() const; + void (RenderStyle::*m_setter)(const Color&); +}; + +static Vector<PropertyWrapperBase*>* gPropertyWrappers = 0; +static int gPropertyWrapperMap[numCSSProperties]; + +static void ensurePropertyMap() +{ + // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed? + if (gPropertyWrappers == 0) { + gPropertyWrappers = new Vector<PropertyWrapperBase*>(); + + // build the list of property wrappers to do the comparisons and blends + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLeft, &RenderStyle::left, &RenderStyle::setLeft)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyRight, &RenderStyle::right, &RenderStyle::setRight)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyTop, &RenderStyle::top, &RenderStyle::setTop)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyBottom, &RenderStyle::bottom, &RenderStyle::setBottom)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWidth, &RenderStyle::width, &RenderStyle::setWidth)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyHeight, &RenderStyle::height, &RenderStyle::setHeight)); + gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth)); + gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth)); + gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth)); + gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyMarginBottom, &RenderStyle::marginBottom, &RenderStyle::setMarginBottom)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingLeft, &RenderStyle::paddingLeft, &RenderStyle::setPaddingLeft)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingRight, &RenderStyle::paddingRight, &RenderStyle::setPaddingRight)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingTop, &RenderStyle::paddingTop, &RenderStyle::setPaddingTop)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyPaddingBottom, &RenderStyle::paddingBottom, &RenderStyle::setPaddingBottom)); + gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity)); + gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyColor, &RenderStyle::color, &RenderStyle::setColor)); + gPropertyWrappers->append(new PropertyWrapper<const Color&>(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor)); + gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyFontSize, &RenderStyle::fontSize, &RenderStyle::setBlendedFontSize)); + gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth)); + gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap)); + gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount)); + gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth)); + gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing)); + gPropertyWrappers->append(new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing)); + gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::lineHeight, &RenderStyle::setLineHeight)); + gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset)); + gPropertyWrappers->append(new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth)); + gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing)); + gPropertyWrappers->append(new PropertyWrapper<int>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing)); + gPropertyWrappers->append(new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX)); + gPropertyWrappers->append(new PropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY)); + gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyWebkitBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius)); + gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyWebkitBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius)); + gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyWebkitBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius)); + gPropertyWrappers->append(new PropertyWrapper<const IntSize&>(CSSPropertyWebkitBorderBottomRightRadius, &RenderStyle::borderBottomRightRadius, &RenderStyle::setBorderBottomRightRadius)); + gPropertyWrappers->append(new PropertyWrapper<EVisibility>(CSSPropertyVisibility, &RenderStyle::visibility, &RenderStyle::setVisibility)); + gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyZoom, &RenderStyle::zoom, &RenderStyle::setZoom)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitColumnRuleColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextStrokeColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyWebkitTextFillColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderLeftColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderRightColor, &RenderStyle::borderRightColor, &RenderStyle::setBorderRightColor)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderTopColor, &RenderStyle::borderTopColor, &RenderStyle::setBorderTopColor)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyBorderBottomColor, &RenderStyle::borderBottomColor, &RenderStyle::setBorderBottomColor)); + gPropertyWrappers->append(new PropertyWrapperMaybeInvalidColor(CSSPropertyOutlineColor, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor)); + + // These are for shadows + gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow)); + gPropertyWrappers->append(new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow)); + +#if ENABLE(SVG) + gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity)); + gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity)); + gPropertyWrappers->append(new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity)); +#endif + + // Make sure unused slots have a value + for (unsigned int i = 0; i < (unsigned int) numCSSProperties; ++i) + gPropertyWrapperMap[i] = CSSPropertyInvalid; + + size_t n = gPropertyWrappers->size(); + for (unsigned int i = 0; i < n; ++i) { + ASSERT((*gPropertyWrappers)[i]->property() - firstCSSProperty < numCSSProperties); + gPropertyWrapperMap[(*gPropertyWrappers)[i]->property() - firstCSSProperty] = i; + } + } +} + +AnimationBase::AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim) + : m_animState(AnimationStateNew) + , m_iteration(0) + , m_isAnimating(false) + , m_waitedForResponse(false) + , m_startTime(0) + , m_pauseTime(-1) + , m_object(renderer) + , m_animationTimerCallback(const_cast<AnimationBase*>(this)) + , m_animation(const_cast<Animation*>(transition)) + , m_compAnim(compAnim) + , m_transformFunctionListValid(false) +{ +} + +AnimationBase::~AnimationBase() +{ + if (m_animState == AnimationStateStartWaitStyleAvailable) + m_compAnim->setWaitingForStyleAvailable(false); +} + +bool AnimationBase::propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b) +{ + ensurePropertyMap(); + if (prop == cAnimateAll) { + size_t n = gPropertyWrappers->size(); + for (unsigned int i = 0; i < n; ++i) { + if (!(*gPropertyWrappers)[i]->equals(a, b)) + return false; + } + } else { + int propIndex = prop - firstCSSProperty; + + if (propIndex >= 0 && propIndex < numCSSProperties) { + int i = gPropertyWrapperMap[propIndex]; + return i >= 0 ? (*gPropertyWrappers)[i]->equals(a, b) : true; + } + } + return true; +} + +int AnimationBase::getPropertyAtIndex(int i) +{ + ensurePropertyMap(); + if (i < 0 || i >= static_cast<int>(gPropertyWrappers->size())) + return CSSPropertyInvalid; + + return (*gPropertyWrappers)[i]->property(); +} + +int AnimationBase::getNumProperties() +{ + ensurePropertyMap(); + return gPropertyWrappers->size(); +} + +// Returns true if we need to start animation timers +bool AnimationBase::blendProperties(const AnimationBase* anim, int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) +{ + ASSERT(prop != cAnimateAll); + // FIXME: Why can this happen? + + ensurePropertyMap(); + if (prop == cAnimateAll) { + bool needsTimer = false; + + size_t n = gPropertyWrappers->size(); + for (unsigned int i = 0; i < n; ++i) { + PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; + if (!wrapper->equals(a, b)) { + wrapper->blend(anim, dst, a, b, progress); + needsTimer = true; + } + } + return needsTimer; + } + + int propIndex = prop - firstCSSProperty; + if (propIndex >= 0 && propIndex < numCSSProperties) { + int i = gPropertyWrapperMap[propIndex]; + if (i >= 0) { + PropertyWrapperBase* wrapper = (*gPropertyWrappers)[i]; + wrapper->blend(anim, dst, a, b, progress); + return true; + } + } + + return false; +} + +void AnimationBase::setChanged(Node* node) +{ + ASSERT(!node || (node->document() && !node->document()->inPageCache())); + node->setChanged(AnimationStyleChange); +} + +double AnimationBase::duration() const +{ + return m_animation->duration(); +} + +bool AnimationBase::playStatePlaying() const +{ + return m_animation && m_animation->playState() == AnimPlayStatePlaying; +} + +bool AnimationBase::animationsMatch(const Animation* anim) const +{ + return m_animation->animationsMatch(anim); +} + +void AnimationBase::updateStateMachine(AnimStateInput input, double param) +{ + // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state. + if (input == AnimationStateInputMakeNew) { + if (m_animState == AnimationStateStartWaitStyleAvailable) + m_compAnim->setWaitingForStyleAvailable(false); + m_animState = AnimationStateNew; + m_startTime = 0; + m_pauseTime = -1; + m_waitedForResponse = false; + endAnimation(false); + return; + } + + if (input == AnimationStateInputRestartAnimation) { + cancelTimers(); + if (m_animState == AnimationStateStartWaitStyleAvailable) + m_compAnim->setWaitingForStyleAvailable(false); + m_animState = AnimationStateNew; + m_startTime = 0; + m_pauseTime = -1; + endAnimation(false); + + if (!paused()) + updateStateMachine(AnimationStateInputStartAnimation, -1); + return; + } + + if (input == AnimationStateInputEndAnimation) { + cancelTimers(); + if (m_animState == AnimationStateStartWaitStyleAvailable) + m_compAnim->setWaitingForStyleAvailable(false); + m_animState = AnimationStateDone; + endAnimation(true); + return; + } + + if (input == AnimationStateInputPauseOverride) { + if (m_animState == AnimationStateStartWaitResponse) { + // If we are in AnimationStateStartWaitResponse, the animation will get canceled before + // we get a response, so move to the next state. + endAnimation(false); + updateStateMachine(AnimationStateInputStartTimeSet, currentTime()); + } + return; + } + + if (input == AnimationStateInputResumeOverride) { + if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) { + // Start the animation + startAnimation(m_startTime); + } + return; + } + + // Execute state machine + switch(m_animState) { + case AnimationStateNew: + ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunnning || input == AnimationStateInputPlayStatePaused); + if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunnning) { + // Set the start timer to the initial delay (0 if no delay) + m_waitedForResponse = false; + m_animState = AnimationStateStartWaitTimer; + m_animationTimerCallback.startTimer(m_animation->delay(), eventNames().webkitAnimationStartEvent, m_animation->delay()); + } + break; + case AnimationStateStartWaitTimer: + ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused); + + if (input == AnimationStateInputStartTimerFired) { + ASSERT(param >= 0); + // Start timer has fired, tell the animation to start and wait for it to respond with start time + m_animState = AnimationStateStartWaitStyleAvailable; + m_compAnim->setWaitingForStyleAvailable(true); + + // Trigger a render so we can start the animation + setChanged(m_object->element()); + m_object->animation()->startUpdateRenderingDispatcher(); + } else { + ASSERT(!paused()); + // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait + m_pauseTime = currentTime(); + cancelTimers(); + m_animState = AnimationStatePausedWaitTimer; + } + break; + case AnimationStateStartWaitStyleAvailable: + ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused); + + m_compAnim->setWaitingForStyleAvailable(false); + + if (input == AnimationStateInputStyleAvailable) { + // Start timer has fired, tell the animation to start and wait for it to respond with start time + m_animState = AnimationStateStartWaitResponse; + + overrideAnimations(); + + // Send start event, if needed + onAnimationStart(0); // The elapsedTime is always 0 here + + // Start the animation + if (overridden() || !startAnimation(0)) { + // We're not going to get a startTime callback, so fire the start time here + m_animState = AnimationStateStartWaitResponse; + updateStateMachine(AnimationStateInputStartTimeSet, currentTime()); + } else + m_waitedForResponse = true; + } else { + ASSERT(!paused()); + // We're waiting for the a notification that the style has been setup. If we're asked to wait + // at this point, the style must have been processed, so we can deal with this like we would + // for WAIT_RESPONSE, except that we don't need to do an endAnimation(). + m_pauseTime = 0; + m_animState = AnimationStateStartWaitResponse; + } + break; + case AnimationStateStartWaitResponse: + ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused); + + if (input == AnimationStateInputStartTimeSet) { + ASSERT(param >= 0); + // We have a start time, set it, unless the startTime is already set + if (m_startTime <= 0) + m_startTime = param; + + // Decide when the end or loop event needs to fire + primeEventTimers(); + + // Trigger a render so we can start the animation + setChanged(m_object->element()); + m_object->animation()->startUpdateRenderingDispatcher(); + } else { + // We are pausing while waiting for a start response. Cancel the animation and wait. When + // we unpause, we will act as though the start timer just fired + m_pauseTime = 0; + endAnimation(false); + m_animState = AnimationStatePausedWaitResponse; + } + break; + case AnimationStateLooping: + ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused); + + if (input == AnimationStateInputLoopTimerFired) { + ASSERT(param >= 0); + // Loop timer fired, loop again or end. + onAnimationIteration(param); + primeEventTimers(); + } else { + // We are pausing while running. Cancel the animation and wait + m_pauseTime = currentTime(); + cancelTimers(); + endAnimation(false); + m_animState = AnimationStatePausedRun; + } + break; + case AnimationStateEnding: + ASSERT(input == AnimationStateInputEndTimerFired || input == AnimationStateInputPlayStatePaused); + + if (input == AnimationStateInputEndTimerFired) { + ASSERT(param >= 0); + // End timer fired, finish up + onAnimationEnd(param); + + resumeOverriddenAnimations(); + + // Fire off another style change so we can set the final value + setChanged(m_object->element()); + m_animState = AnimationStateDone; + m_object->animation()->startUpdateRenderingDispatcher(); + // |this| may be deleted here when we've been called from timerFired() + } else { + // We are pausing while running. Cancel the animation and wait + m_pauseTime = currentTime(); + cancelTimers(); + endAnimation(false); + m_animState = AnimationStatePausedRun; + } + // |this| may be deleted here + break; + case AnimationStatePausedWaitTimer: + ASSERT(input == AnimationStateInputPlayStateRunnning); + ASSERT(paused()); + // Update the times + m_startTime += currentTime() - m_pauseTime; + m_pauseTime = -1; + + // we were waiting for the start timer to fire, go back and wait again + m_animState = AnimationStateNew; + updateStateMachine(AnimationStateInputStartAnimation, 0); + break; + case AnimationStatePausedWaitResponse: + case AnimationStatePausedRun: + // We treat these two cases the same. The only difference is that, when we are in + // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation. + // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice + // that we have already set the startTime and will ignore it. + ASSERT(input == AnimationStateInputPlayStateRunnning); + ASSERT(paused()); + // Update the times + if (m_animState == AnimationStatePausedRun) + m_startTime += currentTime() - m_pauseTime; + else + m_startTime = 0; + m_pauseTime = -1; + + // We were waiting for a begin time response from the animation, go back and wait again + m_animState = AnimationStateStartWaitResponse; + + // Start the animation + if (overridden() || !startAnimation(m_startTime)) { + // We're not going to get a startTime callback, so fire the start time here + updateStateMachine(AnimationStateInputStartTimeSet, currentTime()); + } else + m_waitedForResponse = true; + break; + case AnimationStateDone: + // We're done. Stay in this state until we are deleted + break; + } + // |this| may be deleted here if we came out of AnimationStateEnding when we've been called from timerFired() +} + +void AnimationBase::animationTimerCallbackFired(const AtomicString& eventType, double elapsedTime) +{ + ASSERT(m_object->document() && !m_object->document()->inPageCache()); + + // FIXME: use an enum + if (eventType == eventNames().webkitAnimationStartEvent) + updateStateMachine(AnimationStateInputStartTimerFired, elapsedTime); + else if (eventType == eventNames().webkitAnimationIterationEvent) + updateStateMachine(AnimationStateInputLoopTimerFired, elapsedTime); + else if (eventType == eventNames().webkitAnimationEndEvent) { + updateStateMachine(AnimationStateInputEndTimerFired, elapsedTime); + // |this| may be deleted here + } +} + +void AnimationBase::updatePlayState(bool run) +{ + if (paused() == run || isNew()) + updateStateMachine(run ? AnimationStateInputPlayStateRunnning : AnimationStateInputPlayStatePaused, -1); +} + +double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const +{ + if (preActive()) + return 0; + + double elapsedTime = running() ? (currentTime() - m_startTime) : (m_pauseTime - m_startTime); + if (running() && elapsedTime < 0) + return 0; + + double dur = m_animation->duration(); + if (m_animation->iterationCount() > 0) + dur *= m_animation->iterationCount(); + + if (postActive() || !m_animation->duration() || (m_animation->iterationCount() > 0 && elapsedTime >= dur)) + return 1.0; + + // Compute the fractional time, taking into account direction. + // There is no need to worry about iterations, we assume that we would have + // short circuited above if we were done. + double fractionalTime = elapsedTime / m_animation->duration(); + int integralTime = static_cast<int>(fractionalTime); + fractionalTime -= integralTime; + + if (m_animation->direction() && (integralTime & 1)) + fractionalTime = 1 - fractionalTime; + + if (scale != 1 || offset) + fractionalTime = (fractionalTime - offset) * scale; + + if (!tf) + tf = &m_animation->timingFunction(); + + if (tf->type() == LinearTimingFunction) + return fractionalTime; + + // Cubic bezier. + double result = solveCubicBezierFunction(tf->x1(), + tf->y1(), + tf->x2(), + tf->y2(), + fractionalTime, m_animation->duration()); + return result; +} + +void AnimationBase::primeEventTimers() +{ + // Decide when the end or loop event needs to fire + double ct = currentTime(); + const double elapsedDuration = ct - m_startTime; + ASSERT(elapsedDuration >= 0); + + double totalDuration = -1; + if (m_animation->iterationCount() > 0) + totalDuration = m_animation->duration() * m_animation->iterationCount(); + + double durationLeft = 0; + double nextIterationTime = totalDuration; + if (totalDuration < 0 || elapsedDuration < totalDuration) { + durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); + nextIterationTime = elapsedDuration + durationLeft; + } + + // At this point, we may have 0 durationLeft, if we've gotten the event late and we are already + // past totalDuration. In this case we still fire an end timer before processing the end. + // This defers the call to sendAnimationEvents to avoid re-entrant calls that destroy + // the RenderObject, and therefore |this| before we're done with it. + if (totalDuration < 0 || nextIterationTime < totalDuration) { + // We are not at the end yet, send a loop event + ASSERT(nextIterationTime > 0); + m_animState = AnimationStateLooping; + m_animationTimerCallback.startTimer(durationLeft, eventNames().webkitAnimationIterationEvent, nextIterationTime); + } else { + // We are at the end, send an end event + m_animState = AnimationStateEnding; + m_animationTimerCallback.startTimer(durationLeft, eventNames().webkitAnimationEndEvent, nextIterationTime); + } +} + +} // namespace WebCore diff --git a/WebCore/page/animation/AnimationBase.h b/WebCore/page/animation/AnimationBase.h new file mode 100644 index 0000000..925c0d5 --- /dev/null +++ b/WebCore/page/animation/AnimationBase.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AnimationBase_h +#define AnimationBase_h + +#include "AtomicString.h" +#include "Timer.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +class Animation; +class AnimationBase; +class AnimationController; +class CompositeAnimation; +class Element; +class Node; +class RenderObject; +class RenderStyle; +class TimingFunction; + +class AnimationTimerBase { +public: + AnimationTimerBase(AnimationBase* anim) + : m_timer(this, &AnimationTimerBase::timerFired) + , m_anim(anim) + { + m_timer.startOneShot(0); + } + + virtual ~AnimationTimerBase() { } + + void startTimer(double timeout = 0) + { + m_timer.startOneShot(timeout); + } + + void cancelTimer() + { + m_timer.stop(); + } + + virtual void timerFired(Timer<AnimationTimerBase>*) = 0; + +private: + Timer<AnimationTimerBase> m_timer; + +protected: + AnimationBase* m_anim; +}; + +class AnimationTimerCallback : public AnimationTimerBase { +public: + AnimationTimerCallback(AnimationBase* anim) + : AnimationTimerBase(anim) + , m_elapsedTime(0) + { + } + + virtual ~AnimationTimerCallback() { } + + virtual void timerFired(Timer<AnimationTimerBase>*); + + void startTimer(double timeout, const AtomicString& eventType, double elapsedTime) + { + m_eventType = eventType; + m_elapsedTime = elapsedTime; + AnimationTimerBase::startTimer(timeout); + } + +private: + AtomicString m_eventType; + double m_elapsedTime; +}; + +class AnimationBase : public RefCounted<AnimationBase> { + friend class CompositeAnimationPrivate; + +public: + AnimationBase(const Animation* transition, RenderObject* renderer, CompositeAnimation* compAnim); + virtual ~AnimationBase(); + + RenderObject* renderer() const { return m_object; } + double startTime() const { return m_startTime; } + double duration() const; + + void cancelTimers() + { + m_animationTimerCallback.cancelTimer(); + } + + // Animations and Transitions go through the states below. When entering the STARTED state + // the animation is started. This may or may not require deferred response from the animator. + // If so, we stay in this state until that response is received (and it returns the start time). + // Otherwise, we use the current time as the start time and go immediately to AnimationStateLooping + // or AnimationStateEnding. + enum AnimState { + AnimationStateNew, // animation just created, animation not running yet + AnimationStateStartWaitTimer, // start timer running, waiting for fire + AnimationStateStartWaitStyleAvailable, // waiting for style setup so we can start animations + AnimationStateStartWaitResponse, // animation started, waiting for response + AnimationStateLooping, // response received, animation running, loop timer running, waiting for fire + AnimationStateEnding, // received, animation running, end timer running, waiting for fire + AnimationStatePausedWaitTimer, // in pause mode when animation started + AnimationStatePausedWaitResponse, // animation paused when in STARTING state + AnimationStatePausedRun, // animation paused when in LOOPING or ENDING state + AnimationStateDone // end timer fired, animation finished and removed + }; + + enum AnimStateInput { + AnimationStateInputMakeNew, // reset back to new from any state + AnimationStateInputStartAnimation, // animation requests a start + AnimationStateInputRestartAnimation, // force a restart from any state + AnimationStateInputStartTimerFired, // start timer fired + AnimationStateInputStyleAvailable, // style is setup, ready to start animating + AnimationStateInputStartTimeSet, // m_startTime was set + AnimationStateInputLoopTimerFired, // loop timer fired + AnimationStateInputEndTimerFired, // end timer fired + AnimationStateInputPauseOverride, // pause an animation due to override + AnimationStateInputResumeOverride, // resume an overridden animation + AnimationStateInputPlayStateRunnning, // play state paused -> running + AnimationStateInputPlayStatePaused, // play state running -> paused + AnimationStateInputEndAnimation // force an end from any state + }; + + // Called when animation is in AnimationStateNew to start animation + void updateStateMachine(AnimStateInput, double param); + + // Animation has actually started, at passed time + void onAnimationStartResponse(double startTime); + + // Called to change to or from paused state + void updatePlayState(bool running); + bool playStatePlaying() const; + + bool waitingToStart() const { return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer; } + bool preActive() const + { + return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer || m_animState == AnimationStateStartWaitStyleAvailable || m_animState == AnimationStateStartWaitResponse; + } + + bool postActive() const { return m_animState == AnimationStateDone; } + bool active() const { return !postActive() && !preActive(); } + bool running() const { return !isNew() && !postActive(); } + bool paused() const { return m_pauseTime >= 0; } + bool isNew() const { return m_animState == AnimationStateNew; } + bool waitingForStartTime() const { return m_animState == AnimationStateStartWaitResponse; } + bool waitingForStyleAvailable() const { return m_animState == AnimationStateStartWaitStyleAvailable; } + + // "animating" means that something is running that requires a timer to keep firing + // (e.g. a software animation) + void setAnimating(bool inAnimating = true) { m_isAnimating = inAnimating; } + bool isAnimating() const { return m_isAnimating; } + + double progress(double scale, double offset, const TimingFunction*) const; + + virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle, + const RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { } + + virtual bool shouldFireEvents() const { return false; } + + void animationTimerCallbackFired(const AtomicString& eventType, double elapsedTime); + + bool animationsMatch(const Animation*) const; + + void setAnimation(const Animation* anim) { m_animation = const_cast<Animation*>(anim); } + + // Return true if this animation is overridden. This will only be the case for + // ImplicitAnimations and is used to determine whether or not we should force + // set the start time. If an animation is overridden, it will probably not get + // back the AnimationStateInputStartTimeSet input. + virtual bool overridden() const { return false; } + + // Does this animation/transition involve the given property? + virtual bool affectsProperty(int property) const { return false; } + bool isAnimatingProperty(int property, bool isRunningNow) const + { + if (isRunningNow) + return (!waitingToStart() && !postActive()) && affectsProperty(property); + + return !postActive() && affectsProperty(property); + } + + bool isTransformFunctionListValid() const { return m_transformFunctionListValid; } + +protected: + virtual void overrideAnimations() { } + virtual void resumeOverriddenAnimations() { } + + CompositeAnimation* compositeAnimation() { return m_compAnim; } + + // These are called when the corresponding timer fires so subclasses can do any extra work + virtual void onAnimationStart(double elapsedTime) { } + virtual void onAnimationIteration(double elapsedTime) { } + virtual void onAnimationEnd(double elapsedTime) { } + virtual bool startAnimation(double beginTime) { return false; } + virtual void endAnimation(bool reset) { } + + void primeEventTimers(); + + static bool propertiesEqual(int prop, const RenderStyle* a, const RenderStyle* b); + static int getPropertyAtIndex(int); + static int getNumProperties(); + + // Return true if we need to start software animation timers + static bool blendProperties(const AnimationBase* anim, int prop, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress); + + static void setChanged(Node*); + +protected: + AnimState m_animState; + int m_iteration; + + bool m_isAnimating; // transition/animation requires continual timer firing + bool m_waitedForResponse; + double m_startTime; + double m_pauseTime; + RenderObject* m_object; + + AnimationTimerCallback m_animationTimerCallback; + RefPtr<Animation> m_animation; + CompositeAnimation* m_compAnim; + bool m_transformFunctionListValid; +}; + +} // namespace WebCore + +#endif // AnimationBase_h diff --git a/WebCore/page/animation/AnimationController.cpp b/WebCore/page/animation/AnimationController.cpp new file mode 100644 index 0000000..d449afe --- /dev/null +++ b/WebCore/page/animation/AnimationController.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AnimationController.h" +#include "CompositeAnimation.h" +#include "Frame.h" +#include "Timer.h" + +namespace WebCore { + +static const double cAnimationTimerDelay = 0.025; + +class AnimationControllerPrivate { +public: + AnimationControllerPrivate(Frame*); + ~AnimationControllerPrivate(); + + CompositeAnimation* accessCompositeAnimation(RenderObject*); + bool clear(RenderObject*); + + void animationTimerFired(Timer<AnimationControllerPrivate>*); + void updateAnimationTimer(); + + void updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*); + void startUpdateRenderingDispatcher(); + + bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); } + + void suspendAnimations(Document*); + void resumeAnimations(Document*); + + void styleAvailable(); + + bool isAnimatingPropertyOnRenderer(RenderObject*, int property, bool isRunningNow) const; + +private: + typedef HashMap<RenderObject*, CompositeAnimation*> RenderObjectAnimationMap; + + RenderObjectAnimationMap m_compositeAnimations; + Timer<AnimationControllerPrivate> m_animationTimer; + Timer<AnimationControllerPrivate> m_updateRenderingDispatcher; + Frame* m_frame; +}; + +AnimationControllerPrivate::AnimationControllerPrivate(Frame* frame) + : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) + , m_updateRenderingDispatcher(this, &AnimationControllerPrivate::updateRenderingDispatcherFired) + , m_frame(frame) +{ +} + +AnimationControllerPrivate::~AnimationControllerPrivate() +{ + deleteAllValues(m_compositeAnimations); +} + +CompositeAnimation* AnimationControllerPrivate::accessCompositeAnimation(RenderObject* renderer) +{ + CompositeAnimation* animation = m_compositeAnimations.get(renderer); + if (!animation) { + animation = new CompositeAnimation(m_frame->animation()); + m_compositeAnimations.set(renderer, animation); + } + return animation; +} + +bool AnimationControllerPrivate::clear(RenderObject* renderer) +{ + // Return false if we didn't do anything OR we are suspended (so we don't try to + // do a setChanged() when suspended). + CompositeAnimation* animation = m_compositeAnimations.take(renderer); + if (!animation) + return false; + animation->resetTransitions(renderer); + bool wasSuspended = animation->isSuspended(); + delete animation; + return !wasSuspended; +} + +void AnimationControllerPrivate::styleAvailable() +{ + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); + for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) + it->second->styleAvailable(); +} + +void AnimationControllerPrivate::updateAnimationTimer() +{ + bool isAnimating = false; + + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); + for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { + CompositeAnimation* compAnim = it->second; + if (!compAnim->isSuspended() && compAnim->isAnimating()) { + isAnimating = true; + break; + } + } + + if (isAnimating) { + if (!m_animationTimer.isActive()) + m_animationTimer.startRepeating(cAnimationTimerDelay); + } else if (m_animationTimer.isActive()) + m_animationTimer.stop(); +} + +void AnimationControllerPrivate::updateRenderingDispatcherFired(Timer<AnimationControllerPrivate>*) +{ + if (m_frame && m_frame->document()) + m_frame->document()->updateRendering(); +} + +void AnimationControllerPrivate::startUpdateRenderingDispatcher() +{ + if (!m_updateRenderingDispatcher.isActive()) + m_updateRenderingDispatcher.startOneShot(0); +} + +void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>* timer) +{ + // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate + // updateRendering. It will then call back to us with new information. + bool isAnimating = false; + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); + for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { + CompositeAnimation* compAnim = it->second; + if (!compAnim->isSuspended() && compAnim->isAnimating()) { + isAnimating = true; + compAnim->setAnimating(false); + + Node* node = it->first->element(); + ASSERT(!node || (node->document() && !node->document()->inPageCache())); + node->setChanged(AnimationStyleChange); + } + } + + m_frame->document()->updateRendering(); + + updateAnimationTimer(); +} + +bool AnimationControllerPrivate::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const +{ + CompositeAnimation* animation = m_compositeAnimations.get(renderer); + if (!animation) + return false; + + return animation->isAnimatingProperty(property, isRunningNow); +} + +void AnimationControllerPrivate::suspendAnimations(Document* document) +{ + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); + for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { + RenderObject* renderer = it->first; + CompositeAnimation* compAnim = it->second; + if (renderer->document() == document) + compAnim->suspendAnimations(); + } + + updateAnimationTimer(); +} + +void AnimationControllerPrivate::resumeAnimations(Document* document) +{ + RenderObjectAnimationMap::const_iterator animationsEnd = m_compositeAnimations.end(); + for (RenderObjectAnimationMap::const_iterator it = m_compositeAnimations.begin(); it != animationsEnd; ++it) { + RenderObject* renderer = it->first; + CompositeAnimation* compAnim = it->second; + if (renderer->document() == document) + compAnim->resumeAnimations(); + } + + updateAnimationTimer(); +} + +AnimationController::AnimationController(Frame* frame) + : m_data(new AnimationControllerPrivate(frame)) + , m_numStyleAvailableWaiters(0) +{ +} + +AnimationController::~AnimationController() +{ + delete m_data; +} + +void AnimationController::cancelAnimations(RenderObject* renderer) +{ + if (!m_data->hasAnimations()) + return; + + if (m_data->clear(renderer)) { + Node* node = renderer->element(); + ASSERT(!node || (node->document() && !node->document()->inPageCache())); + node->setChanged(AnimationStyleChange); + } +} + +PassRefPtr<RenderStyle> AnimationController::updateAnimations(RenderObject* renderer, RenderStyle* newStyle) +{ + // Don't do anything if we're in the cache + if (!renderer->document() || renderer->document()->inPageCache()) + return newStyle; + + RenderStyle* oldStyle = renderer->style(); + + if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle->animations() && !newStyle->transitions())) + return newStyle; + + // Fetch our current set of implicit animations from a hashtable. We then compare them + // against the animations in the style and make sure we're in sync. If destination values + // have changed, we reset the animation. We then do a blend to get new values and we return + // a new style. + ASSERT(renderer->element()); // FIXME: We do not animate generated content yet. + + CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer); + RefPtr<RenderStyle> blendedStyle = rendererAnimations->animate(renderer, oldStyle, newStyle); + + m_data->updateAnimationTimer(); + + if (blendedStyle != newStyle) { + // If the animations/transitions change opacity or transform, we neeed to update + // the style to impose the stacking rules. Note that this is also + // done in CSSStyleSelector::adjustRenderStyle(). + if (blendedStyle->hasAutoZIndex() && (blendedStyle->opacity() < 1.0f || blendedStyle->hasTransform())) + blendedStyle->setZIndex(0); + } + return blendedStyle.release(); +} + +void AnimationController::setAnimationStartTime(RenderObject* renderer, double t) +{ + CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer); + rendererAnimations->setAnimationStartTime(t); +} + +void AnimationController::setTransitionStartTime(RenderObject* renderer, int property, double t) +{ + CompositeAnimation* rendererAnimations = m_data->accessCompositeAnimation(renderer); + rendererAnimations->setTransitionStartTime(property, t); +} + +bool AnimationController::isAnimatingPropertyOnRenderer(RenderObject* renderer, int property, bool isRunningNow) const +{ + return m_data->isAnimatingPropertyOnRenderer(renderer, property, isRunningNow); +} + +void AnimationController::suspendAnimations(Document* document) +{ + m_data->suspendAnimations(document); +} + +void AnimationController::resumeAnimations(Document* document) +{ + m_data->resumeAnimations(document); +} + +void AnimationController::startUpdateRenderingDispatcher() +{ + m_data->startUpdateRenderingDispatcher(); +} + +void AnimationController::styleAvailable() +{ + if (!m_numStyleAvailableWaiters) + return; + + m_data->styleAvailable(); +} + +} // namespace WebCore diff --git a/WebCore/page/animation/AnimationController.h b/WebCore/page/animation/AnimationController.h new file mode 100644 index 0000000..bc13a2a --- /dev/null +++ b/WebCore/page/animation/AnimationController.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 AnimationController_h +#define AnimationController_h + +#include <wtf/Forward.h> + +namespace WebCore { + +class AnimationControllerPrivate; +class Document; +class Frame; +class RenderObject; +class RenderStyle; + +class AnimationController { +public: + AnimationController(Frame*); + ~AnimationController(); + + void cancelAnimations(RenderObject*); + PassRefPtr<RenderStyle> updateAnimations(RenderObject*, RenderStyle* newStyle); + + void setAnimationStartTime(RenderObject*, double t); + void setTransitionStartTime(RenderObject*, int property, double t); + + bool isAnimatingPropertyOnRenderer(RenderObject*, int property, bool isRunningNow) const; + + void suspendAnimations(Document*); + void resumeAnimations(Document*); + void updateAnimationTimer(); + + void startUpdateRenderingDispatcher(); + + void styleAvailable(); + + void setWaitingForStyleAvailable(bool waiting) + { + if (waiting) + m_numStyleAvailableWaiters++; + else + m_numStyleAvailableWaiters--; + } + +private: + AnimationControllerPrivate* m_data; + unsigned m_numStyleAvailableWaiters; +}; + +} // namespace WebCore + +#endif // AnimationController_h diff --git a/WebCore/page/animation/CompositeAnimation.cpp b/WebCore/page/animation/CompositeAnimation.cpp new file mode 100644 index 0000000..2ae68d9 --- /dev/null +++ b/WebCore/page/animation/CompositeAnimation.cpp @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "CompositeAnimation.h" + +#include "AnimationController.h" +#include "CSSPropertyNames.h" +#include "ImplicitAnimation.h" +#include "KeyframeAnimation.h" +#include "RenderObject.h" +#include "RenderStyle.h" + +namespace WebCore { + +class CompositeAnimationPrivate { +public: + CompositeAnimationPrivate(AnimationController* animationController, CompositeAnimation* compositeAnimation) + : m_isSuspended(false) + , m_animationController(animationController) + , m_compositeAnimation(compositeAnimation) + , m_numStyleAvailableWaiters(0) + { + } + + ~CompositeAnimationPrivate(); + + PassRefPtr<RenderStyle> animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); + + void setAnimating(bool); + bool isAnimating() const; + + const KeyframeAnimation* getAnimationForProperty(int property) const; + + void resetTransitions(RenderObject*); + void resetAnimations(RenderObject*); + + void cleanupFinishedAnimations(RenderObject*); + + void setAnimationStartTime(double t); + void setTransitionStartTime(int property, double t); + + void suspendAnimations(); + void resumeAnimations(); + bool isSuspended() const { return m_isSuspended; } + + void overrideImplicitAnimations(int property); + void resumeOverriddenImplicitAnimations(int property); + + void styleAvailable(); + + bool isAnimatingProperty(int property, bool isRunningNow) const; + + void setWaitingForStyleAvailable(bool); + +protected: + void updateTransitions(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); + void updateKeyframeAnimations(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); + +private: + typedef HashMap<int, RefPtr<ImplicitAnimation> > CSSPropertyTransitionsMap; + typedef HashMap<AtomicStringImpl*, RefPtr<KeyframeAnimation> > AnimationNameMap; + + CSSPropertyTransitionsMap m_transitions; + AnimationNameMap m_keyframeAnimations; + bool m_isSuspended; + AnimationController* m_animationController; + CompositeAnimation* m_compositeAnimation; + unsigned m_numStyleAvailableWaiters; +}; + +CompositeAnimationPrivate::~CompositeAnimationPrivate() +{ + m_transitions.clear(); + m_keyframeAnimations.clear(); +} + +void CompositeAnimationPrivate::updateTransitions(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +{ + // If currentStyle is null, we don't do transitions + if (!currentStyle || !targetStyle->transitions()) + return; + + // Check to see if we need to update the active transitions + for (size_t i = 0; i < targetStyle->transitions()->size(); ++i) { + const Animation* anim = targetStyle->transitions()->animation(i); + bool isActiveTransition = anim->duration() || anim->delay() > 0; + + int prop = anim->property(); + + if (prop == cAnimateNone) + continue; + + bool all = prop == cAnimateAll; + + // Handle both the 'all' and single property cases. For the single prop case, we make only one pass + // through the loop. + for (int propertyIndex = 0; propertyIndex < AnimationBase::getNumProperties(); ++propertyIndex) { + if (all) { + // Get the next property + prop = AnimationBase::getPropertyAtIndex(propertyIndex); + } + + // ImplicitAnimations are always hashed by actual properties, never cAnimateAll + ASSERT(prop > firstCSSProperty && prop < (firstCSSProperty + numCSSProperties)); + + // If there is a running animation for this property, the transition is overridden + // and we have to use the unanimatedStyle from the animation. We do the test + // against the unanimated style here, but we "override" the transition later. + const KeyframeAnimation* keyframeAnim = getAnimationForProperty(prop); + RenderStyle* fromStyle = keyframeAnim ? keyframeAnim->unanimatedStyle() : currentStyle; + + // See if there is a current transition for this prop + ImplicitAnimation* implAnim = m_transitions.get(prop).get(); + bool equal = true; + + if (implAnim) { + // This implAnim might not be an already running transition. It might be + // newly added to the list in a previous iteration. This would happen if + // you have both an explicit transition-property and 'all' in the same + // list. In this case, the latter one overrides the earlier one, so we + // behave as though this is a running animation being replaced. + if (!isActiveTransition) + m_transitions.remove(prop); + else if (!implAnim->isTargetPropertyEqual(prop, targetStyle)) { + m_transitions.remove(prop); + equal = false; + } + } else { + // We need to start a transition if it is active and the properties don't match + equal = !isActiveTransition || AnimationBase::propertiesEqual(prop, fromStyle, targetStyle); + } + + if (!equal) { + // Add the new transition + m_transitions.set(prop, ImplicitAnimation::create(const_cast<Animation*>(anim), prop, renderer, m_compositeAnimation, fromStyle)); + } + + // We only need one pass for the single prop case + if (!all) + break; + } + } +} + +void CompositeAnimationPrivate::updateKeyframeAnimations(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +{ + // Nothing to do if we don't have any animations, and didn't have any before + if (m_keyframeAnimations.isEmpty() && !targetStyle->hasAnimations()) + return; + + // Nothing to do if the current and target animations are the same + if (currentStyle && currentStyle->hasAnimations() && targetStyle->hasAnimations() && *(currentStyle->animations()) == *(targetStyle->animations())) + return; + + // Mark all existing animations as no longer active + AnimationNameMap::const_iterator kfend = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) + it->second->setIndex(-1); + + // Now mark any still active animations as active and add any new animations + if (targetStyle->animations()) { + int numAnims = targetStyle->animations()->size(); + for (int i = 0; i < numAnims; ++i) { + const Animation* anim = targetStyle->animations()->animation(i); + AtomicString animationName(anim->name()); + + if (!anim->isValidAnimation()) + continue; + + // See if there is a current animation for this name + RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(animationName.impl()); + + if (keyframeAnim) { + // There is one so it is still active + + // Animations match, but play states may differ. update if needed + keyframeAnim->updatePlayState(anim->playState() == AnimPlayStatePlaying); + + // Set the saved animation to this new one, just in case the play state has changed + keyframeAnim->setAnimation(anim); + keyframeAnim->setIndex(i); + } else if ((anim->duration() || anim->delay()) && anim->iterationCount()) { + keyframeAnim = KeyframeAnimation::create(const_cast<Animation*>(anim), renderer, i, m_compositeAnimation, currentStyle ? currentStyle : targetStyle); + m_keyframeAnimations.set(keyframeAnim->name().impl(), keyframeAnim); + } + } + } + + // Make a list of animations to be removed + Vector<AtomicStringImpl*> animsToBeRemoved; + kfend = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) { + KeyframeAnimation* keyframeAnim = it->second.get(); + if (keyframeAnim->index() < 0) + animsToBeRemoved.append(keyframeAnim->name().impl()); + } + + // Now remove the animations from the list + for (size_t j = 0; j < animsToBeRemoved.size(); ++j) + m_keyframeAnimations.remove(animsToBeRemoved[j]); +} + +PassRefPtr<RenderStyle> CompositeAnimationPrivate::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +{ + RefPtr<RenderStyle> resultStyle; + + // Update animations first so we can see if any transitions are overridden + updateKeyframeAnimations(renderer, currentStyle, targetStyle); + + // We don't do any transitions if we don't have a currentStyle (on startup) + updateTransitions(renderer, currentStyle, targetStyle); + + if (currentStyle) { + // Now that we have transition objects ready, let them know about the new goal state. We want them + // to fill in a RenderStyle*& only if needed. + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + if (ImplicitAnimation* anim = it->second.get()) + anim->animate(m_compositeAnimation, renderer, currentStyle, targetStyle, resultStyle); + } + } + + // Now that we have animation objects ready, let them know about the new goal state. We want them + // to fill in a RenderStyle*& only if needed. + if (targetStyle->hasAnimations()) { + for (size_t i = 0; i < targetStyle->animations()->size(); ++i) { + const Animation* anim = targetStyle->animations()->animation(i); + + if (anim->isValidAnimation()) { + AtomicString animationName(anim->name()); + RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(animationName.impl()); + if (keyframeAnim) + keyframeAnim->animate(m_compositeAnimation, renderer, currentStyle, targetStyle, resultStyle); + } + } + } + + cleanupFinishedAnimations(renderer); + + return resultStyle ? resultStyle.release() : targetStyle; +} + +// "animating" means that something is running that requires the timer to keep firing +void CompositeAnimationPrivate::setAnimating(bool animating) +{ + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* transition = it->second.get(); + transition->setAnimating(animating); + } + + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + anim->setAnimating(animating); + } +} + +bool CompositeAnimationPrivate::isAnimating() const +{ + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* transition = it->second.get(); + if (transition && transition->isAnimating() && transition->running()) + return true; + } + + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim && !anim->paused() && anim->isAnimating() && anim->active()) + return true; + } + + return false; +} + +const KeyframeAnimation* CompositeAnimationPrivate::getAnimationForProperty(int property) const +{ + const KeyframeAnimation* retval = 0; + + // We want to send back the last animation with the property if there are multiples. + // So we need to iterate through all animations + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + const KeyframeAnimation* anim = it->second.get(); + if (anim->hasAnimationForProperty(property)) + retval = anim; + } + + return retval; +} + +void CompositeAnimationPrivate::resetTransitions(RenderObject* renderer) +{ + m_transitions.clear(); +} + +void CompositeAnimationPrivate::resetAnimations(RenderObject*) +{ + m_keyframeAnimations.clear(); +} + +void CompositeAnimationPrivate::cleanupFinishedAnimations(RenderObject* renderer) +{ + if (isSuspended()) + return; + + // Make a list of transitions to be deleted + Vector<int> finishedTransitions; + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (!anim) + continue; + if (anim->postActive()) + finishedTransitions.append(anim->animatingProperty()); + } + + // Delete them + size_t finishedTransitionCount = finishedTransitions.size(); + for (size_t i = 0; i < finishedTransitionCount; ++i) + m_transitions.remove(finishedTransitions[i]); + + // Make a list of animations to be deleted + Vector<AtomicStringImpl*> finishedAnimations; + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (!anim) + continue; + if (anim->postActive()) + finishedAnimations.append(anim->name().impl()); + } + + // Delete them + size_t finishedAnimationCount = finishedAnimations.size(); + for (size_t i = 0; i < finishedAnimationCount; ++i) + m_keyframeAnimations.remove(finishedAnimations[i]); +} + +void CompositeAnimationPrivate::setAnimationStartTime(double t) +{ + // Set start time on all animations waiting for it + AnimationNameMap::const_iterator end = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != end; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim && anim->waitingForStartTime()) + anim->updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, t); + } +} + +void CompositeAnimationPrivate::setTransitionStartTime(int property, double t) +{ + // Set the start time for given property transition + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->waitingForStartTime() && anim->animatingProperty() == property) + anim->updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, t); + } +} + +void CompositeAnimationPrivate::suspendAnimations() +{ + if (m_isSuspended) + return; + + m_isSuspended = true; + + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + if (KeyframeAnimation* anim = it->second.get()) + anim->updatePlayState(false); + } + + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->hasStyle()) + anim->updatePlayState(false); + } +} + +void CompositeAnimationPrivate::resumeAnimations() +{ + if (!m_isSuspended) + return; + + m_isSuspended = false; + + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim && anim->playStatePlaying()) + anim->updatePlayState(true); + } + + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->hasStyle()) + anim->updatePlayState(true); + } +} + +void CompositeAnimationPrivate::overrideImplicitAnimations(int property) +{ + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->animatingProperty() == property) + anim->setOverridden(true); + } +} + +void CompositeAnimationPrivate::resumeOverriddenImplicitAnimations(int property) +{ + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->animatingProperty() == property) + anim->setOverridden(false); + } +} + +static inline bool compareAnimationIndices(RefPtr<KeyframeAnimation> a, const RefPtr<KeyframeAnimation> b) +{ + return a->index() < b->index(); +} + +void CompositeAnimationPrivate::styleAvailable() +{ + if (m_numStyleAvailableWaiters == 0) + return; + + // We have to go through animations in the order in which they appear in + // the style, because order matters for additivity. + Vector<RefPtr<KeyframeAnimation> > animations(m_keyframeAnimations.size()); + copyValuesToVector(m_keyframeAnimations, animations); + + if (animations.size() > 1) + std::stable_sort(animations.begin(), animations.end(), compareAnimationIndices); + + for (size_t i = 0; i < animations.size(); ++i) { + KeyframeAnimation* anim = animations[i].get(); + if (anim && anim->waitingForStyleAvailable()) + anim->updateStateMachine(AnimationBase::AnimationStateInputStyleAvailable, -1); + } + + CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->waitingForStyleAvailable()) + anim->updateStateMachine(AnimationBase::AnimationStateInputStyleAvailable, -1); + } +} + +bool CompositeAnimationPrivate::isAnimatingProperty(int property, bool isRunningNow) const +{ + AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); + for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { + KeyframeAnimation* anim = it->second.get(); + if (anim && anim->isAnimatingProperty(property, isRunningNow)) + return true; + } + + CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); + for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { + ImplicitAnimation* anim = it->second.get(); + if (anim && anim->isAnimatingProperty(property, isRunningNow)) + return true; + } + return false; +} + +void CompositeAnimationPrivate::setWaitingForStyleAvailable(bool waiting) +{ + if (waiting) + m_numStyleAvailableWaiters++; + else + m_numStyleAvailableWaiters--; + m_animationController->setWaitingForStyleAvailable(waiting); +} + +CompositeAnimation::CompositeAnimation(AnimationController* animationController) + : m_data(new CompositeAnimationPrivate(animationController, this)) +{ +} + +CompositeAnimation::~CompositeAnimation() +{ + delete m_data; +} + +PassRefPtr<RenderStyle> CompositeAnimation::animate(RenderObject* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +{ + return m_data->animate(renderer, currentStyle, targetStyle); +} + +bool CompositeAnimation::isAnimating() const +{ + return m_data->isAnimating(); +} + +void CompositeAnimation::setWaitingForStyleAvailable(bool b) +{ + m_data->setWaitingForStyleAvailable(b); +} + +void CompositeAnimation::resetTransitions(RenderObject* renderer) +{ + m_data->resetTransitions(renderer); +} + +void CompositeAnimation::suspendAnimations() +{ + m_data->suspendAnimations(); +} + +void CompositeAnimation::resumeAnimations() +{ + m_data->resumeAnimations(); +} + +bool CompositeAnimation::isSuspended() const +{ + return m_data->isSuspended(); +} + +void CompositeAnimation::styleAvailable() +{ + m_data->styleAvailable(); +} + +void CompositeAnimation::setAnimating(bool b) +{ + m_data->setAnimating(b); +} + +bool CompositeAnimation::isAnimatingProperty(int property, bool isRunningNow) const +{ + return m_data->isAnimatingProperty(property, isRunningNow); +} + +void CompositeAnimation::setAnimationStartTime(double t) +{ + m_data->setAnimationStartTime(t); +} + +void CompositeAnimation::setTransitionStartTime(int property, double t) +{ + m_data->setTransitionStartTime(property, t); +} + +void CompositeAnimation::overrideImplicitAnimations(int property) +{ + m_data->overrideImplicitAnimations(property); +} + +void CompositeAnimation::resumeOverriddenImplicitAnimations(int property) +{ + m_data->resumeOverriddenImplicitAnimations(property); +} + +} // namespace WebCore diff --git a/WebCore/page/animation/CompositeAnimation.h b/WebCore/page/animation/CompositeAnimation.h new file mode 100644 index 0000000..13f1179 --- /dev/null +++ b/WebCore/page/animation/CompositeAnimation.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 CompositeAnimation_h +#define CompositeAnimation_h + +#include "AtomicString.h" + +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class CompositeAnimationPrivate; +class AnimationController; +class RenderObject; +class RenderStyle; + +// A CompositeAnimation represents a collection of animations that are running +// on a single RenderObject, such as a number of properties transitioning at once. +class CompositeAnimation : public Noncopyable { +public: + CompositeAnimation(AnimationController* animationController); + ~CompositeAnimation(); + + PassRefPtr<RenderStyle> animate(RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle); + bool isAnimating() const; + + void setWaitingForStyleAvailable(bool); + void resetTransitions(RenderObject*); + + void suspendAnimations(); + void resumeAnimations(); + bool isSuspended() const; + + void styleAvailable(); + void setAnimating(bool); + bool isAnimatingProperty(int property, bool isRunningNow) const; + + void setAnimationStartTime(double t); + void setTransitionStartTime(int property, double t); + + void overrideImplicitAnimations(int property); + void resumeOverriddenImplicitAnimations(int property); + +private: + CompositeAnimationPrivate* m_data; +}; + +} // namespace WebCore + +#endif // CompositeAnimation_h diff --git a/WebCore/page/animation/ImplicitAnimation.cpp b/WebCore/page/animation/ImplicitAnimation.cpp new file mode 100644 index 0000000..4d470e4 --- /dev/null +++ b/WebCore/page/animation/ImplicitAnimation.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "CSSPropertyNames.h" +#include "EventNames.h" +#include "ImplicitAnimation.h" +#include "RenderObject.h" + +namespace WebCore { + +ImplicitAnimation::ImplicitAnimation(const Animation* transition, int animatingProperty, RenderObject* renderer, CompositeAnimation* compAnim, RenderStyle* fromStyle) + : AnimationBase(transition, renderer, compAnim) + , m_transitionProperty(transition->property()) + , m_animatingProperty(animatingProperty) + , m_overridden(false) + , m_fromStyle(fromStyle) +{ + ASSERT(animatingProperty != cAnimateAll); +} + +ImplicitAnimation::~ImplicitAnimation() +{ + // Do the cleanup here instead of in the base class so the specialized methods get called + if (!postActive()) + updateStateMachine(AnimationStateInputEndAnimation, -1); +} + +bool ImplicitAnimation::shouldSendEventForListener(Document::ListenerType inListenerType) +{ + return m_object->document()->hasListenerType(inListenerType); +} + +void ImplicitAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, RenderStyle* currentStyle, + RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +{ + if (paused()) + return; + + // If we get this far and the animation is done, it means we are cleaning up a just finished animation. + // So just return. Everything is already all cleaned up. + if (postActive()) + return; + + // Reset to start the transition if we are new + if (isNew()) + reset(targetStyle); + + // Run a cycle of animation. + // We know we will need a new render style, so make one if needed + if (!animatedStyle) + animatedStyle = RenderStyle::clone(targetStyle); + + if (blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0))) + setAnimating(); +} + +void ImplicitAnimation::onAnimationEnd(double elapsedTime) +{ + if (!sendTransitionEvent(eventNames().webkitTransitionEndEvent, elapsedTime)) { + // We didn't dispatch an event, which would call endAnimation(), so we'll just call it here. + endAnimation(true); + } +} + +bool ImplicitAnimation::sendTransitionEvent(const AtomicString& eventType, double elapsedTime) +{ + if (eventType == eventNames().webkitTransitionEndEvent) { + Document::ListenerType listenerType = Document::TRANSITIONEND_LISTENER; + + if (shouldSendEventForListener(listenerType)) { + String propertyName; + if (m_animatingProperty != cAnimateAll) + propertyName = getPropertyName(static_cast<CSSPropertyID>(m_animatingProperty)); + + // Dispatch the event + RefPtr<Element> element = 0; + if (m_object->node() && m_object->node()->isElementNode()) + element = static_cast<Element*>(m_object->node()); + + ASSERT(!element || element->document() && !element->document()->inPageCache()); + if (!element) + return false; + + // Keep a reference to this ImplicitAnimation so it doesn't go away in the handler + RefPtr<ImplicitAnimation> retainer(this); + + // Call the event handler + element->dispatchWebKitTransitionEvent(eventType, propertyName, elapsedTime); + + // Restore the original (unanimated) style + if (eventType == eventNames().webkitAnimationEndEvent && element->renderer()) + setChanged(element.get()); + + return true; // Did dispatch an event + } + } + + return false; // Didn't dispatch an event +} + +void ImplicitAnimation::reset(RenderStyle* to) +{ + ASSERT(to); + ASSERT(m_fromStyle); + + + m_toStyle = to; + + // Restart the transition + if (m_fromStyle && m_toStyle) + updateStateMachine(AnimationStateInputRestartAnimation, -1); + + // set the transform animation list + validateTransformFunctionList(); +} + +void ImplicitAnimation::setOverridden(bool b) +{ + if (b == m_overridden) + return; + + m_overridden = b; + updateStateMachine(m_overridden ? AnimationStateInputPauseOverride : AnimationStateInputResumeOverride, -1); +} + +bool ImplicitAnimation::affectsProperty(int property) const +{ + return (m_animatingProperty == property); +} + +bool ImplicitAnimation::isTargetPropertyEqual(int prop, const RenderStyle* targetStyle) +{ + return propertiesEqual(prop, m_toStyle.get(), targetStyle); +} + +void ImplicitAnimation::blendPropertyValueInStyle(int prop, RenderStyle* currentStyle) +{ + blendProperties(this, prop, currentStyle, m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); +} + +void ImplicitAnimation::validateTransformFunctionList() +{ + m_transformFunctionListValid = false; + + if (!m_fromStyle || !m_toStyle) + return; + + const TransformOperations* val = &m_fromStyle->transform(); + const TransformOperations* toVal = &m_toStyle->transform(); + + if (val->operations().isEmpty()) + val = toVal; + + if (val->operations().isEmpty()) + return; + + // See if the keyframes are valid + if (val != toVal) { + // A list of length 0 matches anything + if (!toVal->operations().isEmpty()) { + // If the sizes of the function lists don't match, the lists don't match + if (val->operations().size() != toVal->operations().size()) + return; + + // If the types of each function are not the same, the lists don't match + for (size_t j = 0; j < val->operations().size(); ++j) { + if (!val->operations()[j]->isSameType(*toVal->operations()[j])) + return; + } + } + } + + // Keyframes are valid + m_transformFunctionListValid = true; +} + +} // namespace WebCore diff --git a/WebCore/page/animation/ImplicitAnimation.h b/WebCore/page/animation/ImplicitAnimation.h new file mode 100644 index 0000000..7c9d50f --- /dev/null +++ b/WebCore/page/animation/ImplicitAnimation.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 ImplicitAnimation_h +#define ImplicitAnimation_h + +#include "AnimationBase.h" +#include "Document.h" + +namespace WebCore { + +// An ImplicitAnimation tracks the state of a transition of a specific CSS property +// for a single RenderObject. +class ImplicitAnimation : public AnimationBase { +public: + static PassRefPtr<ImplicitAnimation> create(const Animation* animation, int animatingProperty, RenderObject* renderer, CompositeAnimation* compositeAnimation, RenderStyle* fromStyle) + { + return adoptRef(new ImplicitAnimation(animation, animatingProperty, renderer, compositeAnimation, fromStyle)); + }; + + int transitionProperty() const { return m_transitionProperty; } + int animatingProperty() const { return m_animatingProperty; } + + virtual void onAnimationEnd(double elapsedTime); + + virtual void animate(CompositeAnimation*, RenderObject*, RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle); + virtual void reset(RenderStyle* to); + + void setOverridden(bool); + virtual bool overridden() const { return m_overridden; } + + virtual bool shouldFireEvents() const { return true; } + + virtual bool affectsProperty(int) const; + + bool hasStyle() const { return m_fromStyle && m_toStyle; } + + bool isTargetPropertyEqual(int, const RenderStyle* targetStyle); + + void blendPropertyValueInStyle(int, RenderStyle* currentStyle); + +protected: + bool shouldSendEventForListener(Document::ListenerType); + bool sendTransitionEvent(const AtomicString&, double elapsedTime); + + void validateTransformFunctionList(); + +private: + ImplicitAnimation(const Animation*, int animatingProperty, RenderObject*, CompositeAnimation*, RenderStyle* fromStyle); + virtual ~ImplicitAnimation(); + + int m_transitionProperty; // Transition property as specified in the RenderStyle. May be cAnimateAll + int m_animatingProperty; // Specific property for this ImplicitAnimation + bool m_overridden; // true when there is a keyframe animation that overrides the transitioning property + + // The two styles that we are blending. + RefPtr<RenderStyle> m_fromStyle; + RefPtr<RenderStyle> m_toStyle; +}; + +} // namespace WebCore + +#endif // ImplicitAnimation_h diff --git a/WebCore/page/animation/KeyframeAnimation.cpp b/WebCore/page/animation/KeyframeAnimation.cpp new file mode 100644 index 0000000..69fdd11 --- /dev/null +++ b/WebCore/page/animation/KeyframeAnimation.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "KeyframeAnimation.h" + +#include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" +#include "CompositeAnimation.h" +#include "EventNames.h" +#include "RenderObject.h" +#include "SystemTime.h" + +namespace WebCore { + +KeyframeAnimation::KeyframeAnimation(const Animation* animation, RenderObject* renderer, int index, CompositeAnimation* compAnim, RenderStyle* unanimatedStyle) + : AnimationBase(animation, renderer, compAnim) + , m_keyframes(renderer, animation->name()) + , m_index(index) + , m_unanimatedStyle(unanimatedStyle) +{ + // Get the keyframe RenderStyles + if (m_object && m_object->element() && m_object->element()->isElementNode()) + m_object->document()->styleSelector()->keyframeStylesForAnimation(static_cast<Element*>(m_object->element()), unanimatedStyle, m_keyframes); + + // Update the m_transformFunctionListValid flag based on whether the function lists in the keyframes match. + validateTransformFunctionList(); +} + +KeyframeAnimation::~KeyframeAnimation() +{ + // Do the cleanup here instead of in the base class so the specialized methods get called + if (!postActive()) + updateStateMachine(AnimationStateInputEndAnimation, -1); +} + +void KeyframeAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, const RenderStyle* currentStyle, + const RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +{ + // If we have not yet started, we will not have a valid start time, so just start the animation if needed. + if (isNew() && m_animation->playState() == AnimPlayStatePlaying) + updateStateMachine(AnimationStateInputStartAnimation, -1); + + // If we get this far and the animation is done, it means we are cleaning up a just finished animation. + // If so, we need to send back the targetStyle. + if (postActive()) { + if (!animatedStyle) + animatedStyle = const_cast<RenderStyle*>(targetStyle); + return; + } + + // If we are waiting for the start timer, we don't want to change the style yet. + // Special case - if the delay time is 0, then we do want to set the first frame of the + // animation right away. This avoids a flash when the animation starts. + if (waitingToStart() && m_animation->delay() > 0) + return; + + // FIXME: we need to be more efficient about determining which keyframes we are animating between. + // We should cache the last pair or something. + + // Find the first key + double elapsedTime = (m_startTime > 0) ? ((!paused() ? currentTime() : m_pauseTime) - m_startTime) : 0; + if (elapsedTime < 0) + elapsedTime = 0; + + double t = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1; + int i = static_cast<int>(t); + t -= i; + if (m_animation->direction() && (i & 1)) + t = 1 - t; + + const RenderStyle* fromStyle = 0; + const RenderStyle* toStyle = 0; + double scale = 1; + double offset = 0; + Vector<KeyframeValue>::const_iterator endKeyframes = m_keyframes.endKeyframes(); + for (Vector<KeyframeValue>::const_iterator it = m_keyframes.beginKeyframes(); it != endKeyframes; ++it) { + if (t < it->key()) { + // The first key should always be 0, so we should never succeed on the first key + if (!fromStyle) + break; + scale = 1.0 / (it->key() - offset); + toStyle = it->style(); + break; + } + + offset = it->key(); + fromStyle = it->style(); + } + + // If either style is 0 we have an invalid case, just stop the animation. + if (!fromStyle || !toStyle) { + updateStateMachine(AnimationStateInputEndAnimation, -1); + return; + } + + // Run a cycle of animation. + // We know we will need a new render style, so make one if needed. + if (!animatedStyle) + animatedStyle = RenderStyle::clone(targetStyle); + + const TimingFunction* timingFunction = 0; + if (fromStyle->animations() && fromStyle->animations()->size() > 0) + timingFunction = &(fromStyle->animations()->animation(0)->timingFunction()); + + double prog = progress(scale, offset, timingFunction); + + HashSet<int>::const_iterator endProperties = m_keyframes.endProperties(); + for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { + if (blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, prog)) + setAnimating(); + } +} + +bool KeyframeAnimation::hasAnimationForProperty(int property) const +{ + HashSet<int>::const_iterator end = m_keyframes.endProperties(); + for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) { + if (*it == property) + return true; + } + + return false; +} + +void KeyframeAnimation::endAnimation(bool) +{ + // Restore the original (unanimated) style + if (m_object) + setChanged(m_object->element()); +} + +bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listenerType) +{ + return m_object->document()->hasListenerType(listenerType); +} + +void KeyframeAnimation::onAnimationStart(double elapsedTime) +{ + sendAnimationEvent(eventNames().webkitAnimationStartEvent, elapsedTime); +} + +void KeyframeAnimation::onAnimationIteration(double elapsedTime) +{ + sendAnimationEvent(eventNames().webkitAnimationIterationEvent, elapsedTime); +} + +void KeyframeAnimation::onAnimationEnd(double elapsedTime) +{ + if (!sendAnimationEvent(eventNames().webkitAnimationEndEvent, elapsedTime)) { + // We didn't dispatch an event, which would call endAnimation(), so we'll just call it here. + endAnimation(true); + } +} + +bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime) +{ + Document::ListenerType listenerType; + if (eventType == eventNames().webkitAnimationIterationEvent) + listenerType = Document::ANIMATIONITERATION_LISTENER; + else if (eventType == eventNames().webkitAnimationEndEvent) + listenerType = Document::ANIMATIONEND_LISTENER; + else { + ASSERT(eventType == eventNames().webkitAnimationStartEvent); + listenerType = Document::ANIMATIONSTART_LISTENER; + } + + if (shouldSendEventForListener(listenerType)) { + // Dispatch the event + RefPtr<Element> element; + if (m_object->node() && m_object->node()->isElementNode()) + element = static_cast<Element*>(m_object->node()); + + ASSERT(!element || element->document() && !element->document()->inPageCache()); + if (!element) + return false; + + // Keep a reference to this ImplicitAnimation so it doesn't go away in the handler + RefPtr<KeyframeAnimation> retainer(this); + + // Call the event handler + element->dispatchWebKitAnimationEvent(eventType, m_keyframes.animationName(), elapsedTime); + + // Restore the original (unanimated) style + if (eventType == eventNames().webkitAnimationEndEvent && element->renderer()) + setChanged(element.get()); + + return true; // Did dispatch an event + } + + return false; // Did not dispatch an event +} + +void KeyframeAnimation::overrideAnimations() +{ + // This will override implicit animations that match the properties in the keyframe animation + HashSet<int>::const_iterator end = m_keyframes.endProperties(); + for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) + compositeAnimation()->overrideImplicitAnimations(*it); +} + +void KeyframeAnimation::resumeOverriddenAnimations() +{ + // This will resume overridden implicit animations + HashSet<int>::const_iterator end = m_keyframes.endProperties(); + for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) + compositeAnimation()->resumeOverriddenImplicitAnimations(*it); +} + +bool KeyframeAnimation::affectsProperty(int property) const +{ + HashSet<int>::const_iterator end = m_keyframes.endProperties(); + for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) { + if (*it == property) + return true; + } + return false; +} + +void KeyframeAnimation::validateTransformFunctionList() +{ + m_transformFunctionListValid = false; + + if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform)) + return; + + Vector<KeyframeValue>::const_iterator end = m_keyframes.endKeyframes(); + + // Empty transforms match anything, so find the first non-empty entry as the reference + size_t firstIndex = 0; + Vector<KeyframeValue>::const_iterator firstIt = end; + + for (Vector<KeyframeValue>::const_iterator it = m_keyframes.beginKeyframes(); it != end; ++it, ++firstIndex) { + if (it->style()->transform().operations().size() > 0) { + firstIt = it; + break; + } + } + + if (firstIt == end) + return; + + const TransformOperations* firstVal = &firstIt->style()->transform(); + + // See if the keyframes are valid + for (Vector<KeyframeValue>::const_iterator it = firstIt+1; it != end; ++it) { + const TransformOperations* val = &it->style()->transform(); + + // A null transform matches anything + if (val->operations().isEmpty()) + continue; + + // If the sizes of the function lists don't match, the lists don't match + if (firstVal->operations().size() != val->operations().size()) + return; + + // If the types of each function are not the same, the lists don't match + for (size_t j = 0; j < firstVal->operations().size(); ++j) { + if (!firstVal->operations()[j]->isSameType(*val->operations()[j])) + return; + } + } + + // Keyframes are valid + m_transformFunctionListValid = true; +} + +} // namespace WebCore diff --git a/WebCore/page/animation/KeyframeAnimation.h b/WebCore/page/animation/KeyframeAnimation.h new file mode 100644 index 0000000..55b429a --- /dev/null +++ b/WebCore/page/animation/KeyframeAnimation.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2007 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 KeyframeAnimation_h +#define KeyframeAnimation_h + +#include "AnimationBase.h" +#include "Document.h" +#include "KeyframeList.h" +#include "RenderStyle.h" + +namespace WebCore { + +// A KeyframeAnimation tracks the state of an explicit animation +// for a single RenderObject. +class KeyframeAnimation : public AnimationBase { +public: + static PassRefPtr<KeyframeAnimation> create(const Animation* animation, RenderObject* renderer, int index, CompositeAnimation* compositeAnimation, RenderStyle* unanimatedStyle) + { + return adoptRef(new KeyframeAnimation(animation, renderer, index, compositeAnimation, unanimatedStyle)); + }; + + virtual void animate(CompositeAnimation*, RenderObject*, const RenderStyle* currentStyle, const RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle); + + const AtomicString& name() const { return m_keyframes.animationName(); } + int index() const { return m_index; } + void setIndex(int i) { m_index = i; } + + virtual bool shouldFireEvents() const { return true; } + + bool hasAnimationForProperty(int property) const; + + RenderStyle* unanimatedStyle() const { return m_unanimatedStyle.get(); } + +protected: + virtual void onAnimationStart(double elapsedTime); + virtual void onAnimationIteration(double elapsedTime); + virtual void onAnimationEnd(double elapsedTime); + virtual void endAnimation(bool reset); + + virtual void overrideAnimations(); + virtual void resumeOverriddenAnimations(); + + bool shouldSendEventForListener(Document::ListenerType inListenerType); + bool sendAnimationEvent(const AtomicString&, double elapsedTime); + + virtual bool affectsProperty(int) const; + + void validateTransformFunctionList(); + +private: + KeyframeAnimation(const Animation* animation, RenderObject*, int index, CompositeAnimation*, RenderStyle* unanimatedStyle); + virtual ~KeyframeAnimation(); + + // The keyframes that we are blending. + KeyframeList m_keyframes; + + // The order in which this animation appears in the animation-name style. + int m_index; + + // The style just before we started animation + RefPtr<RenderStyle> m_unanimatedStyle; +}; + +} // namespace WebCore + +#endif // KeyframeAnimation_h diff --git a/WebCore/page/gtk/AXObjectCacheAtk.cpp b/WebCore/page/gtk/AXObjectCacheAtk.cpp new file mode 100644 index 0000000..3535cf1 --- /dev/null +++ b/WebCore/page/gtk/AXObjectCacheAtk.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * + * 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 "AXObjectCache.h" + +#include "AccessibilityObject.h" +#include "AccessibilityObjectWrapperAtk.h" + +namespace WebCore { + +void AXObjectCache::detachWrapper(AccessibilityObject* obj) +{ + webkit_accessible_detach(WEBKIT_ACCESSIBLE(obj->wrapper())); +} + +void AXObjectCache::attachWrapper(AccessibilityObject* obj) +{ + AtkObject* atkObj = ATK_OBJECT(webkit_accessible_new(obj)); + obj->setWrapper(atkObj); + g_object_unref(atkObj); +} + +void AXObjectCache::postNotification(RenderObject*, const String&) +{ +} + +void AXObjectCache::postNotificationToElement(RenderObject*, const String&) +{ +} + +void AXObjectCache::handleFocusedUIElementChanged() +{ +} + +} // namespace WebCore diff --git a/WebCore/page/Plugin.h b/WebCore/page/gtk/AccessibilityObjectAtk.cpp index f5b4c63..b755645 100644 --- a/WebCore/page/Plugin.h +++ b/WebCore/page/gtk/AccessibilityObjectAtk.cpp @@ -1,6 +1,5 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2008 Apple Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,24 +17,14 @@ * Boston, MA 02110-1301, USA. */ -#ifndef Plugin_h -#define Plugin_h - -#include <wtf/RefCounted.h> +#include "config.h" +#include "AccessibilityObject.h" namespace WebCore { - class Widget; +bool AccessibilityObject::accessibilityIgnoreAttachment() const +{ + return false; +} - class Plugin : public RefCounted<Plugin> { - public: - Plugin(Widget* view) : m_view(view) { } - Widget* view() const { return m_view; } - - private: - Widget* m_view; - }; - } // namespace WebCore - -#endif // Plugin_h diff --git a/WebCore/page/gtk/AccessibilityObjectWrapperAtk.cpp b/WebCore/page/gtk/AccessibilityObjectWrapperAtk.cpp new file mode 100644 index 0000000..b46fb60 --- /dev/null +++ b/WebCore/page/gtk/AccessibilityObjectWrapperAtk.cpp @@ -0,0 +1,676 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * + * 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 "AccessibilityObjectWrapperAtk.h" + +#include "AXObjectCache.h" +#include "AccessibilityListBox.h" +#include "AccessibilityRenderObject.h" +#include "AtomicString.h" +#include "CString.h" +#include "Document.h" +#include "Editor.h" +#include "Frame.h" +#include "FrameView.h" +#include "IntRect.h" +#include "NotImplemented.h" + +#include <atk/atk.h> + +using namespace WebCore; + +// Used to provide const char* returns. +static const char* returnString(const String& str) +{ + static CString returnedString; + returnedString = str.utf8(); + return returnedString.data(); +} + +static AccessibilityObject* core(WebKitAccessible* accessible) +{ + if (!accessible) + return 0; + + return accessible->m_object; +} + +static AccessibilityObject* core(AtkObject* object) +{ + if (!WEBKIT_IS_ACCESSIBLE(object)) + return 0; + + return core(WEBKIT_ACCESSIBLE(object)); +} + +static AccessibilityObject* core(AtkAction* action) +{ + return core(ATK_OBJECT(action)); +} + +static AccessibilityObject* core(AtkStreamableContent* streamable) +{ + return core(ATK_OBJECT(streamable)); +} + +static AccessibilityObject* core(AtkText* text) +{ + return core(ATK_OBJECT(text)); +} + +static AccessibilityObject* core(AtkEditableText* text) +{ + return core(ATK_OBJECT(text)); +} + +extern "C" { + +static gpointer parent_class = NULL; + +static void webkit_accessible_init(AtkObject* object, gpointer data) +{ + g_return_if_fail(WEBKIT_IS_ACCESSIBLE(object)); + g_return_if_fail(data); + + if (ATK_OBJECT_CLASS(parent_class)->initialize) + ATK_OBJECT_CLASS(parent_class)->initialize(object, data); + + WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data); +} + +static void webkit_accessible_finalize(GObject* object) +{ + // This is a good time to clear the return buffer. + returnString(String()); + + if (G_OBJECT_CLASS(parent_class)->finalize) + G_OBJECT_CLASS(parent_class)->finalize(object); +} + +static const gchar* webkit_accessible_get_name(AtkObject* object) +{ + // TODO: Deal with later changes. + if (!object->name) + atk_object_set_name(object, core(object)->title().utf8().data()); + return object->name; +} + +static const gchar* webkit_accessible_get_description(AtkObject* object) +{ + // TODO: the Mozilla MSAA implementation prepends "Description: " + // Should we do this too? + + // TODO: Deal with later changes. + if (!object->description) + atk_object_set_description(object, core(object)->accessibilityDescription().utf8().data()); + return object->description; +} + +static AtkObject* webkit_accessible_get_parent(AtkObject* object) +{ + AccessibilityObject* coreParent = core(object)->parentObject(); + + if (!coreParent) + return NULL; + + return coreParent->wrapper(); +} + +static gint webkit_accessible_get_n_children(AtkObject* object) +{ + return core(object)->children().size(); +} + +static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index) +{ + AccessibilityObject* coreObject = core(object); + + g_return_val_if_fail(index >= 0, NULL); + g_return_val_if_fail(static_cast<size_t>(index) < coreObject->children().size(), NULL); + + AccessibilityObject* coreChild = coreObject->children().at(index).get(); + + if (!coreChild) + return NULL; + + AtkObject* child = coreChild->wrapper(); + // TODO: Should we call atk_object_set_parent() here? + //atk_object_set_parent(child, object); + g_object_ref(child); + + return child; +} + +static gint webkit_accessible_get_index_in_parent(AtkObject* object) +{ + // FIXME: This needs to be implemented. + notImplemented(); + return 0; +} + +static AtkRole atkRole(AccessibilityRole role) +{ + switch (role) { + case WebCore::ButtonRole: + return ATK_ROLE_PUSH_BUTTON; + case WebCore::RadioButtonRole: + return ATK_ROLE_RADIO_BUTTON; + case WebCore::CheckBoxRole: + return ATK_ROLE_CHECK_BOX; + case WebCore::SliderRole: + return ATK_ROLE_SLIDER; + case WebCore::TabGroupRole: + return ATK_ROLE_PAGE_TAB_LIST; + case WebCore::TextFieldRole: + case WebCore::TextAreaRole: + case WebCore::ListMarkerRole: + return ATK_ROLE_ENTRY; + case WebCore::StaticTextRole: + return ATK_ROLE_TEXT; //? + case WebCore::OutlineRole: + return ATK_ROLE_TREE; + case WebCore::ColumnRole: + return ATK_ROLE_UNKNOWN; //? + case WebCore::RowRole: + return ATK_ROLE_LIST_ITEM; + case WebCore::GroupRole: + return ATK_ROLE_UNKNOWN; //? + case WebCore::ListRole: + return ATK_ROLE_LIST; + case WebCore::TableRole: + return ATK_ROLE_TABLE; + case WebCore::LinkRole: + case WebCore::WebCoreLinkRole: + return ATK_ROLE_LINK; + case WebCore::ImageMapRole: + case WebCore::ImageRole: + return ATK_ROLE_IMAGE; + default: + return ATK_ROLE_UNKNOWN; + } +} + +static AtkRole webkit_accessible_get_role(AtkObject* object) +{ + return atkRole(core(object)->roleValue()); +} + +static void webkit_accessible_class_init(AtkObjectClass* klass) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + klass->initialize = webkit_accessible_init; + + gobject_class->finalize = webkit_accessible_finalize; + + klass->get_name = webkit_accessible_get_name; + klass->get_description = webkit_accessible_get_description; + klass->get_parent = webkit_accessible_get_parent; + klass->get_n_children = webkit_accessible_get_n_children; + klass->ref_child = webkit_accessible_ref_child; + //klass->get_index_in_parent = webkit_accessible_get_index_in_parent; + klass->get_role = webkit_accessible_get_role; + //klass->get_attributes = webkit_accessible_get_attributes; + //klass->ref_state_set = webkit_accessible_ref_state_set; + //klass->ref_relation_set = webkit_accessible_ref_relation_set; +} + +static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i) +{ + g_return_val_if_fail(i == 0, FALSE); + return core(action)->performDefaultAction(); +} + +static gint webkit_accessible_action_get_n_actions(AtkAction* action) +{ + return 1; +} + +static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i) +{ + g_return_val_if_fail(i == 0, NULL); + // TODO: Need a way to provide/localize action descriptions. + notImplemented(); + return ""; +} + +static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i) +{ + g_return_val_if_fail(i == 0, NULL); + // FIXME: Construct a proper keybinding string. + return returnString(core(action)->accessKey().string()); +} + +static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i) +{ + g_return_val_if_fail(i == 0, NULL); + return returnString(core(action)->actionVerb()); +} + +static void atk_action_interface_init(AtkActionIface* iface) +{ + g_return_if_fail(iface); + + iface->do_action = webkit_accessible_action_do_action; + iface->get_n_actions = webkit_accessible_action_get_n_actions; + iface->get_description = webkit_accessible_action_get_description; + iface->get_keybinding = webkit_accessible_action_get_keybinding; + iface->get_name = webkit_accessible_action_get_name; +} + +// Text + +static gchar* webkit_accessible_text_get_text(AtkText* text, gint start_offset, gint end_offset) +{ + String ret = core(text)->doAXStringForRange(PlainTextRange(start_offset, end_offset - start_offset)); + // TODO: Intentionally copied? + return g_strdup(ret.utf8().data()); +} + +static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset) +{ + notImplemented(); + return NULL; +} + +static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset) +{ + notImplemented(); + return NULL; +} + +static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset) +{ + notImplemented(); + return 0; +} + +static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundary_type, gint* start_offset, gint* end_offset) +{ + notImplemented(); + return NULL; +} + +static gint webkit_accessible_text_get_caret_offset(AtkText* text) +{ + // TODO: Verify this, especially for RTL text. + return core(text)->selectionStart(); +} + +static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* start_offset, gint* end_offset) +{ + notImplemented(); + return NULL; +} + +static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text) +{ + notImplemented(); + return NULL; +} + +static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords) +{ + IntRect extents = core(text)->doAXBoundsForRange(PlainTextRange(offset, 1)); + // FIXME: Use the AtkCoordType + // Requires WebCore::ScrollView::contentsToScreen() to be implemented + +#if 0 + switch(coords) { + case ATK_XY_SCREEN: + extents = core(text)->document()->view()->contentsToScreen(extents); + break; + case ATK_XY_WINDOW: + // No-op + break; + } +#endif + + *x = extents.x(); + *y = extents.y(); + *width = extents.width(); + *height = extents.height(); +} + +static gint webkit_accessible_text_get_character_count(AtkText* text) +{ + return core(text)->textLength(); +} + +static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords) +{ + // FIXME: Use the AtkCoordType + // TODO: Is it correct to ignore range.length? + IntPoint pos(x, y); + PlainTextRange range = core(text)->doAXRangeForPosition(pos); + return range.start; +} + +static gint webkit_accessible_text_get_n_selections(AtkText* text) +{ + notImplemented(); + return 0; +} + +static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selection_num, gint* start_offset, gint* end_offset) +{ + notImplemented(); + return NULL; +} + +static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset) +{ + notImplemented(); + return FALSE; +} + +static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selection_num) +{ + notImplemented(); + return FALSE; +} + +static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selection_num, gint start_offset, gint end_offset) +{ + notImplemented(); + return FALSE; +} + +static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset) +{ + // TODO: Verify + //core(text)->setSelectedTextRange(PlainTextRange(offset, 0)); + AccessibilityObject* coreObject = core(text); + coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(offset, 0))); + return TRUE; +} + +#if 0 +// Signal handlers +static void webkit_accessible_text_text_changed(AtkText* text, gint position, gint length) +{ +} + +static void webkit_accessible_text_text_caret_moved(AtkText* text, gint location) +{ +} + +static void webkit_accessible_text_text_selection_changed(AtkText* text) +{ +} + +static void webkit_accessible_text_text_attributes_changed(AtkText* text) +{ +} + +static void webkit_accessible_text_get_range_extents(AtkText* text, gint start_offset, gint end_offset, AtkCoordType coord_type, AtkTextRectangle* rect) +{ +} + +static AtkTextRange** webkit_accessible_text_get_bounded_ranges(AtkText* text, AtkTextRectangle* rect, AtkCoordType coord_type, AtkTextClipType x_clip_type, AtkTextClipType y_clip_type) +{ +} +#endif + +static void atk_text_interface_init(AtkTextIface* iface) +{ + g_return_if_fail(iface); + + iface->get_text = webkit_accessible_text_get_text; + iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset; + iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset; + iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset; + iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset; + iface->get_caret_offset = webkit_accessible_text_get_caret_offset; + iface->get_run_attributes = webkit_accessible_text_get_run_attributes; + iface->get_default_attributes = webkit_accessible_text_get_default_attributes; + iface->get_character_extents = webkit_accessible_text_get_character_extents; + //iface->get_range_extents = ; + iface->get_character_count = webkit_accessible_text_get_character_count; + iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point; + iface->get_n_selections = webkit_accessible_text_get_n_selections; + iface->get_selection = webkit_accessible_text_get_selection; + + // set methods + iface->add_selection = webkit_accessible_text_add_selection; + iface->remove_selection = webkit_accessible_text_remove_selection; + iface->set_selection = webkit_accessible_text_set_selection; + iface->set_caret_offset = webkit_accessible_text_set_caret_offset; +} + +// EditableText + +static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset) +{ + notImplemented(); + return FALSE; +} + +static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string) +{ + // FIXME: string nullcheck? + core(text)->setValue(String::fromUTF8(string)); +} + +static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position) +{ + // FIXME: string nullcheck? + + AccessibilityObject* coreObject = core(text); + // FIXME: Not implemented in WebCore + //coreObject->setSelectedTextRange(PlainTextRange(*position, 0)); + //coreObject->setSelectedText(String::fromUTF8(string)); + + if (!coreObject->document() || !coreObject->document()->frame()) + return; + coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0))); + coreObject->setFocused(true); + // FIXME: We should set position to the actual inserted text length, which may be less than that requested. + if (coreObject->document()->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0)) + *position += length; +} + +static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos) +{ + notImplemented(); +} + +static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos) +{ + notImplemented(); +} + +static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos) +{ + AccessibilityObject* coreObject = core(text); + // FIXME: Not implemented in WebCore + //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos)); + //coreObject->setSelectedText(String()); + + if (!coreObject->document() || !coreObject->document()->frame()) + return; + coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos))); + coreObject->setFocused(true); + coreObject->document()->frame()->editor()->performDelete(); +} + +static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position) +{ + notImplemented(); +} + +static void atk_editable_text_interface_init(AtkEditableTextIface* iface) +{ + g_return_if_fail(iface); + + iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes; + iface->set_text_contents = webkit_accessible_editable_text_set_text_contents; + iface->insert_text = webkit_accessible_editable_text_insert_text; + iface->copy_text = webkit_accessible_editable_text_copy_text; + iface->cut_text = webkit_accessible_editable_text_cut_text; + iface->delete_text = webkit_accessible_editable_text_delete_text; + iface->paste_text = webkit_accessible_editable_text_paste_text; +} + +// StreamableContent + +static gint webkit_accessible_streamable_content_get_n_mime_types(AtkStreamableContent* streamable) +{ + notImplemented(); + return 0; +} + +static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_mime_type(AtkStreamableContent* streamable, gint i) +{ + notImplemented(); + return ""; +} + +static GIOChannel* webkit_accessible_streamable_content_get_stream(AtkStreamableContent* streamable, const gchar* mime_type) +{ + notImplemented(); + return NULL; +} + +static G_CONST_RETURN gchar* webkit_accessible_streamable_content_get_uri(AtkStreamableContent* streamable, const gchar* mime_type) +{ + notImplemented(); + return NULL; +} + +static void atk_streamable_content_interface_init(AtkStreamableContentIface* iface) +{ + g_return_if_fail(iface); + + iface->get_n_mime_types = webkit_accessible_streamable_content_get_n_mime_types; + iface->get_mime_type = webkit_accessible_streamable_content_get_mime_type; + iface->get_stream = webkit_accessible_streamable_content_get_stream; + iface->get_uri = webkit_accessible_streamable_content_get_uri; +} + +GType webkit_accessible_get_type() +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo tinfo = { + sizeof(WebKitAccessibleClass), + (GBaseInitFunc)NULL, + (GBaseFinalizeFunc)NULL, + (GClassInitFunc)webkit_accessible_class_init, + (GClassFinalizeFunc)NULL, + NULL, /* class data */ + sizeof(WebKitAccessible), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc)NULL, + NULL /* value table */ + }; + + type = g_type_register_static(ATK_TYPE_OBJECT, "WebKitAccessible", &tinfo, static_cast<GTypeFlags>(0)); + + // TODO: Only implement interfaces when necessary, not for all objects. + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + g_type_add_interface_static(type, ATK_TYPE_ACTION, &atk_action_info); + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + g_type_add_interface_static(type, ATK_TYPE_TEXT, &atk_text_info); + + static const GInterfaceInfo atk_editable_text_info = + { + (GInterfaceInitFunc) atk_editable_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + g_type_add_interface_static(type, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info); + + static const GInterfaceInfo atk_streamable_content_info = + { + (GInterfaceInitFunc) atk_streamable_content_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + g_type_add_interface_static(type, ATK_TYPE_STREAMABLE_CONTENT, &atk_streamable_content_info); + } + return type; +} + +WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject) +{ + GType type = WEBKIT_TYPE_ACCESSIBLE; + AtkObject* object = static_cast<AtkObject*>(g_object_new(type, NULL)); + atk_object_initialize(object, coreObject); + return WEBKIT_ACCESSIBLE(object); +} + +AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible) +{ + return accessible->m_object; +} + +// FIXME: Remove this static initialization. +static AXObjectCache* fallbackCache = new AXObjectCache(); + +void webkit_accessible_detach(WebKitAccessible* accessible) +{ + ASSERT(accessible->m_object); + + // We replace the WebCore AccessibilityObject with a fallback object that + // provides default implementations to avoid repetitive null-checking after + // detachment. + + // FIXME: Using fallbackCache->get(ListBoxOptionRole) is a hack. + accessible->m_object = fallbackCache->get(ListBoxOptionRole); +} + +} + +namespace WebCore { + +// AccessibilityObject implementations + +AccessibilityObjectWrapper* AccessibilityObject::wrapper() const +{ + return m_wrapper; +} + +void AccessibilityObject::setWrapper(AccessibilityObjectWrapper* wrapper) +{ + if (m_wrapper) + g_object_unref(m_wrapper); + + m_wrapper = wrapper; + + if (m_wrapper) + g_object_ref(m_wrapper); +} + +} // namespace WebCore diff --git a/WebCore/page/gtk/AccessibilityObjectWrapperAtk.h b/WebCore/page/gtk/AccessibilityObjectWrapperAtk.h new file mode 100644 index 0000000..ecfb99e --- /dev/null +++ b/WebCore/page/gtk/AccessibilityObjectWrapperAtk.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 Nuanti Ltd. + * + * 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 AccessibilityObjectWrapperAtk_h +#define AccessibilityObjectWrapperAtk_h + +#include <atk/atk.h> + +namespace WebCore { + class AccessibilityObject; +} + +G_BEGIN_DECLS + +#define WEBKIT_TYPE_ACCESSIBLE (webkit_accessible_get_type ()) +#define WEBKIT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessible)) +#define WEBKIT_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessibleClass)) +#define WEBKIT_IS_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_ACCESSIBLE)) +#define WEBKIT_IS_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_ACCESSIBLE)) +#define WEBKIT_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessibleClass)) + +typedef struct _WebKitAccessible WebKitAccessible; +typedef struct _WebKitAccessibleClass WebKitAccessibleClass; + +struct _WebKitAccessible +{ + AtkObject parent; + WebCore::AccessibilityObject* m_object; +}; + +struct _WebKitAccessibleClass +{ + AtkObjectClass parent_class; +}; + +GType webkit_accessible_get_type (void) G_GNUC_CONST; + +WebKitAccessible* webkit_accessible_new (WebCore::AccessibilityObject* core_object); + +WebCore::AccessibilityObject* webkit_accessible_get_accessibility_object (WebKitAccessible* accessible); + +void webkit_accessible_detach (WebKitAccessible* accessible); + +G_END_DECLS + +#endif // AccessibilityObjectWrapperAtk_h diff --git a/WebCore/page/gtk/EventHandlerGtk.cpp b/WebCore/page/gtk/EventHandlerGtk.cpp index 8215a4e..bb650b4 100644 --- a/WebCore/page/gtk/EventHandlerGtk.cpp +++ b/WebCore/page/gtk/EventHandlerGtk.cpp @@ -27,7 +27,6 @@ #include "EventHandler.h" #include "ClipboardGtk.h" -#include "EventNames.h" #include "FloatPoint.h" #include "FocusController.h" #include "Frame.h" @@ -36,13 +35,16 @@ #include "MouseEventWithHitTestResults.h" #include "NotImplemented.h" #include "Page.h" -#include "PlatformScrollBar.h" +#include "PlatformKeyboardEvent.h" #include "PlatformWheelEvent.h" #include "RenderWidget.h" +#include "Scrollbar.h" namespace WebCore { -using namespace EventNames; +unsigned EventHandler::s_accessKeyModifiers = PlatformKeyboardEvent::AltKey; + +const double EventHandler::TextDragDelay = 0.0; bool EventHandler::tabsToAllControls(KeyboardEvent* event) const { @@ -95,9 +97,9 @@ bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& event, Widget* wid return static_cast<FrameView*>(widget)->frame()->eventHandler()->handleWheelEvent(event); } -Clipboard* EventHandler::createDraggingClipboard() const +PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const { - return new ClipboardGtk(ClipboardWritable, true); + return ClipboardGtk::create(ClipboardWritable, true); } bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe) @@ -118,10 +120,4 @@ bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& return true; } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults&, PlatformScrollbar* scrollbar) -{ - notImplemented(); - return false; -} - } diff --git a/WebCore/page/gtk/FrameGtk.cpp b/WebCore/page/gtk/FrameGtk.cpp index b2afcbd..3cab5a0 100644 --- a/WebCore/page/gtk/FrameGtk.cpp +++ b/WebCore/page/gtk/FrameGtk.cpp @@ -1,9 +1,4 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com - * Copyright (C) 2007 Holger Hans Peter Freyther - * All rights reserved. - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -28,31 +23,15 @@ #include "config.h" #include "Frame.h" -#include "NotImplemented.h" +#include "NotImplemented.h" namespace WebCore { -KJS::Bindings::Instance* Frame::createScriptInstanceForWidget(Widget*) -{ - notImplemented(); - return 0; -} - -void Frame::clearPlatformScriptObjects() -{ - notImplemented(); -} - DragImageRef Frame::dragImageForSelection() { notImplemented(); return 0; } -void Frame::dashboardRegionsChanged() -{ - notImplemented(); -} - } diff --git a/WebCore/page/inspector/ConsolePanel.js b/WebCore/page/inspector/ConsolePanel.js deleted file mode 100644 index 57bf331..0000000 --- a/WebCore/page/inspector/ConsolePanel.js +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.ConsolePanel = function() -{ - WebInspector.Panel.call(this); - - this.messages = []; - - this.commandHistory = []; - this.commandOffset = 0; - - this.messageList = document.createElement("ol"); - this.messageList.className = "console-message-list"; - this.element.appendChild(this.messageList); - - this.messageList.addEventListener("click", this.messageListClicked.bind(this), true); - - this.consolePrompt = document.createElement("textarea"); - this.consolePrompt.className = "console-prompt"; - this.element.appendChild(this.consolePrompt); - - this.consolePrompt.addEventListener("keydown", this.promptKeyDown.bind(this), false); - - var clearButtonText = WebInspector.UIString("Clear"); - this.clearMessagesElement = document.createElement("button"); - this.clearMessagesElement.appendChild(document.createTextNode(clearButtonText)); - this.clearMessagesElement.title = clearButtonText; - this.clearMessagesElement.addEventListener("click", this.clearButtonClicked.bind(this), false); -} - -WebInspector.ConsolePanel.prototype = { - show: function() - { - WebInspector.Panel.prototype.show.call(this); - WebInspector.consoleListItem.select(); - - this.clearMessagesElement.removeStyleClass("hidden"); - if (!this.clearMessagesElement.parentNode) - document.getElementById("toolbarButtons").appendChild(this.clearMessagesElement); - }, - - hide: function() - { - WebInspector.Panel.prototype.hide.call(this); - WebInspector.consoleListItem.deselect(); - this.clearMessagesElement.addStyleClass("hidden"); - }, - - addMessage: function(msg) - { - if (msg.url in WebInspector.resourceURLMap) { - msg.resource = WebInspector.resourceURLMap[msg.url]; - switch (msg.level) { - case WebInspector.ConsoleMessage.MessageLevel.Warning: - ++msg.resource.warnings; - msg.resource.panel.addMessageToSource(msg); - break; - case WebInspector.ConsoleMessage.MessageLevel.Error: - ++msg.resource.errors; - msg.resource.panel.addMessageToSource(msg); - break; - } - } - this.messages.push(msg); - - var item = msg.toListItem(); - item.message = msg; - this.messageList.appendChild(item); - item.scrollIntoView(false); - }, - - clearMessages: function() - { - for (var i = 0; i < this.messages.length; ++i) { - var resource = this.messages[i].resource; - if (!resource) - continue; - - resource.errors = 0; - resource.warnings = 0; - } - - this.messages = []; - this.messageList.removeChildren(); - }, - - clearButtonClicked: function() - { - this.clearMessages(); - }, - - messageListClicked: function(event) - { - var link = event.target.firstParentOrSelfWithNodeName("a"); - if (link && link.representedNode) { - WebInspector.updateFocusedNode(link.representedNode); - return; - } - - var item = event.target.firstParentOrSelfWithNodeName("li"); - if (!item) - return; - - var resource = item.message.resource; - if (!resource) - return; - - if (link && link.hasStyleClass("console-message-url")) { - WebInspector.navigateToResource(resource); - resource.panel.showSourceLine(item.message.line); - } - - event.stopPropagation(); - event.preventDefault(); - }, - - promptKeyDown: function(event) - { - switch (event.keyIdentifier) { - case "Enter": - this._onEnterPressed(event); - break; - case "Up": - this._onUpPressed(event); - break; - case "Down": - this._onDownPressed(event); - break; - } - }, - - _onEnterPressed: function(event) - { - event.preventDefault(); - event.stopPropagation(); - - var str = this.consolePrompt.value; - if (!str.length) - return; - - this.commandHistory.push(str); - this.commandOffset = 0; - - this.consolePrompt.value = ""; - - var result; - var exception = false; - try { - // This with block is needed to work around http://bugs.webkit.org/show_bug.cgi?id=11399 - with (InspectorController.inspectedWindow()) { - result = eval(str); - } - } catch(e) { - result = e; - exception = true; - } - - var level = exception ? WebInspector.ConsoleMessage.MessageLevel.Error : WebInspector.ConsoleMessage.MessageLevel.Log; - - this.addMessage(new WebInspector.ConsoleCommand(str, this._format(result))); - }, - - _onUpPressed: function(event) - { - event.preventDefault(); - event.stopPropagation(); - - if (this.commandOffset == this.commandHistory.length) - return; - - if (this.commandOffset == 0) - this.tempSavedCommand = this.consolePrompt.value; - - ++this.commandOffset; - this.consolePrompt.value = this.commandHistory[this.commandHistory.length - this.commandOffset]; - this.consolePrompt.moveCursorToEnd(); - }, - - _onDownPressed: function(event) - { - event.preventDefault(); - event.stopPropagation(); - - if (this.commandOffset == 0) - return; - - --this.commandOffset; - - if (this.commandOffset == 0) { - this.consolePrompt.value = this.tempSavedCommand; - this.consolePrompt.moveCursorToEnd(); - delete this.tempSavedCommand; - return; - } - - this.consolePrompt.value = this.commandHistory[this.commandHistory.length - this.commandOffset]; - this.consolePrompt.moveCursorToEnd(); - }, - - _format: function(output) - { - var type = Object.type(output); - if (type === "object") { - if (output instanceof Node) - type = "node"; - } - - // We don't perform any special formatting on these types, so we just - // pass them through the simple _formatvalue function. - var undecoratedTypes = { - "undefined": 1, - "null": 1, - "boolean": 1, - "number": 1, - "date": 1, - "function": 1, - }; - - var formatter; - if (type in undecoratedTypes) - formatter = "_formatvalue"; - else { - formatter = "_format" + type; - if (!(formatter in this)) { - formatter = "_formatobject"; - type = "object"; - } - } - - var span = document.createElement("span"); - span.addStyleClass("console-formatted-" + type); - this[formatter](output, span); - return span; - }, - - _formatvalue: function(val, elem) - { - elem.appendChild(document.createTextNode(val)); - }, - - _formatstring: function(str, elem) - { - elem.appendChild(document.createTextNode("\"" + str + "\"")); - }, - - _formatregexp: function(re, elem) - { - var formatted = String(re).replace(/([\\\/])/g, "\\$1").replace(/\\(\/[gim]*)$/, "$1").substring(1); - elem.appendChild(document.createTextNode(formatted)); - }, - - _formatarray: function(arr, elem) - { - elem.appendChild(document.createTextNode("[")); - for (var i = 0; i < arr.length; ++i) { - elem.appendChild(this._format(arr[i])); - if (i < arr.length - 1) - elem.appendChild(document.createTextNode(", ")); - } - elem.appendChild(document.createTextNode("]")); - }, - - _formatnode: function(node, elem) - { - var anchor = document.createElement("a"); - anchor.innerHTML = node.titleInfo().title; - anchor.representedNode = node; - elem.appendChild(anchor); - }, - - _formatobject: function(obj, elem) - { - elem.appendChild(document.createTextNode(Object.describe(obj))); - }, -} - -WebInspector.ConsolePanel.prototype.__proto__ = WebInspector.Panel.prototype; - -WebInspector.ConsoleMessage = function(source, level, message, line, url) -{ - this.source = source; - this.level = level; - this.message = message; - this.line = line; - this.url = url; -} - -WebInspector.ConsoleMessage.prototype = { - get shortURL() - { - if (this.resource) - return this.resource.displayName; - return this.url; - }, - - toListItem: function() - { - var item = document.createElement("li"); - item.className = "console-message"; - switch (this.source) { - case WebInspector.ConsoleMessage.MessageSource.HTML: - item.className += " console-html-source"; - break; - case WebInspector.ConsoleMessage.MessageSource.XML: - item.className += " console-xml-source"; - break; - case WebInspector.ConsoleMessage.MessageSource.JS: - item.className += " console-js-source"; - break; - case WebInspector.ConsoleMessage.MessageSource.CSS: - item.className += " console-css-source"; - break; - case WebInspector.ConsoleMessage.MessageSource.Other: - item.className += " console-other-source"; - break; - } - - switch (this.level) { - case WebInspector.ConsoleMessage.MessageLevel.Tip: - item.className += " console-tip-level"; - break; - case WebInspector.ConsoleMessage.MessageLevel.Log: - item.className += " console-log-level"; - break; - case WebInspector.ConsoleMessage.MessageLevel.Warning: - item.className += " console-warning-level"; - break; - case WebInspector.ConsoleMessage.MessageLevel.Error: - item.className += " console-error-level"; - } - - var messageDiv = document.createElement("div"); - messageDiv.className = "console-message-message"; - messageDiv.textContent = this.message; - item.appendChild(messageDiv); - - if (this.url && this.url !== "undefined") { - var urlElement = document.createElement("a"); - urlElement.className = "console-message-url"; - - if (this.line > 0) - urlElement.textContent = WebInspector.UIString("%s (line %d)", this.url, this.line); - else - urlElement.textContent = this.url; - - item.appendChild(urlElement); - } - - return item; - }, - - toString: function() - { - var sourceString; - switch (this.source) { - case WebInspector.ConsoleMessage.MessageSource.HTML: - sourceString = "HTML"; - break; - case WebInspector.ConsoleMessage.MessageSource.XML: - sourceString = "XML"; - break; - case WebInspector.ConsoleMessage.MessageSource.JS: - sourceString = "JS"; - break; - case WebInspector.ConsoleMessage.MessageSource.CSS: - sourceString = "CSS"; - break; - case WebInspector.ConsoleMessage.MessageSource.Other: - sourceString = "Other"; - break; - } - - var levelString; - switch (this.level) { - case WebInspector.ConsoleMessage.MessageLevel.Tip: - levelString = "Tip"; - break; - case WebInspector.ConsoleMessage.MessageLevel.Log: - levelString = "Log"; - break; - case WebInspector.ConsoleMessage.MessageLevel.Warning: - levelString = "Warning"; - break; - case WebInspector.ConsoleMessage.MessageLevel.Error: - levelString = "Error"; - break; - } - - return sourceString + " " + levelString + ": " + this.message + "\n" + this.url + " line " + this.line; - } -} - -// Note: Keep these constants in sync with the ones in Chrome.h -WebInspector.ConsoleMessage.MessageSource = { - HTML: 0, - XML: 1, - JS: 2, - CSS: 3, - Other: 4, -}; - -WebInspector.ConsoleMessage.MessageLevel = { - Tip: 0, - Log: 1, - Warning: 2, - Error: 3, -}; - -WebInspector.ConsoleCommand = function(input, output) -{ - this.input = input; - this.output = output; -} - -WebInspector.ConsoleCommand.prototype = { - toListItem: function() - { - var item = document.createElement("li"); - item.className = "console-command"; - - var inputDiv = document.createElement("div"); - inputDiv.className = "console-command-input"; - inputDiv.textContent = this.input; - item.appendChild(inputDiv); - - var outputDiv = document.createElement("div"); - outputDiv.className = "console-command-output"; - outputDiv.appendChild(this.output); - item.appendChild(outputDiv); - - return item; - } -} diff --git a/WebCore/page/inspector/Database.js b/WebCore/page/inspector/Database.js deleted file mode 100644 index 36e89da..0000000 --- a/WebCore/page/inspector/Database.js +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.Database = function(database, domain, name, version) -{ - this.database = database; - this.domain = domain; - this.name = name; - this.version = version; - - this.listItem = new WebInspector.ResourceTreeElement(this); - this.updateTitle(); - - this.category.addResource(this); -} - -WebInspector.Database.prototype = { - get database() - { - return this._database; - }, - - set database(x) - { - if (this._database === x) - return; - this._database = x; - }, - - get name() - { - return this._name; - }, - - set name(x) - { - if (this._name === x) - return; - this._name = x; - this.updateTitleSoon(); - }, - - get version() - { - return this._version; - }, - - set version(x) - { - if (this._version === x) - return; - this._version = x; - }, - - get domain() - { - return this._domain; - }, - - set domain(x) - { - if (this._domain === x) - return; - this._domain = x; - this.updateTitleSoon(); - }, - - get category() - { - return WebInspector.resourceCategories.databases; - }, - - updateTitle: function() - { - delete this.updateTitleTimeout; - - var title = this.name; - - var info = ""; - if (this.domain && (!WebInspector.mainResource || (WebInspector.mainResource && this.domain !== WebInspector.mainResource.domain))) - info = this.domain; - - var fullTitle = "<span class=\"title" + (info && info.length ? "" : " only") + "\">" + title.escapeHTML() + "</span>"; - if (info && info.length) - fullTitle += "<span class=\"info\">" + info.escapeHTML() + "</span>"; - fullTitle += "<div class=\"icon database\"></div>"; - - this.listItem.title = fullTitle; - }, - - get panel() - { - if (!this._panel) - this._panel = new WebInspector.DatabasePanel(this); - return this._panel; - }, - - // Inherit the other functions from the Resource prototype. - updateTitleSoon: WebInspector.Resource.prototype.updateTitleSoon, - select: WebInspector.Resource.prototype.select, - deselect: WebInspector.Resource.prototype.deselect, - attach: WebInspector.Resource.prototype.attach, - detach: WebInspector.Resource.prototype.detach -} diff --git a/WebCore/page/inspector/DatabasePanel.js b/WebCore/page/inspector/DatabasePanel.js deleted file mode 100644 index 9101b9c..0000000 --- a/WebCore/page/inspector/DatabasePanel.js +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.DatabasePanel = function(database, views) -{ - var allViews = [{ title: WebInspector.UIString("Query"), name: "query" }, { title: WebInspector.UIString("Browse"), name: "browse" }]; - if (views) - allViews = allViews.concat(views); - - WebInspector.ResourcePanel.call(this, database, allViews); - - this.currentView = this.views.browse; - - this.queryPromptElement = document.createElement("textarea"); - this.queryPromptElement.className = "database-prompt"; - this.element.appendChild(this.queryPromptElement); - - this.queryPromptElement.addEventListener("keydown", this.queryInputKeyDown.bind(this), false); - - this.queryPromptHistory = []; - this.queryPromptHistoryOffset = 0; - - var queryView = this.views.query; - - queryView.commandListElement = document.createElement("ol"); - queryView.commandListElement.className = "database-command-list"; - queryView.contentElement.appendChild(queryView.commandListElement); - - var panel = this; - queryView.show = function() - { - panel.queryPromptElement.focus(); - this.commandListElement.scrollTop = this.previousScrollTop; - }; - - queryView.hide = function() - { - this.previousScrollTop = this.commandListElement.scrollTop; - }; - - var browseView = this.views.browse; - - browseView.reloadTableElement = document.createElement("button"); - browseView.reloadTableElement.appendChild(document.createElement("img")); - browseView.reloadTableElement.className = "database-table-reload"; - browseView.reloadTableElement.title = WebInspector.UIString("Reload"); - browseView.reloadTableElement.addEventListener("click", this.reloadClicked.bind(this), false); - - browseView.show = function() - { - panel.updateTableList(); - panel.queryPromptElement.focus(); - - this.tableSelectElement.removeStyleClass("hidden"); - if (!this.tableSelectElement.parentNode) - document.getElementById("toolbarButtons").appendChild(this.tableSelectElement); - - this.reloadTableElement.removeStyleClass("hidden"); - if (!this.reloadTableElement.parentNode) - document.getElementById("toolbarButtons").appendChild(this.reloadTableElement); - - this.contentElement.scrollTop = this.previousScrollTop; - }; - - browseView.hide = function() - { - this.tableSelectElement.addStyleClass("hidden"); - this.reloadTableElement.addStyleClass("hidden"); - this.previousScrollTop = this.contentElement.scrollTop; - }; -} - -// FIXME: The function and local variables are a workaround for http://bugs.webkit.org/show_bug.cgi?id=15574. -WebInspector.DatabasePanel.prototype = (function() { -var document = window.document; -var Math = window.Math; -return { - show: function() - { - WebInspector.ResourcePanel.prototype.show.call(this); - this.queryPromptElement.focus(); - }, - - get currentTable() - { - return this._currentTable; - }, - - set currentTable(x) - { - if (this._currentTable === x) - return; - - this._currentTable = x; - - if (x) { - var browseView = this.views.browse; - if (browseView.tableSelectElement) { - var length = browseView.tableSelectElement.options.length; - for (var i = 0; i < length; ++i) { - var option = browseView.tableSelectElement.options[i]; - if (option.value === x) { - browseView.tableSelectElement.selectedIndex = i; - break; - } - } - } - - this.updateTableBrowser(); - } - }, - - reloadClicked: function() - { - this.updateTableList(); - this.updateTableBrowser(); - }, - - updateTableList: function() - { - var browseView = this.views.browse; - if (!browseView.tableSelectElement) { - browseView.tableSelectElement = document.createElement("select"); - browseView.tableSelectElement.className = "database-table-select hidden"; - - var panel = this; - var changeTableFunction = function() - { - var index = browseView.tableSelectElement.selectedIndex; - if (index != -1) - panel.currentTable = browseView.tableSelectElement.options[index].value; - else - panel.currentTable = null; - }; - - browseView.tableSelectElement.addEventListener("change", changeTableFunction, false); - } - - browseView.tableSelectElement.removeChildren(); - - var selectedTableName = this.currentTable; - var tableNames = InspectorController.databaseTableNames(this.resource.database).sort(); - - var length = tableNames.length; - for (var i = 0; i < length; ++i) { - var option = document.createElement("option"); - option.value = tableNames[i]; - option.text = tableNames[i]; - browseView.tableSelectElement.appendChild(option); - - if (tableNames[i] === selectedTableName) - browseView.tableSelectElement.selectedIndex = i; - } - - if (!selectedTableName && length) - this.currentTable = tableNames[0]; - }, - - updateTableBrowser: function() - { - if (!this.currentTable) { - this.views.browse.contentElement.removeChildren(); - return; - } - - var panel = this; - var query = "SELECT * FROM " + this.currentTable; - this.resource.database.transaction(function(tx) - { - tx.executeSql(query, [], function(tx, result) { panel.browseQueryFinished(result) }, function(tx, error) { panel.browseQueryError(error) }); - }, function(tx, error) { panel.browseQueryError(error) }); - }, - - browseQueryFinished: function(result) - { - this.views.browse.contentElement.removeChildren(); - - var table = this._tableForResult(result); - if (!table) { - var emptyMsgElement = document.createElement("div"); - emptyMsgElement.className = "database-table-empty"; - emptyMsgElement.textContent = WebInspector.UIString("The “%s”\ntable is empty.", this.currentTable); - this.views.browse.contentElement.appendChild(emptyMsgElement); - return; - } - - var rowCount = table.getElementsByTagName("tr").length; - var columnCount = table.getElementsByTagName("tr").item(0).getElementsByTagName("th").length; - - var tr = document.createElement("tr"); - tr.className = "database-result-filler-row"; - table.appendChild(tr); - - if (!(rowCount % 2)) - tr.addStyleClass("alternate"); - - for (var i = 0; i < columnCount; ++i) { - var td = document.createElement("td"); - tr.appendChild(td); - } - - table.addStyleClass("database-browse-table"); - this.views.browse.contentElement.appendChild(table); - }, - - browseQueryError: function(error) - { - this.views.browse.contentElement.removeChildren(); - - var errorMsgElement = document.createElement("div"); - errorMsgElement.className = "database-table-error"; - errorMsgElement.textContent = WebInspector.UIString("An error occurred trying to\nread the “%s” table.", this.currentTable); - this.views.browse.contentElement.appendChild(errorMsgElement); - }, - - queryInputKeyDown: function(event) - { - switch (event.keyIdentifier) { - case "Enter": - this._onQueryInputEnterPressed(event); - break; - case "Up": - this._onQueryInputUpPressed(event); - break; - case "Down": - this._onQueryInputDownPressed(event); - break; - } - }, - - appendQueryResult: function(query, result, resultClassName) - { - var commandItem = document.createElement("li"); - commandItem.className = "database-command"; - - var queryDiv = document.createElement("div"); - queryDiv.className = "database-command-query"; - queryDiv.textContent = query; - commandItem.appendChild(queryDiv); - - var resultDiv = document.createElement("div"); - resultDiv.className = "database-command-result"; - commandItem.appendChild(resultDiv); - - if (resultClassName) - resultDiv.addStyleClass(resultClassName); - - if (typeof result === "string" || result instanceof String) - resultDiv.textContent = result; - else if (result && result.nodeName) - resultDiv.appendChild(result); - - this.views.query.commandListElement.appendChild(commandItem); - commandItem.scrollIntoView(false); - }, - - queryFinished: function(query, result) - { - this.appendQueryResult(query, this._tableForResult(result)); - }, - - queryError: function(query, error) - { - if (this.currentView !== this.views.query) - this.currentView = this.views.query; - - if (error.code == 1) - var message = error.message; - else if (error.code == 2) - var message = WebInspector.UIString("Database no longer has expected version."); - else - var message = WebInspector.UIString("An unexpected error %s occured.", error.code); - - this.appendQueryResult(query, message, "error"); - }, - - _onQueryInputEnterPressed: function(event) - { - event.preventDefault(); - event.stopPropagation(); - - var query = this.queryPromptElement.value; - if (!query.length) - return; - - var panel = this; - this.resource.database.transaction(function(tx) - { - tx.executeSql(query, [], function(tx, result) { panel.queryFinished(query, result) }, function(tx, error) { panel.queryError(query, error) }); - }, function(tx, error) { panel.queryError(query, error) }); - - this.queryPromptHistory.push(query); - this.queryPromptHistoryOffset = 0; - - this.queryPromptElement.value = ""; - - if (query.match(/^select /i)) { - if (this.currentView !== this.views.query) - this.currentView = this.views.query; - } else { - if (query.match(/^create /i) || query.match(/^drop table /i)) - this.updateTableList(); - - // FIXME: we should only call updateTableBrowser() is we know the current table was modified - this.updateTableBrowser(); - } - }, - - _onQueryInputUpPressed: function(event) - { - event.preventDefault(); - event.stopPropagation(); - - if (this.queryPromptHistoryOffset == this.queryPromptHistory.length) - return; - - if (this.queryPromptHistoryOffset == 0) - this.tempSavedQuery = this.queryPromptElement.value; - - ++this.queryPromptHistoryOffset; - this.queryPromptElement.value = this.queryPromptHistory[this.queryPromptHistory.length - this.queryPromptHistoryOffset]; - this.queryPromptElement.moveCursorToEnd(); - }, - - _onQueryInputDownPressed: function(event) - { - event.preventDefault(); - event.stopPropagation(); - - if (this.queryPromptHistoryOffset == 0) - return; - - --this.queryPromptHistoryOffset; - - if (this.queryPromptHistoryOffset == 0) { - this.queryPromptElement.value = this.tempSavedQuery; - this.queryPromptElement.moveCursorToEnd(); - delete this.tempSavedQuery; - return; - } - - this.queryPromptElement.value = this.queryPromptHistory[this.queryPromptHistory.length - this.queryPromptHistoryOffset]; - this.queryPromptElement.moveCursorToEnd(); - }, - - _tableForResult: function(result) - { - if (!result.rows.length) - return null; - - var rows = result.rows; - var length = rows.length; - var columnWidths = []; - - var table = document.createElement("table"); - table.className = "database-result-table"; - - var headerRow = document.createElement("tr"); - table.appendChild(headerRow); - - var j = 0; - for (var column in rows.item(0)) { - var th = document.createElement("th"); - headerRow.appendChild(th); - - var div = document.createElement("div"); - div.textContent = column; - div.title = column; - th.appendChild(div); - - columnWidths[j++] = column.length; - } - - for (var i = 0; i < length; ++i) { - var row = rows.item(i); - var tr = document.createElement("tr"); - if (i % 2) - tr.className = "alternate"; - table.appendChild(tr); - - var j = 0; - for (var column in row) { - var td = document.createElement("td"); - tr.appendChild(td); - - var text = row[column]; - var div = document.createElement("div"); - div.textContent = text; - div.title = text; - td.appendChild(div); - - if (text.length > columnWidths[j]) - columnWidths[j] = text.length; - ++j; - } - } - - var totalColumnWidths = 0; - length = columnWidths.length; - for (var i = 0; i < length; ++i) - totalColumnWidths += columnWidths[i]; - - // Calculate the percentage width for the columns. - var minimumPrecent = 5; - var recoupPercent = 0; - for (var i = 0; i < length; ++i) { - columnWidths[i] = Math.round((columnWidths[i] / totalColumnWidths) * 100); - if (columnWidths[i] < minimumPrecent) { - recoupPercent += (minimumPrecent - columnWidths[i]); - columnWidths[i] = minimumPrecent; - } - } - - // Enforce the minimum percentage width. - while (recoupPercent > 0) { - for (var i = 0; i < length; ++i) { - if (columnWidths[i] > minimumPrecent) { - --columnWidths[i]; - --recoupPercent; - if (!recoupPercent) - break; - } - } - } - - length = headerRow.childNodes.length; - for (var i = 0; i < length; ++i) { - var th = headerRow.childNodes[i]; - th.style.width = columnWidths[i] + "%"; - } - - return table; - } -} -})(); - -WebInspector.DatabasePanel.prototype.__proto__ = WebInspector.ResourcePanel.prototype; diff --git a/WebCore/page/inspector/DocumentPanel.js b/WebCore/page/inspector/DocumentPanel.js deleted file mode 100644 index 770f9ba..0000000 --- a/WebCore/page/inspector/DocumentPanel.js +++ /dev/null @@ -1,837 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.DocumentPanel = function(resource, views) -{ - var allViews = [{ title: WebInspector.UIString("DOM"), name: "dom" }]; - if (views) - allViews = allViews.concat(views); - - WebInspector.SourcePanel.call(this, resource, allViews); - - var panel = this; - var domView = this.views.dom; - domView.hide = function() { InspectorController.hideDOMNodeHighlight() }; - domView.show = function() { - InspectorController.highlightDOMNode(panel.focusedDOMNode); - panel.updateBreadcrumb(); - panel.updateTreeSelection(); - }; - - domView.sideContentElement = document.createElement("div"); - domView.sideContentElement.className = "content side"; - - domView.treeContentElement = document.createElement("div"); - domView.treeContentElement.className = "content tree outline-disclosure"; - - domView.treeListElement = document.createElement("ol"); - domView.treeOutline = new TreeOutline(domView.treeListElement); - domView.treeOutline.panel = this; - - domView.crumbsElement = document.createElement("div"); - domView.crumbsElement.className = "crumbs"; - - domView.innerCrumbsElement = document.createElement("div"); - domView.crumbsElement.appendChild(domView.innerCrumbsElement); - - domView.sidebarPanes = {}; - domView.sidebarPanes.styles = new WebInspector.StylesSidebarPane(); - domView.sidebarPanes.metrics = new WebInspector.MetricsSidebarPane(); - domView.sidebarPanes.properties = new WebInspector.PropertiesSidebarPane(); - - domView.sidebarPanes.styles.onexpand = function() { panel.updateStyles() }; - domView.sidebarPanes.metrics.onexpand = function() { panel.updateMetrics() }; - domView.sidebarPanes.properties.onexpand = function() { panel.updateProperties() }; - - domView.sidebarPanes.styles.expanded = true; - - domView.sidebarElement = document.createElement("div"); - domView.sidebarElement.className = "sidebar"; - - domView.sidebarElement.appendChild(domView.sidebarPanes.styles.element); - domView.sidebarElement.appendChild(domView.sidebarPanes.metrics.element); - domView.sidebarElement.appendChild(domView.sidebarPanes.properties.element); - - domView.sideContentElement.appendChild(domView.treeContentElement); - domView.sideContentElement.appendChild(domView.crumbsElement); - domView.treeContentElement.appendChild(domView.treeListElement); - - domView.sidebarResizeElement = document.createElement("div"); - domView.sidebarResizeElement.className = "sidebar-resizer-vertical sidebar-resizer-vertical-right"; - domView.sidebarResizeElement.addEventListener("mousedown", this.rightSidebarResizerDragStart.bind(this), false); - - domView.contentElement.appendChild(domView.sideContentElement); - domView.contentElement.appendChild(domView.sidebarElement); - domView.contentElement.appendChild(domView.sidebarResizeElement); - - this.rootDOMNode = this.resource.documentNode; -} - -WebInspector.DocumentPanel.prototype = { - resize: function() - { - this.updateTreeSelection(); - this.updateBreadcrumbSizes(); - }, - - updateTreeSelection: function() - { - if (!this.views.dom.treeOutline || !this.views.dom.treeOutline.selectedTreeElement) - return; - var element = this.views.dom.treeOutline.selectedTreeElement; - element.updateSelection(); - }, - - get rootDOMNode() - { - return this._rootDOMNode; - }, - - set rootDOMNode(x) - { - if (this._rootDOMNode === x) - return; - - this._rootDOMNode = x; - - this.updateBreadcrumb(); - this.updateTreeOutline(); - }, - - get focusedDOMNode() - { - return this._focusedDOMNode; - }, - - set focusedDOMNode(x) - { - if (this._focusedDOMNode === x) { - var nodeItem = this.revealNode(x); - if (nodeItem) - nodeItem.select(); - return; - } - - this._focusedDOMNode = x; - - this.updateBreadcrumb(); - - for (var pane in this.views.dom.sidebarPanes) - this.views.dom.sidebarPanes[pane].needsUpdate = true; - - this.updateStyles(); - this.updateMetrics(); - this.updateProperties(); - - InspectorController.highlightDOMNode(x); - - var nodeItem = this.revealNode(x); - if (nodeItem) - nodeItem.select(); - }, - - revealNode: function(node) - { - var nodeItem = this.views.dom.treeOutline.findTreeElement(node, function(a, b) { return isAncestorNode.call(a, b); }, function(a) { return a.parentNode; }); - if (!nodeItem) - return; - - nodeItem.reveal(); - return nodeItem; - }, - - updateTreeOutline: function() - { - this.views.dom.treeOutline.removeChildrenRecursive(); - - if (!this.rootDOMNode) - return; - - // FIXME: this could use findTreeElement to reuse a tree element if it already exists - var node = (Preferences.ignoreWhitespace ? firstChildSkippingWhitespace.call(this.rootDOMNode) : this.rootDOMNode.firstChild); - while (node) { - this.views.dom.treeOutline.appendChild(new WebInspector.DOMNodeTreeElement(node)); - node = Preferences.ignoreWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling; - } - - this.updateTreeSelection(); - }, - - updateBreadcrumb: function() - { - if (!this.visible) - return; - - var crumbs = this.views.dom.innerCrumbsElement; - - var handled = false; - var foundRoot = false; - var crumb = crumbs.firstChild; - while (crumb) { - if (crumb.representedObject === this.rootDOMNode) - foundRoot = true; - - if (foundRoot) - crumb.addStyleClass("dimmed"); - else - crumb.removeStyleClass("dimmed"); - - if (crumb.representedObject === this.focusedDOMNode) { - crumb.addStyleClass("selected"); - handled = true; - } else { - crumb.removeStyleClass("selected"); - } - - crumb = crumb.nextSibling; - } - - if (handled) { - // We don't need to rebuild the crumbs, but we need to adjust sizes - // to reflect the new focused or root node. - this.updateBreadcrumbSizes(); - return; - } - - crumbs.removeChildren(); - - var panel = this; - var selectCrumbFunction = function(event) { - var crumb = event.currentTarget; - if (crumb.hasStyleClass("collapsed")) { - // Clicking a collapsed crumb will expose the hidden crumbs. - if (crumb === panel.views.dom.innerCrumbsElement.firstChild) { - // If the focused crumb is the first child, pick the farthest crumb - // that is still hidden. This allows the user to expose every crumb. - var currentCrumb = crumb; - while (currentCrumb) { - var hidden = currentCrumb.hasStyleClass("hidden"); - var collapsed = currentCrumb.hasStyleClass("collapsed"); - if (!hidden && !collapsed) - break; - crumb = currentCrumb; - currentCrumb = currentCrumb.nextSibling; - } - } - - panel.updateBreadcrumbSizes(crumb); - } else { - // Clicking a dimmed crumb or double clicking (event.detail >= 2) - // will change the root node in addition to the focused node. - if (event.detail >= 2 || crumb.hasStyleClass("dimmed")) - panel.rootDOMNode = crumb.representedObject.parentNode; - panel.focusedDOMNode = crumb.representedObject; - } - - event.preventDefault(); - }; - - var mouseOverCrumbFunction = function(event) { - panel.mouseOverCrumb = true; - - if ("mouseOutTimeout" in panel) { - clearTimeout(panel.mouseOutTimeout); - delete panel.mouseOutTimeout; - } - }; - - var mouseOutCrumbFunction = function(event) { - delete panel.mouseOverCrumb; - - if ("mouseOutTimeout" in panel) { - clearTimeout(panel.mouseOutTimeout); - delete panel.mouseOutTimeout; - } - - var timeoutFunction = function() { - if (!panel.mouseOverCrumb) - panel.updateBreadcrumbSizes(); - }; - - panel.mouseOutTimeout = setTimeout(timeoutFunction, 500); - }; - - foundRoot = false; - var current = this.focusedDOMNode; - while (current) { - if (current.nodeType === Node.DOCUMENT_NODE) - break; - - if (current === this.rootDOMNode) - foundRoot = true; - - var crumb = document.createElement("span"); - crumb.className = "crumb"; - crumb.representedObject = current; - crumb.addEventListener("mousedown", selectCrumbFunction, false); - crumb.addEventListener("mouseover", mouseOverCrumbFunction, false); - crumb.addEventListener("mouseout", mouseOutCrumbFunction, false); - - var crumbTitle; - switch (current.nodeType) { - case Node.ELEMENT_NODE: - crumbTitle = current.nodeName.toLowerCase(); - - var nameElement = document.createElement("span"); - nameElement.textContent = crumbTitle; - crumb.appendChild(nameElement); - - var idAttribute = current.getAttribute("id"); - if (idAttribute) { - var idElement = document.createElement("span"); - crumb.appendChild(idElement); - - var part = "#" + idAttribute; - crumbTitle += part; - idElement.appendChild(document.createTextNode(part)); - - // Mark the name as extra, since the ID is more important. - nameElement.className = "extra"; - } - - var classAttribute = current.getAttribute("class"); - if (classAttribute) { - var classes = classAttribute.split(/\s+/); - var foundClasses = {}; - - if (classes.length) { - var classesElement = document.createElement("span"); - classesElement.className = "extra"; - crumb.appendChild(classesElement); - - for (var i = 0; i < classes.length; ++i) { - var className = classes[i]; - if (className && !(className in foundClasses)) { - var part = "." + className; - crumbTitle += part; - classesElement.appendChild(document.createTextNode(part)); - foundClasses[className] = true; - } - } - } - } - - break; - - case Node.TEXT_NODE: - if (isNodeWhitespace.call(current)) - crumbTitle = WebInspector.UIString("(whitespace)"); - else - crumbTitle = WebInspector.UIString("(text)"); - break - - case Node.COMMENT_NODE: - crumbTitle = "<!-->"; - break; - - default: - crumbTitle = current.nodeName.toLowerCase(); - } - - if (!crumb.childNodes.length) { - var nameElement = document.createElement("span"); - nameElement.textContent = crumbTitle; - crumb.appendChild(nameElement); - } - - crumb.title = crumbTitle; - - if (foundRoot) - crumb.addStyleClass("dimmed"); - if (current === this.focusedDOMNode) - crumb.addStyleClass("selected"); - if (!crumbs.childNodes.length) - crumb.addStyleClass("end"); - if (current.parentNode.nodeType === Node.DOCUMENT_NODE) - crumb.addStyleClass("start"); - - crumbs.appendChild(crumb); - current = current.parentNode; - } - - this.updateBreadcrumbSizes(); - }, - - updateBreadcrumbSizes: function(focusedCrumb) - { - if (!this.visible) - return; - - if (document.body.offsetWidth <= 0) { - // The stylesheet hasn't loaded yet, so we need to update later. - setTimeout(this.updateBreadcrumbSizes.bind(this), 0); - return; - } - - var crumbs = this.views.dom.innerCrumbsElement; - if (!crumbs.childNodes.length) - return; // No crumbs, do nothing. - - var crumbsContainer = this.views.dom.crumbsElement; - if (crumbsContainer.offsetWidth <= 0 || crumbs.offsetWidth <= 0) - return; - - // A Zero index is the right most child crumb in the breadcrumb. - var selectedIndex = 0; - var focusedIndex = 0; - var selectedCrumb; - - var i = 0; - var crumb = crumbs.firstChild; - while (crumb) { - // Find the selected crumb and index. - if (!selectedCrumb && crumb.hasStyleClass("selected")) { - selectedCrumb = crumb; - selectedIndex = i; - } - - // Find the focused crumb index. - if (crumb === focusedCrumb) - focusedIndex = i; - - // Remove any styles that affect size before - // deciding to shorten any crumbs. - if (crumb !== crumbs.lastChild) - crumb.removeStyleClass("start"); - if (crumb !== crumbs.firstChild) - crumb.removeStyleClass("end"); - - crumb.removeStyleClass("compact"); - crumb.removeStyleClass("collapsed"); - crumb.removeStyleClass("hidden"); - - crumb = crumb.nextSibling; - ++i; - } - - // Restore the start and end crumb classes in case they got removed in coalesceCollapsedCrumbs(). - // The order of the crumbs in the document is opposite of the visual order. - crumbs.firstChild.addStyleClass("end"); - crumbs.lastChild.addStyleClass("start"); - - function crumbsAreSmallerThanContainer() - { - // There is some fixed extra space that is not returned in the crumbs' offsetWidth. - // This padding is added to the crumbs' offsetWidth when comparing to the crumbsContainer. - var rightPadding = 9; - return ((crumbs.offsetWidth + rightPadding) < crumbsContainer.offsetWidth); - } - - if (crumbsAreSmallerThanContainer()) - return; // No need to compact the crumbs, they all fit at full size. - - var BothSides = 0; - var AncestorSide = -1; - var ChildSide = 1; - - function makeCrumbsSmaller(shrinkingFunction, direction, significantCrumb) - { - if (!significantCrumb) - significantCrumb = (focusedCrumb || selectedCrumb); - - if (significantCrumb === selectedCrumb) - var significantIndex = selectedIndex; - else if (significantCrumb === focusedCrumb) - var significantIndex = focusedIndex; - else { - var significantIndex = 0; - for (var i = 0; i < crumbs.childNodes.length; ++i) { - if (crumbs.childNodes[i] === significantCrumb) { - significantIndex = i; - break; - } - } - } - - function shrinkCrumbAtIndex(index) - { - var shrinkCrumb = crumbs.childNodes[index]; - if (shrinkCrumb && shrinkCrumb !== significantCrumb) - shrinkingFunction(shrinkCrumb); - if (crumbsAreSmallerThanContainer()) - return true; // No need to compact the crumbs more. - return false; - } - - // Shrink crumbs one at a time by applying the shrinkingFunction until the crumbs - // fit in the crumbsContainer or we run out of crumbs to shrink. - if (direction) { - // Crumbs are shrunk on only one side (based on direction) of the signifcant crumb. - var index = (direction > 0 ? 0 : crumbs.childNodes.length - 1); - while (index !== significantIndex) { - if (shrinkCrumbAtIndex(index)) - return true; - index += (direction > 0 ? 1 : -1); - } - } else { - // Crumbs are shrunk in order of descending distance from the signifcant crumb, - // with a tie going to child crumbs. - var startIndex = 0; - var endIndex = crumbs.childNodes.length - 1; - while (startIndex != significantIndex || endIndex != significantIndex) { - var startDistance = significantIndex - startIndex; - var endDistance = endIndex - significantIndex; - if (startDistance >= endDistance) - var index = startIndex++; - else - var index = endIndex--; - if (shrinkCrumbAtIndex(index)) - return true; - } - } - - // We are not small enough yet, return false so the caller knows. - return false; - } - - function coalesceCollapsedCrumbs() - { - var crumb = crumbs.firstChild; - var collapsedRun = false; - var newStartNeeded = false; - var newEndNeeded = false; - while (crumb) { - var hidden = crumb.hasStyleClass("hidden"); - if (!hidden) { - var collapsed = crumb.hasStyleClass("collapsed"); - if (collapsedRun && collapsed) { - crumb.addStyleClass("hidden"); - crumb.removeStyleClass("compact"); - crumb.removeStyleClass("collapsed"); - - if (crumb.hasStyleClass("start")) { - crumb.removeStyleClass("start"); - newStartNeeded = true; - } - - if (crumb.hasStyleClass("end")) { - crumb.removeStyleClass("end"); - newEndNeeded = true; - } - - continue; - } - - collapsedRun = collapsed; - - if (newEndNeeded) { - newEndNeeded = false; - crumb.addStyleClass("end"); - } - } else - collapsedRun = true; - crumb = crumb.nextSibling; - } - - if (newStartNeeded) { - crumb = crumbs.lastChild; - while (crumb) { - if (!crumb.hasStyleClass("hidden")) { - crumb.addStyleClass("start"); - break; - } - crumb = crumb.previousSibling; - } - } - } - - function compact(crumb) - { - if (crumb.hasStyleClass("hidden")) - return; - crumb.addStyleClass("compact"); - } - - function collapse(crumb, dontCoalesce) - { - if (crumb.hasStyleClass("hidden")) - return; - crumb.addStyleClass("collapsed"); - crumb.removeStyleClass("compact"); - if (!dontCoalesce) - coalesceCollapsedCrumbs(); - } - - function compactDimmed(crumb) - { - if (crumb.hasStyleClass("dimmed")) - compact(crumb); - } - - function collapseDimmed(crumb) - { - if (crumb.hasStyleClass("dimmed")) - collapse(crumb); - } - - if (!focusedCrumb) { - // When not focused on a crumb we can be biased and collapse less important - // crumbs that the user might not care much about. - - // Compact child crumbs. - if (makeCrumbsSmaller(compact, ChildSide)) - return; - - // Collapse child crumbs. - if (makeCrumbsSmaller(collapse, ChildSide)) - return; - - // Compact dimmed ancestor crumbs. - if (makeCrumbsSmaller(compactDimmed, AncestorSide)) - return; - - // Collapse dimmed ancestor crumbs. - if (makeCrumbsSmaller(collapseDimmed, AncestorSide)) - return; - } - - // Compact ancestor crumbs, or from both sides if focused. - if (makeCrumbsSmaller(compact, (focusedCrumb ? BothSides : AncestorSide))) - return; - - // Collapse ancestor crumbs, or from both sides if focused. - if (makeCrumbsSmaller(collapse, (focusedCrumb ? BothSides : AncestorSide))) - return; - - if (!selectedCrumb) - return; - - // Compact the selected crumb. - compact(selectedCrumb); - if (crumbsAreSmallerThanContainer()) - return; - - // Collapse the selected crumb as a last resort. Pass true to prevent coalescing. - collapse(selectedCrumb, true); - }, - - updateStyles: function() - { - var stylesSidebarPane = this.views.dom.sidebarPanes.styles; - if (!stylesSidebarPane.expanded || !stylesSidebarPane.needsUpdate) - return; - - stylesSidebarPane.update(this.focusedDOMNode); - stylesSidebarPane.needsUpdate = false; - }, - - updateMetrics: function() - { - var metricsSidebarPane = this.views.dom.sidebarPanes.metrics; - if (!metricsSidebarPane.expanded || !metricsSidebarPane.needsUpdate) - return; - - metricsSidebarPane.update(this.focusedDOMNode); - metricsSidebarPane.needsUpdate = false; - }, - - updateProperties: function() - { - var propertiesSidebarPane = this.views.dom.sidebarPanes.properties; - if (!propertiesSidebarPane.expanded || !propertiesSidebarPane.needsUpdate) - return; - - propertiesSidebarPane.update(this.focusedDOMNode); - propertiesSidebarPane.needsUpdate = false; - }, - - handleKeyEvent: function(event) - { - if (this.views.dom.treeOutline && this.currentView && this.currentView === this.views.dom) - this.views.dom.treeOutline.handleKeyEvent(event); - }, - - handleCopyEvent: function(event) - { - if (this.currentView !== this.views.dom) - return; - - // Don't prevent the normal copy if the user has a selection. - if (!window.getSelection().isCollapsed) - return; - - switch (this.focusedDOMNode.nodeType) { - case Node.ELEMENT_NODE: - var data = this.focusedDOMNode.outerHTML; - break; - - case Node.COMMENT_NODE: - var data = "<!--" + this.focusedDOMNode.nodeValue + "-->"; - break; - - default: - case Node.TEXT_NODE: - var data = this.focusedDOMNode.nodeValue; - } - - event.clipboardData.clearData(); - event.preventDefault(); - - if (data) - event.clipboardData.setData("text/plain", data); - }, - - rightSidebarResizerDragStart: function(event) - { - if (this.sidebarDragEventListener || this.sidebarDragEndEventListener) - return this.rightSidebarResizerDragEnd(event); - - this.sidebarDragEventListener = this.rightSidebarResizerDrag.bind(this); - this.sidebarDragEndEventListener = this.rightSidebarResizerDragEnd.bind(this); - WebInspector.elementDragStart(this.views.dom.sidebarElement, this.sidebarDragEventListener, this.sidebarDragEndEventListener, event, "col-resize"); - }, - - rightSidebarResizerDragEnd: function(event) - { - WebInspector.elementDragEnd(this.views.dom.sidebarElement, this.sidebarDragEventListener, this.sidebarDragEndEventListener, event); - delete this.sidebarDragEventListener; - delete this.sidebarDragEndEventListener; - }, - - rightSidebarResizerDrag: function(event) - { - var x = event.pageX; - - var leftSidebarWidth = document.getElementById("sidebar").offsetWidth; - var newWidth = Number.constrain(window.innerWidth - x, 100, window.innerWidth - leftSidebarWidth - 100); - - this.views.dom.sidebarElement.style.width = newWidth + "px"; - this.views.dom.sideContentElement.style.right = newWidth + "px"; - this.views.dom.sidebarResizeElement.style.right = (newWidth - 3) + "px"; - - this.updateTreeSelection(); - this.updateBreadcrumbSizes(); - - event.preventDefault(); - } -} - -WebInspector.DocumentPanel.prototype.__proto__ = WebInspector.SourcePanel.prototype; - -WebInspector.DOMNodeTreeElement = function(node) -{ - var hasChildren = (Preferences.ignoreWhitespace ? (firstChildSkippingWhitespace.call(node) ? true : false) : node.hasChildNodes()); - var titleInfo = nodeTitleInfo.call(node, hasChildren, WebInspector.linkifyURL); - - if (titleInfo.hasChildren) - this.whitespaceIgnored = Preferences.ignoreWhitespace; - - TreeElement.call(this, titleInfo.title, node, titleInfo.hasChildren); -} - -WebInspector.DOMNodeTreeElement.prototype = { - updateSelection: function() - { - var listItemElement = this.listItemElement; - if (!listItemElement) - return; - - if (document.body.offsetWidth <= 0) { - // The stylesheet hasn't loaded yet, so we need to update later. - setTimeout(this.updateSelection.bind(this), 0); - return; - } - - if (!this.selectionElement) { - this.selectionElement = document.createElement("div"); - this.selectionElement.className = "selection selected"; - listItemElement.insertBefore(this.selectionElement, listItemElement.firstChild); - } - - this.selectionElement.style.height = listItemElement.offsetHeight + "px"; - }, - - onattach: function() - { - this.listItemElement.addEventListener("mousedown", this.onmousedown.bind(this), false); - }, - - onpopulate: function() - { - if (this.children.length || this.whitespaceIgnored !== Preferences.ignoreWhitespace) - return; - - this.removeChildren(); - this.whitespaceIgnored = Preferences.ignoreWhitespace; - - var node = (Preferences.ignoreWhitespace ? firstChildSkippingWhitespace.call(this.representedObject) : this.representedObject.firstChild); - while (node) { - this.appendChild(new WebInspector.DOMNodeTreeElement(node)); - node = Preferences.ignoreWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling; - } - - if (this.representedObject.nodeType == Node.ELEMENT_NODE) { - var title = "<span class=\"webkit-html-tag close\"></" + this.representedObject.nodeName.toLowerCase().escapeHTML() + "></span>"; - var item = new TreeElement(title, this.representedObject, false); - item.selectable = false; - this.appendChild(item); - } - }, - - onexpand: function() - { - this.treeOutline.panel.updateTreeSelection(); - }, - - oncollapse: function() - { - this.treeOutline.panel.updateTreeSelection(); - }, - - onreveal: function() - { - if (!this.listItemElement || !this.treeOutline) - return; - this.treeOutline.panel.views.dom.treeContentElement.scrollToElement(this.listItemElement); - }, - - onselect: function() - { - this.treeOutline.panel.focusedDOMNode = this.representedObject; - this.updateSelection(); - }, - - onmousedown: function(event) - { - // Prevent selecting the nearest word on double click. - if (event.detail >= 2) - event.preventDefault(); - }, - - ondblclick: function() - { - var panel = this.treeOutline.panel; - panel.rootDOMNode = this.representedObject.parentNode; - panel.focusedDOMNode = this.representedObject; - - if (this.hasChildren && !this.expanded) - this.expand(); - } -} - -WebInspector.DOMNodeTreeElement.prototype.__proto__ = TreeElement.prototype; diff --git a/WebCore/page/inspector/FontPanel.js b/WebCore/page/inspector/FontPanel.js deleted file mode 100644 index c1ffd2f..0000000 --- a/WebCore/page/inspector/FontPanel.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.FontPanel = function(resource) -{ - WebInspector.ResourcePanel.call(this, resource); - - this.element.addStyleClass("font"); - - var uniqueFontName = "WebInspectorFontPreview" + this.resource.identifier; - - this.fontPreviewElement = document.createElement("div"); - this.fontPreviewElement.className = "preview"; - this.element.appendChild(this.fontPreviewElement); - - this.fontPreviewElement.style.setProperty("font-family", uniqueFontName, null); - this.fontPreviewElement.innerHTML = "ABCDEFGHIJKLM<br>NOPQRSTUVWXYZ<br>abcdefghijklm<br>nopqrstuvwxyz<br>1234567890"; - - this.updateFontPreviewSize(); -} - -WebInspector.FontPanel.prototype = { - show: function() - { - WebInspector.ResourcePanel.prototype.show.call(this); - this.updateFontPreviewSize(); - }, - - resize: function() - { - this.updateFontPreviewSize(); - }, - - updateFontPreviewSize: function () - { - if (!this.fontPreviewElement || !this.visible) - return; - - this.fontPreviewElement.removeStyleClass("preview"); - - var measureFontSize = 50; - this.fontPreviewElement.style.setProperty("position", "absolute", null); - this.fontPreviewElement.style.setProperty("font-size", measureFontSize + "px", null); - this.fontPreviewElement.style.removeProperty("height"); - - var height = this.fontPreviewElement.offsetHeight; - var width = this.fontPreviewElement.offsetWidth; - - var containerHeight = this.element.offsetHeight; - var containerWidth = this.element.offsetWidth; - - if (!height || !width || !containerHeight || !containerWidth) { - this.fontPreviewElement.style.removeProperty("font-size"); - this.fontPreviewElement.style.removeProperty("position"); - this.fontPreviewElement.addStyleClass("preview"); - return; - } - - var lineCount = this.fontPreviewElement.getElementsByTagName("br").length + 1; - var realLineHeight = Math.floor(height / lineCount); - var fontSizeLineRatio = measureFontSize / realLineHeight; - var widthRatio = containerWidth / width; - var heightRatio = containerHeight / height; - - if (heightRatio < widthRatio) - var finalFontSize = Math.floor(realLineHeight * heightRatio * fontSizeLineRatio) - 1; - else - var finalFontSize = Math.floor(realLineHeight * widthRatio * fontSizeLineRatio) - 1; - - this.fontPreviewElement.style.setProperty("font-size", finalFontSize + "px", null); - this.fontPreviewElement.style.setProperty("height", this.fontPreviewElement.offsetHeight + "px", null); - this.fontPreviewElement.style.removeProperty("position"); - - this.fontPreviewElement.addStyleClass("preview"); - } -} - -WebInspector.FontPanel.prototype.__proto__ = WebInspector.ResourcePanel.prototype; diff --git a/WebCore/page/inspector/ImagePanel.js b/WebCore/page/inspector/ImagePanel.js deleted file mode 100644 index 402b754..0000000 --- a/WebCore/page/inspector/ImagePanel.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.ImagePanel = function(resource) -{ - WebInspector.ResourcePanel.call(this, resource); - - this.element.addStyleClass("image"); - - var container = document.createElement("div"); - container.className = "image"; - this.element.appendChild(container); - - this.imagePreviewElement = document.createElement("img"); - this.imagePreviewElement.setAttribute("src", this.resource.url); - - container.appendChild(this.imagePreviewElement); - - container = document.createElement("div"); - container.className = "info"; - this.element.appendChild(container); - - var imageNameElement = document.createElement("h1"); - imageNameElement.className = "title"; - imageNameElement.textContent = this.resource.displayName; - container.appendChild(imageNameElement); - - var infoListElement = document.createElement("dl"); - infoListElement.className = "infoList"; - - var imageProperties = [ - { name: WebInspector.UIString("Dimensions"), value: WebInspector.UIString("%d × %d", this.imagePreviewElement.naturalWidth, this.imagePreviewElement.height) }, - { name: WebInspector.UIString("File size"), value: WebInspector.UIString("%.2fKB", (this.resource.contentLength / 1024)) }, - { name: WebInspector.UIString("MIME type"), value: this.resource.mimeType } - ]; - - var listHTML = ''; - for (var i = 0; i < imageProperties.length; ++i) - listHTML += "<dt>" + imageProperties[i].name + "</dt><dd>" + imageProperties[i].value + "</dd>"; - - infoListElement.innerHTML = listHTML; - container.appendChild(infoListElement); -} - -WebInspector.ImagePanel.prototype = { - -} - -WebInspector.ImagePanel.prototype.__proto__ = WebInspector.ResourcePanel.prototype; diff --git a/WebCore/page/inspector/Images/alternateTableRows.png b/WebCore/page/inspector/Images/alternateTableRows.png Binary files differdeleted file mode 100644 index 7706f50..0000000 --- a/WebCore/page/inspector/Images/alternateTableRows.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/attachedShadow.png b/WebCore/page/inspector/Images/attachedShadow.png Binary files differdeleted file mode 100644 index b0b687c..0000000 --- a/WebCore/page/inspector/Images/attachedShadow.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/backNormal.png b/WebCore/page/inspector/Images/backNormal.png Binary files differdeleted file mode 100644 index cff21a5..0000000 --- a/WebCore/page/inspector/Images/backNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/bottomShadow.png b/WebCore/page/inspector/Images/bottomShadow.png Binary files differdeleted file mode 100644 index 61699c4..0000000 --- a/WebCore/page/inspector/Images/bottomShadow.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/breadcrumbBackground.png b/WebCore/page/inspector/Images/breadcrumbBackground.png Binary files differdeleted file mode 100644 index 516452b..0000000 --- a/WebCore/page/inspector/Images/breadcrumbBackground.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/checker.png b/WebCore/page/inspector/Images/checker.png Binary files differdeleted file mode 100644 index 8349908..0000000 --- a/WebCore/page/inspector/Images/checker.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/console.png b/WebCore/page/inspector/Images/console.png Binary files differdeleted file mode 100644 index c85fed0..0000000 --- a/WebCore/page/inspector/Images/console.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/darkShadow.png b/WebCore/page/inspector/Images/darkShadow.png Binary files differdeleted file mode 100644 index 2761b7d..0000000 --- a/WebCore/page/inspector/Images/darkShadow.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/database.png b/WebCore/page/inspector/Images/database.png Binary files differdeleted file mode 100644 index 2c13789..0000000 --- a/WebCore/page/inspector/Images/database.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseBrowserViewNormal.png b/WebCore/page/inspector/Images/databaseBrowserViewNormal.png Binary files differdeleted file mode 100644 index 0d55522..0000000 --- a/WebCore/page/inspector/Images/databaseBrowserViewNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseBrowserViewNormalSelected.png b/WebCore/page/inspector/Images/databaseBrowserViewNormalSelected.png Binary files differdeleted file mode 100644 index af32982..0000000 --- a/WebCore/page/inspector/Images/databaseBrowserViewNormalSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseBrowserViewSmall.png b/WebCore/page/inspector/Images/databaseBrowserViewSmall.png Binary files differdeleted file mode 100644 index fa33eec..0000000 --- a/WebCore/page/inspector/Images/databaseBrowserViewSmall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseBrowserViewSmallSelected.png b/WebCore/page/inspector/Images/databaseBrowserViewSmallSelected.png Binary files differdeleted file mode 100644 index 6880954..0000000 --- a/WebCore/page/inspector/Images/databaseBrowserViewSmallSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseQueryViewNormal.png b/WebCore/page/inspector/Images/databaseQueryViewNormal.png Binary files differdeleted file mode 100644 index 326e8c3..0000000 --- a/WebCore/page/inspector/Images/databaseQueryViewNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseQueryViewNormalSelected.png b/WebCore/page/inspector/Images/databaseQueryViewNormalSelected.png Binary files differdeleted file mode 100644 index 8ffda5c..0000000 --- a/WebCore/page/inspector/Images/databaseQueryViewNormalSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseQueryViewSmall.png b/WebCore/page/inspector/Images/databaseQueryViewSmall.png Binary files differdeleted file mode 100644 index d11a127..0000000 --- a/WebCore/page/inspector/Images/databaseQueryViewSmall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/databaseQueryViewSmallSelected.png b/WebCore/page/inspector/Images/databaseQueryViewSmallSelected.png Binary files differdeleted file mode 100644 index 35be952..0000000 --- a/WebCore/page/inspector/Images/databaseQueryViewSmallSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/disclosureDownPressed.png b/WebCore/page/inspector/Images/disclosureDownPressed.png Binary files differdeleted file mode 100644 index 32ac517..0000000 --- a/WebCore/page/inspector/Images/disclosureDownPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/disclosureRightDown.png b/WebCore/page/inspector/Images/disclosureRightDown.png Binary files differdeleted file mode 100644 index 104ea86..0000000 --- a/WebCore/page/inspector/Images/disclosureRightDown.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/disclosureRightPressed.png b/WebCore/page/inspector/Images/disclosureRightPressed.png Binary files differdeleted file mode 100644 index 41c9b20..0000000 --- a/WebCore/page/inspector/Images/disclosureRightPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/document.png b/WebCore/page/inspector/Images/document.png Binary files differdeleted file mode 100644 index fa9c3f5..0000000 --- a/WebCore/page/inspector/Images/document.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/domViewNormal.png b/WebCore/page/inspector/Images/domViewNormal.png Binary files differdeleted file mode 100644 index 6cd4ac4..0000000 --- a/WebCore/page/inspector/Images/domViewNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/domViewNormalSelected.png b/WebCore/page/inspector/Images/domViewNormalSelected.png Binary files differdeleted file mode 100644 index 5831819..0000000 --- a/WebCore/page/inspector/Images/domViewNormalSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/domViewSmall.png b/WebCore/page/inspector/Images/domViewSmall.png Binary files differdeleted file mode 100644 index e9fb0da..0000000 --- a/WebCore/page/inspector/Images/domViewSmall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/domViewSmallSelected.png b/WebCore/page/inspector/Images/domViewSmallSelected.png Binary files differdeleted file mode 100644 index 517479d..0000000 --- a/WebCore/page/inspector/Images/domViewSmallSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/downTriangle.png b/WebCore/page/inspector/Images/downTriangle.png Binary files differdeleted file mode 100644 index 18a2a89..0000000 --- a/WebCore/page/inspector/Images/downTriangle.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/errorIcon.png b/WebCore/page/inspector/Images/errorIcon.png Binary files differdeleted file mode 100644 index d6ec461..0000000 --- a/WebCore/page/inspector/Images/errorIcon.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/errorMediumIcon.png b/WebCore/page/inspector/Images/errorMediumIcon.png Binary files differdeleted file mode 100644 index 6ca32bb..0000000 --- a/WebCore/page/inspector/Images/errorMediumIcon.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/folder.png b/WebCore/page/inspector/Images/folder.png Binary files differdeleted file mode 100644 index 9bd7d9e..0000000 --- a/WebCore/page/inspector/Images/folder.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/forwardNormal.png b/WebCore/page/inspector/Images/forwardNormal.png Binary files differdeleted file mode 100644 index 5cb2c1b..0000000 --- a/WebCore/page/inspector/Images/forwardNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/glossyHeader.png b/WebCore/page/inspector/Images/glossyHeader.png Binary files differdeleted file mode 100644 index 8c80b6b..0000000 --- a/WebCore/page/inspector/Images/glossyHeader.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/glossyHeaderPressed.png b/WebCore/page/inspector/Images/glossyHeaderPressed.png Binary files differdeleted file mode 100644 index 6b0dd60..0000000 --- a/WebCore/page/inspector/Images/glossyHeaderPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/goArrow.png b/WebCore/page/inspector/Images/goArrow.png Binary files differdeleted file mode 100644 index f318a56..0000000 --- a/WebCore/page/inspector/Images/goArrow.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/gradient.png b/WebCore/page/inspector/Images/gradient.png Binary files differdeleted file mode 100644 index aa8568b..0000000 --- a/WebCore/page/inspector/Images/gradient.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/gradientHighlight.png b/WebCore/page/inspector/Images/gradientHighlight.png Binary files differdeleted file mode 100644 index 93bc9c1..0000000 --- a/WebCore/page/inspector/Images/gradientHighlight.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/gradientHighlightBottom.png b/WebCore/page/inspector/Images/gradientHighlightBottom.png Binary files differdeleted file mode 100644 index 89b0b67..0000000 --- a/WebCore/page/inspector/Images/gradientHighlightBottom.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/hideStatusWidget.png b/WebCore/page/inspector/Images/hideStatusWidget.png Binary files differdeleted file mode 100644 index 52ecece..0000000 --- a/WebCore/page/inspector/Images/hideStatusWidget.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/hideStatusWidgetPressed.png b/WebCore/page/inspector/Images/hideStatusWidgetPressed.png Binary files differdeleted file mode 100644 index f041662..0000000 --- a/WebCore/page/inspector/Images/hideStatusWidgetPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/network.png b/WebCore/page/inspector/Images/network.png Binary files differdeleted file mode 100644 index bc215bc..0000000 --- a/WebCore/page/inspector/Images/network.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/paneBottomGrow.png b/WebCore/page/inspector/Images/paneBottomGrow.png Binary files differdeleted file mode 100644 index d55b865..0000000 --- a/WebCore/page/inspector/Images/paneBottomGrow.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/paneBottomGrowActive.png b/WebCore/page/inspector/Images/paneBottomGrowActive.png Binary files differdeleted file mode 100644 index ef3f259..0000000 --- a/WebCore/page/inspector/Images/paneBottomGrowActive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/paneGrowHandleLine.png b/WebCore/page/inspector/Images/paneGrowHandleLine.png Binary files differdeleted file mode 100644 index 4eaf61b..0000000 --- a/WebCore/page/inspector/Images/paneGrowHandleLine.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/paneHeader.png b/WebCore/page/inspector/Images/paneHeader.png Binary files differdeleted file mode 100644 index cc66afa..0000000 --- a/WebCore/page/inspector/Images/paneHeader.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/paneHeaderActive.png b/WebCore/page/inspector/Images/paneHeaderActive.png Binary files differdeleted file mode 100644 index 08205fb..0000000 --- a/WebCore/page/inspector/Images/paneHeaderActive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/plainDocument.png b/WebCore/page/inspector/Images/plainDocument.png Binary files differdeleted file mode 100644 index 2e92b71..0000000 --- a/WebCore/page/inspector/Images/plainDocument.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/popupArrows.png b/WebCore/page/inspector/Images/popupArrows.png Binary files differdeleted file mode 100644 index b980e46..0000000 --- a/WebCore/page/inspector/Images/popupArrows.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/popupArrowsBlack.png b/WebCore/page/inspector/Images/popupArrowsBlack.png Binary files differdeleted file mode 100644 index 9c9b061..0000000 --- a/WebCore/page/inspector/Images/popupArrowsBlack.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/reload.png b/WebCore/page/inspector/Images/reload.png Binary files differdeleted file mode 100644 index 9797d26..0000000 --- a/WebCore/page/inspector/Images/reload.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/rightTriangle.png b/WebCore/page/inspector/Images/rightTriangle.png Binary files differdeleted file mode 100644 index 9f519f2..0000000 --- a/WebCore/page/inspector/Images/rightTriangle.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/segment.png b/WebCore/page/inspector/Images/segment.png Binary files differdeleted file mode 100644 index bcef26a..0000000 --- a/WebCore/page/inspector/Images/segment.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/segmentEnd.png b/WebCore/page/inspector/Images/segmentEnd.png Binary files differdeleted file mode 100644 index 374a53d..0000000 --- a/WebCore/page/inspector/Images/segmentEnd.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/segmentHover.png b/WebCore/page/inspector/Images/segmentHover.png Binary files differdeleted file mode 100644 index f54c029..0000000 --- a/WebCore/page/inspector/Images/segmentHover.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/segmentHoverEnd.png b/WebCore/page/inspector/Images/segmentHoverEnd.png Binary files differdeleted file mode 100644 index e377c2d..0000000 --- a/WebCore/page/inspector/Images/segmentHoverEnd.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/segmentSelected.png b/WebCore/page/inspector/Images/segmentSelected.png Binary files differdeleted file mode 100644 index a7f3a8f..0000000 --- a/WebCore/page/inspector/Images/segmentSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/segmentSelectedEnd.png b/WebCore/page/inspector/Images/segmentSelectedEnd.png Binary files differdeleted file mode 100644 index be2c61b..0000000 --- a/WebCore/page/inspector/Images/segmentSelectedEnd.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/showStatusWidget.png b/WebCore/page/inspector/Images/showStatusWidget.png Binary files differdeleted file mode 100644 index 051d883..0000000 --- a/WebCore/page/inspector/Images/showStatusWidget.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/showStatusWidgetPressed.png b/WebCore/page/inspector/Images/showStatusWidgetPressed.png Binary files differdeleted file mode 100644 index 5622c9c..0000000 --- a/WebCore/page/inspector/Images/showStatusWidgetPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidbarItemBackground.png b/WebCore/page/inspector/Images/sidbarItemBackground.png Binary files differdeleted file mode 100644 index 5b94167..0000000 --- a/WebCore/page/inspector/Images/sidbarItemBackground.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarActionWidget.png b/WebCore/page/inspector/Images/sidebarActionWidget.png Binary files differdeleted file mode 100644 index daa1aff..0000000 --- a/WebCore/page/inspector/Images/sidebarActionWidget.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarActionWidgetPressed.png b/WebCore/page/inspector/Images/sidebarActionWidgetPressed.png Binary files differdeleted file mode 100644 index 794b187..0000000 --- a/WebCore/page/inspector/Images/sidebarActionWidgetPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarAttachWidget.png b/WebCore/page/inspector/Images/sidebarAttachWidget.png Binary files differdeleted file mode 100644 index 5f13f36..0000000 --- a/WebCore/page/inspector/Images/sidebarAttachWidget.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarAttachWidgetPressed.png b/WebCore/page/inspector/Images/sidebarAttachWidgetPressed.png Binary files differdeleted file mode 100644 index f70dd32..0000000 --- a/WebCore/page/inspector/Images/sidebarAttachWidgetPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarDetachWidget.png b/WebCore/page/inspector/Images/sidebarDetachWidget.png Binary files differdeleted file mode 100644 index 2443c21..0000000 --- a/WebCore/page/inspector/Images/sidebarDetachWidget.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarDetachWidgetPressed.png b/WebCore/page/inspector/Images/sidebarDetachWidgetPressed.png Binary files differdeleted file mode 100644 index e13f4c1..0000000 --- a/WebCore/page/inspector/Images/sidebarDetachWidgetPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarResizeWidget.png b/WebCore/page/inspector/Images/sidebarResizeWidget.png Binary files differdeleted file mode 100644 index 7c1ff70..0000000 --- a/WebCore/page/inspector/Images/sidebarResizeWidget.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarSelection.png b/WebCore/page/inspector/Images/sidebarSelection.png Binary files differdeleted file mode 100644 index fccfa0f..0000000 --- a/WebCore/page/inspector/Images/sidebarSelection.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarSelectionBlurred.png b/WebCore/page/inspector/Images/sidebarSelectionBlurred.png Binary files differdeleted file mode 100644 index 1e49cdf..0000000 --- a/WebCore/page/inspector/Images/sidebarSelectionBlurred.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarSelectionBlurredTall.png b/WebCore/page/inspector/Images/sidebarSelectionBlurredTall.png Binary files differdeleted file mode 100644 index 33a7cfc..0000000 --- a/WebCore/page/inspector/Images/sidebarSelectionBlurredTall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarSelectionGray.png b/WebCore/page/inspector/Images/sidebarSelectionGray.png Binary files differdeleted file mode 100644 index f4faf2c..0000000 --- a/WebCore/page/inspector/Images/sidebarSelectionGray.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarSelectionGrayTall.png b/WebCore/page/inspector/Images/sidebarSelectionGrayTall.png Binary files differdeleted file mode 100644 index 01379d2..0000000 --- a/WebCore/page/inspector/Images/sidebarSelectionGrayTall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarSelectionTall.png b/WebCore/page/inspector/Images/sidebarSelectionTall.png Binary files differdeleted file mode 100644 index 0bb56da..0000000 --- a/WebCore/page/inspector/Images/sidebarSelectionTall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sidebarStatusAreaBackground.png b/WebCore/page/inspector/Images/sidebarStatusAreaBackground.png Binary files differdeleted file mode 100644 index 0127d9d..0000000 --- a/WebCore/page/inspector/Images/sidebarStatusAreaBackground.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sourceViewNormal.png b/WebCore/page/inspector/Images/sourceViewNormal.png Binary files differdeleted file mode 100644 index 2b23460..0000000 --- a/WebCore/page/inspector/Images/sourceViewNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sourceViewNormalSelected.png b/WebCore/page/inspector/Images/sourceViewNormalSelected.png Binary files differdeleted file mode 100644 index 0c24b1c..0000000 --- a/WebCore/page/inspector/Images/sourceViewNormalSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sourceViewSmall.png b/WebCore/page/inspector/Images/sourceViewSmall.png Binary files differdeleted file mode 100644 index 82d06ef..0000000 --- a/WebCore/page/inspector/Images/sourceViewSmall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/sourceViewSmallSelected.png b/WebCore/page/inspector/Images/sourceViewSmallSelected.png Binary files differdeleted file mode 100644 index fe0351e..0000000 --- a/WebCore/page/inspector/Images/sourceViewSmallSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/splitviewDimple.png b/WebCore/page/inspector/Images/splitviewDimple.png Binary files differdeleted file mode 100644 index 584ffd4..0000000 --- a/WebCore/page/inspector/Images/splitviewDimple.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/splitviewDividerBackground.png b/WebCore/page/inspector/Images/splitviewDividerBackground.png Binary files differdeleted file mode 100644 index 1120a7f..0000000 --- a/WebCore/page/inspector/Images/splitviewDividerBackground.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/tab.png b/WebCore/page/inspector/Images/tab.png Binary files differdeleted file mode 100644 index 2f5000a..0000000 --- a/WebCore/page/inspector/Images/tab.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/tabSelected.png b/WebCore/page/inspector/Images/tabSelected.png Binary files differdeleted file mode 100644 index 76c2f66..0000000 --- a/WebCore/page/inspector/Images/tabSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/timelinePillBlue.png b/WebCore/page/inspector/Images/timelinePillBlue.png Binary files differdeleted file mode 100644 index c897faa..0000000 --- a/WebCore/page/inspector/Images/timelinePillBlue.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/timelinePillGray.png b/WebCore/page/inspector/Images/timelinePillGray.png Binary files differdeleted file mode 100644 index 2128896..0000000 --- a/WebCore/page/inspector/Images/timelinePillGray.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/timelinePillGreen.png b/WebCore/page/inspector/Images/timelinePillGreen.png Binary files differdeleted file mode 100644 index 9b66125..0000000 --- a/WebCore/page/inspector/Images/timelinePillGreen.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/timelinePillOrange.png b/WebCore/page/inspector/Images/timelinePillOrange.png Binary files differdeleted file mode 100644 index dd944fb..0000000 --- a/WebCore/page/inspector/Images/timelinePillOrange.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/timelinePillPurple.png b/WebCore/page/inspector/Images/timelinePillPurple.png Binary files differdeleted file mode 100644 index 21b96f7..0000000 --- a/WebCore/page/inspector/Images/timelinePillPurple.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/timelinePillRed.png b/WebCore/page/inspector/Images/timelinePillRed.png Binary files differdeleted file mode 100644 index f5e213b..0000000 --- a/WebCore/page/inspector/Images/timelinePillRed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/timelinePillYellow.png b/WebCore/page/inspector/Images/timelinePillYellow.png Binary files differdeleted file mode 100644 index ae2a5a2..0000000 --- a/WebCore/page/inspector/Images/timelinePillYellow.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/tipBalloon.png b/WebCore/page/inspector/Images/tipBalloon.png Binary files differdeleted file mode 100644 index 4cdf738..0000000 --- a/WebCore/page/inspector/Images/tipBalloon.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/tipBalloonBottom.png b/WebCore/page/inspector/Images/tipBalloonBottom.png Binary files differdeleted file mode 100644 index 3317a5a..0000000 --- a/WebCore/page/inspector/Images/tipBalloonBottom.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/tipIcon.png b/WebCore/page/inspector/Images/tipIcon.png Binary files differdeleted file mode 100644 index 8ca6124..0000000 --- a/WebCore/page/inspector/Images/tipIcon.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/tipIconPressed.png b/WebCore/page/inspector/Images/tipIconPressed.png Binary files differdeleted file mode 100644 index 443e410..0000000 --- a/WebCore/page/inspector/Images/tipIconPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toggleDown.png b/WebCore/page/inspector/Images/toggleDown.png Binary files differdeleted file mode 100644 index 9e73bfb..0000000 --- a/WebCore/page/inspector/Images/toggleDown.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toggleUp.png b/WebCore/page/inspector/Images/toggleUp.png Binary files differdeleted file mode 100644 index 6b62aee..0000000 --- a/WebCore/page/inspector/Images/toggleUp.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarBackground.png b/WebCore/page/inspector/Images/toolbarBackground.png Binary files differdeleted file mode 100644 index 257d63d..0000000 --- a/WebCore/page/inspector/Images/toolbarBackground.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarBackgroundInactive.png b/WebCore/page/inspector/Images/toolbarBackgroundInactive.png Binary files differdeleted file mode 100644 index a62b8df..0000000 --- a/WebCore/page/inspector/Images/toolbarBackgroundInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonNormal.png b/WebCore/page/inspector/Images/toolbarButtonNormal.png Binary files differdeleted file mode 100644 index bd16c12..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalInactive.png b/WebCore/page/inspector/Images/toolbarButtonNormalInactive.png Binary files differdeleted file mode 100644 index 3c9d5bc..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonNormalInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalPressed.png b/WebCore/page/inspector/Images/toolbarButtonNormalPressed.png Binary files differdeleted file mode 100644 index aca9b02..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonNormalPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalSelected.png b/WebCore/page/inspector/Images/toolbarButtonNormalSelected.png Binary files differdeleted file mode 100644 index 41013b3..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonNormalSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalSelectedInactive.png b/WebCore/page/inspector/Images/toolbarButtonNormalSelectedInactive.png Binary files differdeleted file mode 100644 index f6fdb08..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonNormalSelectedInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonSmall.png b/WebCore/page/inspector/Images/toolbarButtonSmall.png Binary files differdeleted file mode 100644 index 04398fd..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonSmall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallInactive.png b/WebCore/page/inspector/Images/toolbarButtonSmallInactive.png Binary files differdeleted file mode 100644 index a6928be..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonSmallInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallPressed.png b/WebCore/page/inspector/Images/toolbarButtonSmallPressed.png Binary files differdeleted file mode 100644 index 0d83a38..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonSmallPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallSelected.png b/WebCore/page/inspector/Images/toolbarButtonSmallSelected.png Binary files differdeleted file mode 100644 index d5433cb..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonSmallSelected.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallSelectedInactive.png b/WebCore/page/inspector/Images/toolbarButtonSmallSelectedInactive.png Binary files differdeleted file mode 100644 index 41a3249..0000000 --- a/WebCore/page/inspector/Images/toolbarButtonSmallSelectedInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonNormal.png b/WebCore/page/inspector/Images/toolbarPopupButtonNormal.png Binary files differdeleted file mode 100644 index 438b5ea..0000000 --- a/WebCore/page/inspector/Images/toolbarPopupButtonNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonNormalInactive.png b/WebCore/page/inspector/Images/toolbarPopupButtonNormalInactive.png Binary files differdeleted file mode 100644 index 0e8f6a2..0000000 --- a/WebCore/page/inspector/Images/toolbarPopupButtonNormalInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonNormalPressed.png b/WebCore/page/inspector/Images/toolbarPopupButtonNormalPressed.png Binary files differdeleted file mode 100644 index 23bcafd..0000000 --- a/WebCore/page/inspector/Images/toolbarPopupButtonNormalPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonSmall.png b/WebCore/page/inspector/Images/toolbarPopupButtonSmall.png Binary files differdeleted file mode 100644 index efa68a3..0000000 --- a/WebCore/page/inspector/Images/toolbarPopupButtonSmall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonSmallInactive.png b/WebCore/page/inspector/Images/toolbarPopupButtonSmallInactive.png Binary files differdeleted file mode 100644 index 55e3c0c..0000000 --- a/WebCore/page/inspector/Images/toolbarPopupButtonSmallInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonSmallPressed.png b/WebCore/page/inspector/Images/toolbarPopupButtonSmallPressed.png Binary files differdeleted file mode 100644 index 56e574c..0000000 --- a/WebCore/page/inspector/Images/toolbarPopupButtonSmallPressed.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormal.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormal.png Binary files differdeleted file mode 100644 index bf6de38..0000000 --- a/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormal.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormalInactive.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormalInactive.png Binary files differdeleted file mode 100644 index c61541c..0000000 --- a/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormalInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmall.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmall.png Binary files differdeleted file mode 100644 index 9f248f6..0000000 --- a/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmall.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmallInactive.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmallInactive.png Binary files differdeleted file mode 100644 index 7365c34..0000000 --- a/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmallInactive.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/treeDownTriangleBlack.png b/WebCore/page/inspector/Images/treeDownTriangleBlack.png Binary files differdeleted file mode 100644 index 0821112..0000000 --- a/WebCore/page/inspector/Images/treeDownTriangleBlack.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/treeDownTriangleWhite.png b/WebCore/page/inspector/Images/treeDownTriangleWhite.png Binary files differdeleted file mode 100644 index 1667b51..0000000 --- a/WebCore/page/inspector/Images/treeDownTriangleWhite.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/treeLeftTriangleBlack.png b/WebCore/page/inspector/Images/treeLeftTriangleBlack.png Binary files differdeleted file mode 100644 index 81bc7e0..0000000 --- a/WebCore/page/inspector/Images/treeLeftTriangleBlack.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/treeRightTriangleBlack.png b/WebCore/page/inspector/Images/treeRightTriangleBlack.png Binary files differdeleted file mode 100644 index 90de820..0000000 --- a/WebCore/page/inspector/Images/treeRightTriangleBlack.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/treeRightTriangleWhite.png b/WebCore/page/inspector/Images/treeRightTriangleWhite.png Binary files differdeleted file mode 100644 index 2b6a82f..0000000 --- a/WebCore/page/inspector/Images/treeRightTriangleWhite.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/warningIcon.png b/WebCore/page/inspector/Images/warningIcon.png Binary files differdeleted file mode 100644 index f37727e..0000000 --- a/WebCore/page/inspector/Images/warningIcon.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/warningMediumIcon.png b/WebCore/page/inspector/Images/warningMediumIcon.png Binary files differdeleted file mode 100644 index 291e111..0000000 --- a/WebCore/page/inspector/Images/warningMediumIcon.png +++ /dev/null diff --git a/WebCore/page/inspector/Images/warningsErrors.png b/WebCore/page/inspector/Images/warningsErrors.png Binary files differdeleted file mode 100644 index 878b593..0000000 --- a/WebCore/page/inspector/Images/warningsErrors.png +++ /dev/null diff --git a/WebCore/page/inspector/MetricsSidebarPane.js b/WebCore/page/inspector/MetricsSidebarPane.js deleted file mode 100644 index e805560..0000000 --- a/WebCore/page/inspector/MetricsSidebarPane.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.MetricsSidebarPane = function() -{ - WebInspector.SidebarPane.call(this, WebInspector.UIString("Metrics")); -} - -WebInspector.MetricsSidebarPane.prototype = { - update: function(node) - { - var body = this.bodyElement; - - body.removeChildren(); - - if (!node) - return; - - var style; - if (node.nodeType === Node.ELEMENT_NODE) - style = node.ownerDocument.defaultView.getComputedStyle(node); - if (!style) - return; - - var metricsElement = document.createElement("div"); - metricsElement.className = "metrics"; - - function boxPartValue(style, name, suffix) - { - var value = style.getPropertyValue(name + suffix); - if (value === "" || value === "0px") - value = "\u2012"; - return value.replace(/px$/, ""); - } - - // Display types for which margin is ignored. - var noMarginDisplayType = { - "table-cell": true, - "table-column": true, - "table-column-group": true, - "table-footer-group": true, - "table-header-group": true, - "table-row": true, - "table-row-group": true - }; - - // Display types for which padding is ignored. - var noPaddingDisplayType = { - "table-column": true, - "table-column-group": true, - "table-footer-group": true, - "table-header-group": true, - "table-row": true, - "table-row-group": true - }; - - var boxes = ["content", "padding", "border", "margin"]; - var boxLabels = [WebInspector.UIString("content"), WebInspector.UIString("padding"), WebInspector.UIString("border"), WebInspector.UIString("margin")]; - var previousBox; - for (var i = 0; i < boxes.length; ++i) { - var name = boxes[i]; - - if (name === "margin" && noMarginDisplayType[style.display]) - continue; - if (name === "padding" && noPaddingDisplayType[style.display]) - continue; - - var boxElement = document.createElement("div"); - boxElement.className = name; - - if (name === "content") { - var width = style.width.replace(/px$/, ""); - var height = style.height.replace(/px$/, ""); - boxElement.textContent = width + " \u00D7 " + height; - } else { - var suffix = (name === "border" ? "-width" : ""); - - var labelElement = document.createElement("div"); - labelElement.className = "label"; - labelElement.textContent = boxLabels[i]; - boxElement.appendChild(labelElement); - - var topElement = document.createElement("div"); - topElement.className = "top"; - topElement.textContent = boxPartValue(style, name + "-top", suffix); - boxElement.appendChild(topElement); - - var leftElement = document.createElement("div"); - leftElement.className = "left"; - leftElement.textContent = boxPartValue(style, name + "-left", suffix); - boxElement.appendChild(leftElement); - - if (previousBox) - boxElement.appendChild(previousBox); - - var rightElement = document.createElement("div"); - rightElement.className = "right"; - rightElement.textContent = boxPartValue(style, name + "-right", suffix); - boxElement.appendChild(rightElement); - - var bottomElement = document.createElement("div"); - bottomElement.className = "bottom"; - bottomElement.textContent = boxPartValue(style, name + "-bottom", suffix); - boxElement.appendChild(bottomElement); - } - - previousBox = boxElement; - } - - metricsElement.appendChild(previousBox); - body.appendChild(metricsElement); - } -} - -WebInspector.MetricsSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; diff --git a/WebCore/page/inspector/NetworkPanel.js b/WebCore/page/inspector/NetworkPanel.js deleted file mode 100644 index 2f000ee..0000000 --- a/WebCore/page/inspector/NetworkPanel.js +++ /dev/null @@ -1,1036 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.NetworkPanel = function() -{ - WebInspector.Panel.call(this); - - this.timelineEntries = []; - - this.timelineElement = document.createElement("div"); - this.timelineElement.className = "network-timeline"; - this.element.appendChild(this.timelineElement); - - this.summaryElement = document.createElement("div"); - this.summaryElement.className = "network-summary"; - this.element.appendChild(this.summaryElement); - - this.dividersElement = document.createElement("div"); - this.dividersElement.className = "network-dividers"; - this.timelineElement.appendChild(this.dividersElement); - - this.resourcesElement = document.createElement("div"); - this.resourcesElement.className = "network-resources"; - this.resourcesElement.addEventListener("click", this.resourcesClicked.bind(this), false); - this.timelineElement.appendChild(this.resourcesElement); - - var graphArea = document.createElement("div"); - graphArea.className = "network-graph-area"; - this.summaryElement.appendChild(graphArea); - - this.graphLabelElement = document.createElement("div"); - this.graphLabelElement.className = "network-graph-label"; - graphArea.appendChild(this.graphLabelElement); - - this.graphModeSelectElement = document.createElement("select"); - this.graphModeSelectElement.className = "network-graph-mode"; - this.graphModeSelectElement.addEventListener("change", this.changeGraphMode.bind(this), false); - this.graphLabelElement.appendChild(this.graphModeSelectElement); - this.graphLabelElement.appendChild(document.createElement("br")); - - var sizeOptionElement = document.createElement("option"); - sizeOptionElement.calculator = new WebInspector.TransferSizeCalculator(); - sizeOptionElement.textContent = sizeOptionElement.calculator.title; - this.graphModeSelectElement.appendChild(sizeOptionElement); - - var timeOptionElement = document.createElement("option"); - timeOptionElement.calculator = new WebInspector.TransferTimeCalculator(); - timeOptionElement.textContent = timeOptionElement.calculator.title; - this.graphModeSelectElement.appendChild(timeOptionElement); - - var graphSideElement = document.createElement("div"); - graphSideElement.className = "network-graph-side"; - graphArea.appendChild(graphSideElement); - - this.summaryGraphElement = document.createElement("canvas"); - this.summaryGraphElement.setAttribute("width", "450"); - this.summaryGraphElement.setAttribute("height", "38"); - this.summaryGraphElement.className = "network-summary-graph"; - graphSideElement.appendChild(this.summaryGraphElement); - - this.legendElement = document.createElement("div"); - this.legendElement.className = "network-graph-legend"; - graphSideElement.appendChild(this.legendElement); - - this.drawSummaryGraph(); // draws an empty graph - - this.needsRefresh = true; -} - -WebInspector.NetworkPanel.prototype = { - show: function() - { - WebInspector.Panel.prototype.show.call(this); - WebInspector.networkListItem.select(); - this.refreshIfNeeded(); - }, - - hide: function() - { - WebInspector.Panel.prototype.hide.call(this); - WebInspector.networkListItem.deselect(); - }, - - resize: function() - { - this.updateTimelineDividersIfNeeded(); - }, - - resourcesClicked: function(event) - { - // If the click wasn't inside a network resource row, ignore it. - var resourceElement = event.target.firstParentOrSelfWithClass("network-resource"); - if (!resourceElement) - return; - - // If the click was within the network info element, ignore it. - var networkInfo = event.target.firstParentOrSelfWithClass("network-info"); - if (networkInfo) - return; - - // If the click was within the tip balloon element, hide it. - var balloon = event.target.firstParentOrSelfWithClass("tip-balloon"); - if (balloon) { - resourceElement.timelineEntry.showingTipBalloon = false; - return; - } - - resourceElement.timelineEntry.toggleShowingInfo(); - }, - - changeGraphMode: function(event) - { - this.updateSummaryGraph(); - }, - - get calculator() - { - return this.graphModeSelectElement.options[this.graphModeSelectElement.selectedIndex].calculator; - }, - - get totalDuration() - { - return this.latestEndTime - this.earliestStartTime; - }, - - get needsRefresh() - { - return this._needsRefresh; - }, - - set needsRefresh(x) - { - if (this._needsRefresh === x) - return; - this._needsRefresh = x; - if (x && this.visible) - this.refresh(); - }, - - refreshIfNeeded: function() - { - if (this.needsRefresh) - this.refresh(); - }, - - refresh: function() - { - this.needsRefresh = false; - - // calling refresh will call updateTimelineBoundriesIfNeeded, which can clear needsRefresh for future entries, - // so find all the entries that needs refresh first, then loop back trough them to call refresh - var entriesNeedingRefresh = []; - var entriesLength = this.timelineEntries.length; - for (var i = 0; i < entriesLength; ++i) { - var entry = this.timelineEntries[i]; - if (entry.needsRefresh || entry.infoNeedsRefresh) - entriesNeedingRefresh.push(entry); - } - - entriesLength = entriesNeedingRefresh.length; - for (var i = 0; i < entriesLength; ++i) - entriesNeedingRefresh[i].refresh(false, true, true); - - this.updateTimelineDividersIfNeeded(); - this.sortTimelineEntriesIfNeeded(); - this.updateSummaryGraph(); - }, - - makeLegendElement: function(label, value, color) - { - var legendElement = document.createElement("label"); - legendElement.className = "network-graph-legend-item"; - - if (color) { - var swatch = document.createElement("canvas"); - swatch.className = "network-graph-legend-swatch"; - swatch.setAttribute("width", "13"); - swatch.setAttribute("height", "24"); - - legendElement.appendChild(swatch); - - this.drawSwatch(swatch, color); - } - - var labelElement = document.createElement("div"); - labelElement.className = "network-graph-legend-label"; - legendElement.appendChild(labelElement); - - var headerElement = document.createElement("div"); - var headerElement = document.createElement("div"); - headerElement.className = "network-graph-legend-header"; - headerElement.textContent = label; - labelElement.appendChild(headerElement); - - var valueElement = document.createElement("div"); - valueElement.className = "network-graph-legend-value"; - valueElement.textContent = value; - labelElement.appendChild(valueElement); - - return legendElement; - }, - - sortTimelineEntriesSoonIfNeeded: function() - { - if ("sortTimelineEntriesTimeout" in this) - return; - this.sortTimelineEntriesTimeout = setTimeout(this.sortTimelineEntriesIfNeeded.bind(this), 500); - }, - - sortTimelineEntriesIfNeeded: function() - { - if ("sortTimelineEntriesTimeout" in this) { - clearTimeout(this.sortTimelineEntriesTimeout); - delete this.sortTimelineEntriesTimeout; - } - - this.timelineEntries.sort(WebInspector.NetworkPanel.timelineEntryCompare); - - var nextSibling = null; - for (var i = (this.timelineEntries.length - 1); i >= 0; --i) { - var entry = this.timelineEntries[i]; - if (entry.resourceElement.nextSibling !== nextSibling) - this.resourcesElement.insertBefore(entry.resourceElement, nextSibling); - nextSibling = entry.resourceElement; - } - }, - - updateTimelineBoundriesIfNeeded: function(resource, immediate) - { - var didUpdate = false; - if (resource.startTime !== -1 && (this.earliestStartTime === undefined || resource.startTime < this.earliestStartTime)) { - this.earliestStartTime = resource.startTime; - didUpdate = true; - } - - if (resource.endTime !== -1 && (this.latestEndTime === undefined || resource.endTime > this.latestEndTime)) { - this.latestEndTime = resource.endTime; - didUpdate = true; - } - - if (didUpdate) { - if (immediate) { - this.refreshAllTimelineEntries(true, true, immediate); - this.updateTimelineDividersIfNeeded(); - } else { - this.refreshAllTimelineEntriesSoon(true, true, immediate); - this.updateTimelineDividersSoonIfNeeded(); - } - } - - return didUpdate; - }, - - updateTimelineDividersSoonIfNeeded: function() - { - if ("updateTimelineDividersTimeout" in this) - return; - this.updateTimelineDividersTimeout = setTimeout(this.updateTimelineDividersIfNeeded.bind(this), 500); - }, - - updateTimelineDividersIfNeeded: function() - { - if ("updateTimelineDividersTimeout" in this) { - clearTimeout(this.updateTimelineDividersTimeout); - delete this.updateTimelineDividersTimeout; - } - - if (!this.visible) { - this.needsRefresh = true; - return; - } - - if (document.body.offsetWidth <= 0) { - // The stylesheet hasn't loaded yet, so we need to update later. - setTimeout(this.updateTimelineDividersIfNeeded.bind(this), 0); - return; - } - - var dividerCount = Math.round(this.dividersElement.offsetWidth / 64); - var timeSlice = this.totalDuration / dividerCount; - - if (this.lastDividerTimeSlice === timeSlice) - return; - - this.lastDividerTimeSlice = timeSlice; - - this.dividersElement.removeChildren(); - - for (var i = 1; i <= dividerCount; ++i) { - var divider = document.createElement("div"); - divider.className = "network-divider"; - if (i === dividerCount) - divider.addStyleClass("last"); - divider.style.left = ((i / dividerCount) * 100) + "%"; - - var label = document.createElement("div"); - label.className = "network-divider-label"; - label.textContent = Number.secondsToString(timeSlice * i); - divider.appendChild(label); - - this.dividersElement.appendChild(divider); - } - }, - - refreshAllTimelineEntriesSoon: function(skipBoundryUpdate, skipTimelineSort, immediate) - { - if ("refreshAllTimelineEntriesTimeout" in this) - return; - this.refreshAllTimelineEntriesTimeout = setTimeout(this.refreshAllTimelineEntries.bind(this), 500, skipBoundryUpdate, skipTimelineSort, immediate); - }, - - refreshAllTimelineEntries: function(skipBoundryUpdate, skipTimelineSort, immediate) - { - if ("refreshAllTimelineEntriesTimeout" in this) { - clearTimeout(this.refreshAllTimelineEntriesTimeout); - delete this.refreshAllTimelineEntriesTimeout; - } - - var entriesLength = this.timelineEntries.length; - for (var i = 0; i < entriesLength; ++i) - this.timelineEntries[i].refresh(skipBoundryUpdate, skipTimelineSort, immediate); - }, - - fadeOutRect: function(ctx, x, y, w, h, a1, a2) - { - ctx.save(); - - var gradient = ctx.createLinearGradient(x, y, x, y + h); - gradient.addColorStop(0.0, "rgba(0, 0, 0, " + (1.0 - a1) + ")"); - gradient.addColorStop(0.8, "rgba(0, 0, 0, " + (1.0 - a2) + ")"); - gradient.addColorStop(1.0, "rgba(0, 0, 0, 1.0)"); - - ctx.globalCompositeOperation = "destination-out"; - - ctx.fillStyle = gradient; - ctx.fillRect(x, y, w, h); - - ctx.restore(); - }, - - drawSwatch: function(canvas, color) - { - var ctx = canvas.getContext("2d"); - - function drawSwatchSquare() { - ctx.fillStyle = color; - ctx.fillRect(0, 0, 13, 13); - - var gradient = ctx.createLinearGradient(0, 0, 13, 13); - gradient.addColorStop(0.0, "rgba(255, 255, 255, 0.2)"); - gradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); - - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, 13, 13); - - gradient = ctx.createLinearGradient(13, 13, 0, 0); - gradient.addColorStop(0.0, "rgba(0, 0, 0, 0.2)"); - gradient.addColorStop(1.0, "rgba(0, 0, 0, 0.0)"); - - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, 13, 13); - - ctx.strokeStyle = "rgba(0, 0, 0, 0.6)"; - ctx.strokeRect(0.5, 0.5, 12, 12); - } - - ctx.clearRect(0, 0, 13, 24); - - drawSwatchSquare(); - - ctx.save(); - - ctx.translate(0, 25); - ctx.scale(1, -1); - - drawSwatchSquare(); - - ctx.restore(); - - this.fadeOutRect(ctx, 0, 13, 13, 13, 0.5, 0.0); - }, - - drawSummaryGraph: function(segments) - { - if (!this.summaryGraphElement) - return; - - if (!segments || !segments.length) - segments = [{color: "white", value: 1}]; - - // Calculate the total of all segments. - var total = 0; - for (var i = 0; i < segments.length; ++i) - total += segments[i].value; - - // Calculate the percentage of each segment, rounded to the nearest percent. - var percents = segments.map(function(s) { return Math.max(Math.round(100 * s.value / total), 1) }); - - // Calculate the total percentage. - var percentTotal = 0; - for (var i = 0; i < percents.length; ++i) - percentTotal += percents[i]; - - // Make sure our percentage total is not greater-than 100, it can be greater - // if we rounded up for a few segments. - while (percentTotal > 100) { - for (var i = 0; i < percents.length && percentTotal > 100; ++i) { - if (percents[i] > 1) { - --percents[i]; - --percentTotal; - } - } - } - - // Make sure our percentage total is not less-than 100, it can be less - // if we rounded down for a few segments. - while (percentTotal < 100) { - for (var i = 0; i < percents.length && percentTotal < 100; ++i) { - ++percents[i]; - ++percentTotal; - } - } - - var ctx = this.summaryGraphElement.getContext("2d"); - - var x = 0; - var y = 0; - var w = 450; - var h = 19; - var r = (h / 2); - - function drawPillShadow() - { - // This draws a line with a shadow that is offset away from the line. The line is stroked - // twice with different X shadow offsets to give more feathered edges. Later we erase the - // line with destination-out 100% transparent black, leaving only the shadow. This only - // works if nothing has been drawn into the canvas yet. - - ctx.beginPath(); - ctx.moveTo(x + 4, y + h - 3 - 0.5); - ctx.lineTo(x + w - 4, y + h - 3 - 0.5); - ctx.closePath(); - - ctx.save(); - - ctx.shadowBlur = 2; - ctx.shadowColor = "rgba(0, 0, 0, 0.5)"; - ctx.shadowOffsetX = 3; - ctx.shadowOffsetY = 5; - - ctx.strokeStyle = "white"; - ctx.lineWidth = 1; - - ctx.stroke(); - - ctx.shadowOffsetX = -3; - - ctx.stroke(); - - ctx.restore(); - - ctx.save(); - - ctx.globalCompositeOperation = "destination-out"; - ctx.strokeStyle = "rgba(0, 0, 0, 1)"; - ctx.lineWidth = 1; - - ctx.stroke(); - - ctx.restore(); - } - - function drawPill() - { - // Make a rounded rect path. - ctx.beginPath(); - ctx.moveTo(x, y + r); - ctx.lineTo(x, y + h - r); - ctx.quadraticCurveTo(x, y + h, x + r, y + h); - ctx.lineTo(x + w - r, y + h); - ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r); - ctx.lineTo(x + w, y + r); - ctx.quadraticCurveTo(x + w, y, x + w - r, y); - ctx.lineTo(x + r, y); - ctx.quadraticCurveTo(x, y, x, y + r); - ctx.closePath(); - - // Clip to the rounded rect path. - ctx.save(); - ctx.clip(); - - // Fill the segments with the associated color. - var previousSegmentsWidth = 0; - for (var i = 0; i < segments.length; ++i) { - var segmentWidth = Math.round(w * percents[i] / 100); - ctx.fillStyle = segments[i].color; - ctx.fillRect(x + previousSegmentsWidth, y, segmentWidth, h); - previousSegmentsWidth += segmentWidth; - } - - // Draw the segment divider lines. - ctx.lineWidth = 1; - for (var i = 1; i < 20; ++i) { - ctx.beginPath(); - ctx.moveTo(x + (i * Math.round(w / 20)) + 0.5, y); - ctx.lineTo(x + (i * Math.round(w / 20)) + 0.5, y + h); - ctx.closePath(); - - ctx.strokeStyle = "rgba(0, 0, 0, 0.2)"; - ctx.stroke(); - - ctx.beginPath(); - ctx.moveTo(x + (i * Math.round(w / 20)) + 1.5, y); - ctx.lineTo(x + (i * Math.round(w / 20)) + 1.5, y + h); - ctx.closePath(); - - ctx.strokeStyle = "rgba(255, 255, 255, 0.2)"; - ctx.stroke(); - } - - // Draw the pill shading. - var lightGradient = ctx.createLinearGradient(x, y, x, y + (h / 1.5)); - lightGradient.addColorStop(0.0, "rgba(220, 220, 220, 0.6)"); - lightGradient.addColorStop(0.4, "rgba(220, 220, 220, 0.2)"); - lightGradient.addColorStop(1.0, "rgba(255, 255, 255, 0.0)"); - - var darkGradient = ctx.createLinearGradient(x, y + (h / 3), x, y + h); - darkGradient.addColorStop(0.0, "rgba(0, 0, 0, 0.0)"); - darkGradient.addColorStop(0.8, "rgba(0, 0, 0, 0.2)"); - darkGradient.addColorStop(1.0, "rgba(0, 0, 0, 0.5)"); - - ctx.fillStyle = darkGradient; - ctx.fillRect(x, y, w, h); - - ctx.fillStyle = lightGradient; - ctx.fillRect(x, y, w, h); - - ctx.restore(); - } - - ctx.clearRect(x, y, w, (h * 2)); - - drawPillShadow(); - drawPill(); - - ctx.save(); - - ctx.translate(0, (h * 2) + 1); - ctx.scale(1, -1); - - drawPill(); - - ctx.restore(); - - this.fadeOutRect(ctx, x, y + h + 1, w, h, 0.5, 0.0); - }, - - updateSummaryGraphSoon: function() - { - if ("updateSummaryGraphTimeout" in this) - return; - this.updateSummaryGraphTimeout = setTimeout(this.updateSummaryGraph.bind(this), 500); - }, - - updateSummaryGraph: function() - { - if ("updateSummaryGraphTimeout" in this) { - clearTimeout(this.updateSummaryGraphTimeout); - delete this.updateSummaryGraphTimeout; - } - - var graphInfo = this.calculator.computeValues(this.timelineEntries); - - var categoryOrder = ["documents", "stylesheets", "images", "scripts", "fonts", "other"]; - var categoryColors = {documents: {r: 47, g: 102, b: 236}, stylesheets: {r: 157, g: 231, b: 119}, images: {r: 164, g: 60, b: 255}, scripts: {r: 255, g: 121, b: 0}, fonts: {r: 231, g: 231, b: 10}, other: {r: 186, g: 186, b: 186}}; - var fillSegments = []; - - this.legendElement.removeChildren(); - - if (this.totalLegendLabel) - this.totalLegendLabel.parentNode.removeChild(this.totalLegendLabel); - - this.totalLegendLabel = this.makeLegendElement(this.calculator.totalTitle, this.calculator.formatValue(graphInfo.total)); - this.totalLegendLabel.addStyleClass("network-graph-legend-total"); - this.graphLabelElement.appendChild(this.totalLegendLabel); - - for (var i = 0; i < categoryOrder.length; ++i) { - var category = categoryOrder[i]; - var size = graphInfo.categoryValues[category]; - if (!size) - continue; - - var color = categoryColors[category]; - var colorString = "rgb(" + color.r + ", " + color.g + ", " + color.b + ")"; - - var fillSegment = {color: colorString, value: size}; - fillSegments.push(fillSegment); - - var legendLabel = this.makeLegendElement(WebInspector.resourceCategories[category].title, this.calculator.formatValue(size), colorString); - this.legendElement.appendChild(legendLabel); - } - - this.drawSummaryGraph(fillSegments); - }, - - clearTimeline: function() - { - delete this.earliestStartTime; - delete this.latestEndTime; - - var entriesLength = this.timelineEntries.length; - for (var i = 0; i < entriesLength; ++i) - delete this.timelineEntries[i].resource.networkTimelineEntry; - - this.timelineEntries = []; - this.resourcesElement.removeChildren(); - - this.drawSummaryGraph(); // draws an empty graph - }, - - addResourceToTimeline: function(resource) - { - var timelineEntry = new WebInspector.NetworkTimelineEntry(this, resource); - this.timelineEntries.push(timelineEntry); - this.resourcesElement.appendChild(timelineEntry.resourceElement); - - timelineEntry.refresh(); - this.updateSummaryGraphSoon(); - } -} - -WebInspector.NetworkPanel.prototype.__proto__ = WebInspector.Panel.prototype; - -WebInspector.NetworkPanel.timelineEntryCompare = function(a, b) -{ - if (a.resource.startTime < b.resource.startTime) - return -1; - if (a.resource.startTime > b.resource.startTime) - return 1; - if (a.resource.endTime < b.resource.endTime) - return -1; - if (a.resource.endTime > b.resource.endTime) - return 1; - return 0; -} - -WebInspector.NetworkTimelineEntry = function(panel, resource) -{ - this.panel = panel; - this.resource = resource; - resource.networkTimelineEntry = this; - - this.resourceElement = document.createElement("div"); - this.resourceElement.className = "network-resource"; - this.resourceElement.timelineEntry = this; - - this.titleElement = document.createElement("div"); - this.titleElement.className = "network-title"; - this.resourceElement.appendChild(this.titleElement); - - this.fileElement = document.createElement("div"); - this.fileElement.className = "network-file"; - this.fileElement.innerHTML = WebInspector.linkifyURL(resource.url, resource.displayName); - this.titleElement.appendChild(this.fileElement); - - this.tipButtonElement = document.createElement("button"); - this.tipButtonElement.className = "tip-button"; - this.showingTipButton = this.resource.tips.length; - this.fileElement.insertBefore(this.tipButtonElement, this.fileElement.firstChild); - - this.tipButtonElement.addEventListener("click", this.toggleTipBalloon.bind(this), false ); - - this.areaElement = document.createElement("div"); - this.areaElement.className = "network-area"; - this.titleElement.appendChild(this.areaElement); - - this.barElement = document.createElement("div"); - this.areaElement.appendChild(this.barElement); - - this.infoElement = document.createElement("div"); - this.infoElement.className = "network-info hidden"; - this.resourceElement.appendChild(this.infoElement); -} - -WebInspector.NetworkTimelineEntry.prototype = { - refresh: function(skipBoundryUpdate, skipTimelineSort, immediate) - { - if (!this.panel.visible) { - this.needsRefresh = true; - this.panel.needsRefresh = true; - return; - } - - delete this.needsRefresh; - - if (!skipBoundryUpdate) { - if (this.panel.updateTimelineBoundriesIfNeeded(this.resource, immediate)) - return; // updateTimelineBoundriesIfNeeded calls refresh() on all entries, so we can just return - } - - if (!skipTimelineSort) { - if (immediate) - this.panel.sortTimelineEntriesIfNeeded(); - else - this.panel.sortTimelineEntriesSoonIfNeeded(); - } - - if (this.resource.startTime !== -1) { - var percentStart = ((this.resource.startTime - this.panel.earliestStartTime) / this.panel.totalDuration) * 100; - this.barElement.style.left = percentStart + "%"; - } else { - this.barElement.style.left = null; - } - - if (this.resource.endTime !== -1) { - var percentEnd = ((this.panel.latestEndTime - this.resource.endTime) / this.panel.totalDuration) * 100; - this.barElement.style.right = percentEnd + "%"; - } else { - this.barElement.style.right = "0px"; - } - - this.barElement.className = "network-bar network-category-" + this.resource.category.name; - - if (this.infoNeedsRefresh) - this.refreshInfo(); - }, - - refreshInfo: function() - { - if (!this.showingInfo) { - this.infoNeedsRefresh = true; - return; - } - - if (!this.panel.visible) { - this.panel.needsRefresh = true; - this.infoNeedsRefresh = true; - return; - } - - this.infoNeedsRefresh = false; - - this.infoElement.removeChildren(); - - var sections = [ - {title: WebInspector.UIString("Request"), info: this.resource.sortedRequestHeaders}, - {title: WebInspector.UIString("Response"), info: this.resource.sortedResponseHeaders} - ]; - - function createSectionTable(section) - { - if (!section.info.length) - return; - - var table = document.createElement("table"); - this.infoElement.appendChild(table); - - var heading = document.createElement("th"); - heading.textContent = section.title; - - var row = table.createTHead().insertRow(-1).appendChild(heading); - var body = document.createElement("tbody"); - table.appendChild(body); - - section.info.forEach(function(header) { - var row = body.insertRow(-1); - var th = document.createElement("th"); - th.textContent = header.header; - row.appendChild(th); - row.insertCell(-1).textContent = header.value; - }); - } - - sections.forEach(createSectionTable, this); - }, - - refreshInfoIfNeeded: function() - { - if (this.infoNeedsRefresh === false) - return; - - this.refreshInfo(); - }, - - toggleShowingInfo: function() - { - this.showingInfo = !this.showingInfo; - }, - - get showingInfo() - { - return this._showingInfo; - }, - - set showingInfo(x) - { - if (this._showingInfo === x) - return; - - this._showingInfo = x; - - var element = this.infoElement; - if (x) { - element.removeStyleClass("hidden"); - element.style.setProperty("overflow", "hidden"); - this.refreshInfoIfNeeded(); - WebInspector.animateStyle([{element: element, start: {height: 0}, end: {height: element.offsetHeight}}], 250, function() { element.style.removeProperty("height"); element.style.removeProperty("overflow") }); - } else { - element.style.setProperty("overflow", "hidden"); - WebInspector.animateStyle([{element: element, end: {height: 0}}], 250, function() { element.addStyleClass("hidden"); element.style.removeProperty("height") }); - } - }, - - get showingTipButton() - { - return !this.tipButtonElement.hasStyleClass("hidden"); - }, - - set showingTipButton(x) - { - if (x) - this.tipButtonElement.removeStyleClass("hidden"); - else - this.tipButtonElement.addStyleClass("hidden"); - }, - - toggleTipBalloon: function(event) - { - this.showingTipBalloon = !this.showingTipBalloon; - event.stopPropagation(); - }, - - get showingTipBalloon() - { - return this._showingTipBalloon; - }, - - set showingTipBalloon(x) - { - if (this._showingTipBalloon === x) - return; - - this._showingTipBalloon = x; - - if (x) { - if (!this.tipBalloonElement) { - this.tipBalloonElement = document.createElement("div"); - this.tipBalloonElement.className = "tip-balloon"; - this.titleElement.appendChild(this.tipBalloonElement); - - this.tipBalloonContentElement = document.createElement("div"); - this.tipBalloonContentElement.className = "tip-balloon-content"; - this.tipBalloonElement.appendChild(this.tipBalloonContentElement); - var tipText = ""; - for (var id in this.resource.tips) - tipText += this.resource.tips[id].message + "\n"; - this.tipBalloonContentElement.textContent = tipText; - } - - this.tipBalloonElement.removeStyleClass("hidden"); - WebInspector.animateStyle([{element: this.tipBalloonElement, start: {left: 160, opacity: 0}, end: {left: 145, opacity: 1}}], 250); - } else { - var element = this.tipBalloonElement; - WebInspector.animateStyle([{element: this.tipBalloonElement, start: {left: 145, opacity: 1}, end: {left: 160, opacity: 0}}], 250, function() { element.addStyleClass("hidden") }); - } - } -} - -WebInspector.TimelineValueCalculator = function() -{ -} - -WebInspector.TimelineValueCalculator.prototype = { - computeValues: function(entries) - { - var total = 0; - var categoryValues = {}; - - function compute(entry) - { - var value = this._value(entry); - if (value === undefined) - return; - - if (!(entry.resource.category.name in categoryValues)) - categoryValues[entry.resource.category.name] = 0; - categoryValues[entry.resource.category.name] += value; - total += value; - } - entries.forEach(compute, this); - - return {categoryValues: categoryValues, total: total}; - }, - - _value: function(entry) - { - return 0; - }, - - get title() - { - return ""; - }, - - formatValue: function(value) - { - return value.toString(); - } -} - -WebInspector.TransferTimeCalculator = function() -{ - WebInspector.TimelineValueCalculator.call(this); -} - -WebInspector.TransferTimeCalculator.prototype = { - computeValues: function(entries) - { - var entriesByCategory = {}; - entries.forEach(function(entry) { - if (!(entry.resource.category.name in entriesByCategory)) - entriesByCategory[entry.resource.category.name] = []; - entriesByCategory[entry.resource.category.name].push(entry); - }); - - var earliestStart; - var latestEnd; - var categoryValues = {}; - for (var category in entriesByCategory) { - entriesByCategory[category].sort(WebInspector.NetworkPanel.timelineEntryCompare); - categoryValues[category] = 0; - - var segment = {start: -1, end: -1}; - entriesByCategory[category].forEach(function(entry) { - if (entry.resource.startTime == -1 || entry.resource.endTime == -1) - return; - - if (earliestStart === undefined) - earliestStart = entry.resource.startTime; - else - earliestStart = Math.min(earliestStart, entry.resource.startTime); - - if (latestEnd === undefined) - latestEnd = entry.resource.endTime; - else - latestEnd = Math.max(latestEnd, entry.resource.endTime); - - if (entry.resource.startTime <= segment.end) { - segment.end = Math.max(segment.end, entry.resource.endTime); - return; - } - - categoryValues[category] += segment.end - segment.start; - - segment.start = entry.resource.startTime; - segment.end = entry.resource.endTime; - }); - - // Add the last segment - categoryValues[category] += segment.end - segment.start; - } - - return {categoryValues: categoryValues, total: latestEnd - earliestStart}; - }, - - get title() - { - return WebInspector.UIString("Transfer Time"); - }, - - get totalTitle() - { - return WebInspector.UIString("Total Time"); - }, - - formatValue: function(value) - { - return Number.secondsToString(value); - } -} - -WebInspector.TransferTimeCalculator.prototype.__proto__ = WebInspector.TimelineValueCalculator.prototype; - -WebInspector.TransferSizeCalculator = function() -{ - WebInspector.TimelineValueCalculator.call(this); -} - -WebInspector.TransferSizeCalculator.prototype = { - _value: function(entry) - { - return entry.resource.contentLength; - }, - - get title() - { - return WebInspector.UIString("Transfer Size"); - }, - - get totalTitle() - { - return WebInspector.UIString("Total Size"); - }, - - formatValue: function(value) - { - return Number.bytesToString(value); - } -} - -WebInspector.TransferSizeCalculator.prototype.__proto__ = WebInspector.TimelineValueCalculator.prototype; diff --git a/WebCore/page/inspector/Panel.js b/WebCore/page/inspector/Panel.js deleted file mode 100644 index 7631fa3..0000000 --- a/WebCore/page/inspector/Panel.js +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.Panel = function(views) -{ - this._visible = false; - - this.element = document.createElement("div"); - this.element.className = "panel"; - - this.views = {}; - this.viewButtons = []; - - if (views) { - var selectViewFunction = function(event) - { - var clickedView = event.currentTarget.view; - clickedView.panel.currentView = clickedView; - }; - - for (var i = 0; i < views.length; ++i) { - var view = views[i]; - view.panel = this; - - view.buttonElement = document.createElement("button"); - view.buttonElement.title = view.title; - view.buttonElement.addEventListener("click", selectViewFunction, false); - view.buttonElement.appendChild(document.createElement("img")); - view.buttonElement.view = view; - - view.contentElement = document.createElement("div"); - view.contentElement.className = "content " + view.name; - - this.views[view.name] = view; - this.viewButtons.push(view.buttonElement); - this.element.appendChild(view.contentElement); - } - } -} - -WebInspector.Panel.prototype = { - show: function() - { - this._visible = true; - if (!this.element.parentNode) - this.attach(); - this.element.addStyleClass("selected"); - this.updateToolbar(); - if (this.currentView && this.currentView.show) - this.currentView.show(); - }, - - hide: function() - { - if (this.currentView && this.currentView.hide) - this.currentView.hide(); - document.getElementById("toolbarButtons").removeChildren(); - this.element.removeStyleClass("selected"); - this._visible = false; - }, - - updateToolbar: function() - { - var buttonContainer = document.getElementById("toolbarButtons"); - buttonContainer.removeChildren(); - - var buttons = this.viewButtons; - if (buttons.length < 2) - return; - - for (var i = 0; i < buttons.length; ++i) { - var button = buttons[i]; - - if (i === 0) - button.addStyleClass("first"); - else if (i === (buttons.length - 1)) - button.addStyleClass("last"); - - if (i) { - var divider = document.createElement("img"); - divider.className = "split-button-divider"; - buttonContainer.appendChild(divider); - } - - button.addStyleClass("split-button"); - button.addStyleClass("view-button-" + button.title.toLowerCase()); - - buttonContainer.appendChild(button); - } - }, - - attach: function() - { - document.getElementById("panels").appendChild(this.element); - }, - - detach: function() - { - if (WebInspector.currentPanel === this) - WebInspector.currentPanel = null; - if (this.element && this.element.parentNode) - this.element.parentNode.removeChild(this.element); - }, - - get currentView() - { - return this._currentView; - }, - - set currentView(x) - { - if (typeof x === "string" || x instanceof String) - x = this.views[x]; - - if (this._currentView === x) - return; - - if (this !== x.panel) { - console.error("Set currentView to a view " + x.title + " whose panel is not this panel"); - return; - } - - if (this._currentView) { - this._currentView.buttonElement.removeStyleClass("selected"); - this._currentView.contentElement.removeStyleClass("selected"); - if (this._currentView.hide) - this._currentView.hide(); - } - - this._currentView = x; - - if (x) { - x.buttonElement.addStyleClass("selected"); - x.contentElement.addStyleClass("selected"); - if (x.show) - x.show(); - } - }, - - get visible() - { - return this._visible; - }, - - set visible(x) - { - if (this._visible === x) - return; - - if (x) - this.show(); - else - this.hide(); - } -} diff --git a/WebCore/page/inspector/PropertiesSection.js b/WebCore/page/inspector/PropertiesSection.js deleted file mode 100644 index b2a3473..0000000 --- a/WebCore/page/inspector/PropertiesSection.js +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.PropertiesSection = function(title, subtitle) -{ - this.element = document.createElement("div"); - this.element.className = "section"; - - this.headerElement = document.createElement("div"); - this.headerElement.className = "header"; - - this.titleElement = document.createElement("div"); - this.titleElement.className = "title"; - - this.subtitleElement = document.createElement("div"); - this.subtitleElement.className = "subtitle"; - - this.headerElement.appendChild(this.titleElement); - this.headerElement.appendChild(this.subtitleElement); - this.headerElement.addEventListener("click", this.toggleExpanded.bind(this), false); - - this.propertiesElement = document.createElement("ol"); - this.propertiesElement.className = "properties"; - this.propertiesTreeOutline = new TreeOutline(this.propertiesElement); - this.propertiesTreeOutline.section = this; - - this.element.appendChild(this.headerElement); - this.element.appendChild(this.propertiesElement); - - this.title = title; - this.subtitle = subtitle; - this.expanded = false; -} - -WebInspector.PropertiesSection.prototype = { - get title() - { - return this._title; - }, - - set title(x) - { - if (this._title === x) - return; - this._title = x; - this.titleElement.textContent = x; - }, - - get subtitle() - { - return this._subtitle; - }, - - set subtitle(x) - { - if (this._subtitle === x) - return; - this._subtitle = x; - this.subtitleElement.innerHTML = x; - }, - - get expanded() - { - return this._expanded; - }, - - set expanded(x) - { - if (x) - this.expand(); - else - this.collapse(); - }, - - get populated() - { - return this._populated; - }, - - set populated(x) - { - this._populated = x; - if (!x && this.onpopulate && this._expanded) { - this.onpopulate(this); - this._populated = true; - } - }, - - expand: function() - { - if (this._expanded) - return; - this._expanded = true; - this.element.addStyleClass("expanded"); - - if (!this._populated && this.onpopulate) { - this.onpopulate(this); - this._populated = true; - } - }, - - collapse: function() - { - if (!this._expanded) - return; - this._expanded = false; - this.element.removeStyleClass("expanded"); - }, - - toggleExpanded: function() - { - this.expanded = !this.expanded; - } -} diff --git a/WebCore/page/inspector/PropertiesSidebarPane.js b/WebCore/page/inspector/PropertiesSidebarPane.js deleted file mode 100644 index 0266d53..0000000 --- a/WebCore/page/inspector/PropertiesSidebarPane.js +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.PropertiesSidebarPane = function() -{ - WebInspector.SidebarPane.call(this, WebInspector.UIString("Properties")); -} - -WebInspector.PropertiesSidebarPane.prototype = { - update: function(object) - { - var body = this.bodyElement; - - body.removeChildren(); - - this.sections = []; - - if (!object) - return; - - for (var prototype = object; prototype; prototype = prototype.__proto__) { - var section = new WebInspector.ObjectPropertiesSection(prototype); - this.sections.push(section); - body.appendChild(section.element); - } - } -} - -WebInspector.PropertiesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; - -WebInspector.ObjectPropertiesSection = function(object) -{ - var title = Object.describe(object); - var subtitle; - if (title.match(/Prototype$/)) { - title = title.replace(/Prototype$/, ""); - subtitle = WebInspector.UIString("Prototype"); - } - - this.object = object; - - WebInspector.PropertiesSection.call(this, title, subtitle); -} - -WebInspector.ObjectPropertiesSection.prototype = { - onpopulate: function() - { - var properties = Object.sortedProperties(this.object); - for (var i = 0; i < properties.length; ++i) { - var propertyName = properties[i]; - if (!this.object.hasOwnProperty(propertyName) || propertyName === "__treeElementIdentifier") - continue; - this.propertiesTreeOutline.appendChild(new WebInspector.ObjectPropertyTreeElement(this.object, propertyName)); - } - } -} - -WebInspector.ObjectPropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype; - -WebInspector.ObjectPropertyTreeElement = function(parentObject, propertyName) -{ - this.parentObject = parentObject; - this.propertyName = propertyName; - - var childObject = this.safePropertyValue(parentObject, propertyName); - var isGetter = parentObject.__lookupGetter__(propertyName); - - var title = "<span class=\"name\">" + propertyName.escapeHTML() + "</span>: "; - if (!isGetter) - title += "<span class=\"value\">" + Object.describe(childObject, true).escapeHTML() + "</span>"; - else - // FIXME: this should show something like "getter" once we can change localization (bug 16734). - title += "<span class=\"value dimmed\">—</span>"; - - var hasSubProperties = false; - var type = typeof childObject; - if (childObject && (type === "object" || type === "function")) { - for (subPropertyName in childObject) { - if (subPropertyName === "__treeElementIdentifier") - continue; - hasSubProperties = true; - break; - } - } - - TreeElement.call(this, title, null, hasSubProperties); -} - -WebInspector.ObjectPropertyTreeElement.prototype = { - safePropertyValue: function(object, propertyName) - { - var getter = object.__lookupGetter__(propertyName); - if (getter) - return; - return object[propertyName]; - }, - - onpopulate: function() - { - if (this.children.length) - return; - - var childObject = this.safePropertyValue(this.parentObject, this.propertyName); - var properties = Object.sortedProperties(childObject); - for (var i = 0; i < properties.length; ++i) { - var propertyName = properties[i]; - if (propertyName === "__treeElementIdentifier") - continue; - this.appendChild(new WebInspector.ObjectPropertyTreeElement(childObject, propertyName)); - } - } -} - -WebInspector.ObjectPropertyTreeElement.prototype.__proto__ = TreeElement.prototype; diff --git a/WebCore/page/inspector/Resource.js b/WebCore/page/inspector/Resource.js deleted file mode 100644 index 2f87b13..0000000 --- a/WebCore/page/inspector/Resource.js +++ /dev/null @@ -1,690 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.Resource = function(requestHeaders, url, domain, path, lastPathComponent, identifier, mainResource, cached) -{ - this.identifier = identifier; - - this.startTime = -1; - this.endTime = -1; - this.mainResource = mainResource; - this.requestHeaders = requestHeaders; - this.url = url; - this.domain = domain; - this.path = path; - this.lastPathComponent = lastPathComponent; - this.cached = cached; - - this.listItem = new WebInspector.ResourceTreeElement(this); - this.updateTitle(); - - this.category = WebInspector.resourceCategories.other; -} - -// Keep these in sync with WebCore::InspectorResource::Type -WebInspector.Resource.Type = { - Document: 0, - Stylesheet: 1, - Image: 2, - Font: 3, - Script: 4, - Other: 5, - - isTextType: function(type) - { - return (type == this.Document) || (type == this.Stylesheet) || (type == this.Script); - }, - - toString: function(type) - { - switch (type) { - case this.Document: - return WebInspector.UIString("document"); - case this.Stylesheet: - return WebInspector.UIString("stylesheet"); - case this.Image: - return WebInspector.UIString("image"); - case this.Font: - return WebInspector.UIString("font"); - case this.Script: - return WebInspector.UIString("script"); - case this.Other: - default: - return WebInspector.UIString("other"); - } - } -} - -WebInspector.Resource.prototype = { - get url() - { - return this._url; - }, - - set url(x) - { - if (this._url === x) - return; - - var oldURL = this._url; - this._url = x; - WebInspector.resourceURLChanged(this, oldURL); - this.updateTitleSoon(); - }, - - get domain() - { - return this._domain; - }, - - set domain(x) - { - if (this._domain === x) - return; - this._domain = x; - this.updateTitleSoon(); - }, - - get lastPathComponent() - { - return this._lastPathComponent; - }, - - set lastPathComponent(x) - { - if (this._lastPathComponent === x) - return; - this._lastPathComponent = x; - this._lastPathComponentLowerCase = x ? x.toLowerCase() : null; - this.updateTitleSoon(); - }, - - get displayName() - { - var title = this.lastPathComponent; - if (!title) - title = this.domain; - if (!title) - title = this.url; - return title; - }, - - get startTime() - { - return this._startTime; - }, - - set startTime(x) - { - if (this._startTime === x) - return; - - this._startTime = x; - - if (this.networkTimelineEntry) - this.networkTimelineEntry.refresh(); - }, - - get responseReceivedTime() - { - return this._responseReceivedTime; - }, - - set responseReceivedTime(x) - { - if (this._responseReceivedTime === x) - return; - - this._responseReceivedTime = x; - - if (this.networkTimelineEntry) - this.networkTimelineEntry.refresh(); - }, - - get endTime() - { - return this._endTime; - }, - - set endTime(x) - { - if (this._endTime === x) - return; - - this._endTime = x; - - if (this.networkTimelineEntry) - this.networkTimelineEntry.refresh(); - }, - - get contentLength() - { - return this._contentLength; - }, - - set contentLength(x) - { - if (this._contentLength === x) - return; - - this._contentLength = x; - - if (this._expectedContentLength && this._expectedContentLength > x) { - this.updateTitle(); - var canvas = document.getElementById("loadingIcon" + this.identifier); - if (canvas) - WebInspector.drawLoadingPieChart(canvas, (x / this._expectedContentLength)); - } - - WebInspector.networkPanel.updateSummaryGraphSoon(); - }, - - get expectedContentLength() - { - return this._expectedContentLength; - }, - - set expectedContentLength(x) - { - if (this._expectedContentLength === x) - return; - - this._expectedContentLength = x; - - if (x && this._contentLength && this._contentLength <= x) { - var canvas = document.getElementById("loadingIcon" + this.identifier); - if (canvas) - WebInspector.drawLoadingPieChart(canvas, (this._contentLength / x)); - } - }, - - get finished() - { - return this._finished; - }, - - set finished(x) - { - if (this._finished === x) - return; - - this._finished = x; - - if (x) { - var canvas = document.getElementById("loadingIcon" + this.identifier); - if (canvas) - canvas.parentNode.removeChild(canvas); - - this._checkTips(); - this._checkWarnings(); - } - - this.updateTitleSoon(); - this.updatePanel(); - }, - - get failed() - { - return this._failed; - }, - - set failed(x) - { - this._failed = x; - - this.updateTitleSoon(); - this.updatePanel(); - }, - - get category() - { - return this._category; - }, - - set category(x) - { - if (this._category === x) - return; - - var oldCategory = this._category; - if (oldCategory) - oldCategory.removeResource(this); - - this._category = x; - this.updateTitle(); - - if (this._category) - this._category.addResource(this); - - this.updatePanel(); - }, - - get mimeType() - { - return this._mimeType; - }, - - set mimeType(x) - { - if (this._mimeType === x) - return; - - this._mimeType = x; - }, - - get type() - { - return this._type; - }, - - set type(x) - { - if (this._type === x) - return; - - this._type = x; - - switch (x) { - case WebInspector.Resource.Type.Document: - this.category = WebInspector.resourceCategories.documents; - break; - case WebInspector.Resource.Type.Stylesheet: - this.category = WebInspector.resourceCategories.stylesheets; - break; - case WebInspector.Resource.Type.Script: - this.category = WebInspector.resourceCategories.scripts; - break; - case WebInspector.Resource.Type.Image: - this.category = WebInspector.resourceCategories.images; - break; - case WebInspector.Resource.Type.Font: - this.category = WebInspector.resourceCategories.fonts; - break; - case WebInspector.Resource.Type.Other: - default: - this.category = WebInspector.resourceCategories.other; - break; - } - }, - - get documentNode() { - if ("identifier" in this) - return InspectorController.getResourceDocumentNode(this.identifier); - return null; - }, - - get requestHeaders() - { - if (this._requestHeaders === undefined) - this._requestHeaders = {}; - return this._requestHeaders; - }, - - set requestHeaders(x) - { - if (this._requestHeaders === x) - return; - - this._requestHeaders = x; - delete this._sortedRequestHeaders; - - if (this.networkTimelineEntry) - this.networkTimelineEntry.refreshInfo(); - }, - - get sortedRequestHeaders() - { - if (this._sortedRequestHeaders !== undefined) - return this._sortedRequestHeaders; - - this._sortedRequestHeaders = []; - for (var key in this.requestHeaders) - this._sortedRequestHeaders.push({header: key, value: this.requestHeaders[key]}); - this._sortedRequestHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) }); - - return this._sortedRequestHeaders; - }, - - get responseHeaders() - { - if (this._responseHeaders === undefined) - this._responseHeaders = {}; - return this._responseHeaders; - }, - - set responseHeaders(x) - { - if (this._responseHeaders === x) - return; - - this._responseHeaders = x; - delete this._sortedResponseHeaders; - - if (this.networkTimelineEntry) - this.networkTimelineEntry.refreshInfo(); - }, - - get sortedResponseHeaders() - { - if (this._sortedResponseHeaders !== undefined) - return this._sortedResponseHeaders; - - this._sortedResponseHeaders = []; - for (var key in this.responseHeaders) - this._sortedResponseHeaders.push({header: key, value: this.responseHeaders[key]}); - this._sortedResponseHeaders.sort(function(a,b) { return a.header.localeCompare(b.header) }); - - return this._sortedResponseHeaders; - }, - - get tips() - { - if (!("_tips" in this)) - this._tips = {}; - return this._tips; - }, - - _addTip: function(tip) - { - if (tip.id in this.tips) - return; - - this.tips[tip.id] = tip; - - // FIXME: Re-enable this code once we have a scope bar in the Console. - // Otherwise, we flood the Console with too many tips. - /* - var msg = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.Other, - WebInspector.ConsoleMessage.MessageLevel.Tip, tip.message, -1, this.url); - WebInspector.consolePanel.addMessage(msg); - */ - - if (this.networkTimelineEntry) - this.networkTimelineEntry.showingTipButton = true; - }, - - _checkTips: function() - { - for (var tip in WebInspector.Tips) - this._checkTip(WebInspector.Tips[tip]); - }, - - _checkTip: function(tip) - { - var addTip = false; - switch (tip.id) { - case WebInspector.Tips.ResourceNotCompressed.id: - addTip = this._shouldCompress(); - break; - } - - if (addTip) - this._addTip(tip); - }, - - _shouldCompress: function() - { - return WebInspector.Resource.Type.isTextType(this.type) - && this.domain - && !("Content-Encoding" in this.responseHeaders) - && this.contentLength !== undefined - && this.contentLength >= 512; - }, - - _mimeTypeIsConsistentWithType: function() - { - if (this.type === undefined || this.type === WebInspector.Resource.Type.Other) - return true; - - if (this.mimeType in WebInspector.MIMETypes) - return this.type in WebInspector.MIMETypes[this.mimeType]; - - return true; - }, - - _checkWarnings: function() - { - for (var warning in WebInspector.Warnings) - this._checkWarning(WebInspector.Warnings[warning]); - }, - - _checkWarning: function(warning) - { - var addWarning = false; - var msg; - switch (warning.id) { - case WebInspector.Warnings.IncorrectMIMEType.id: - if (!this._mimeTypeIsConsistentWithType()) - msg = new WebInspector.ConsoleMessage(WebInspector.ConsoleMessage.MessageSource.Other, - WebInspector.ConsoleMessage.MessageLevel.Warning, - String.sprintf(WebInspector.Warnings.IncorrectMIMEType.message, - WebInspector.Resource.Type.toString(this.type), this.mimeType), - -1, this.url); - break; - } - - if (msg) - WebInspector.consolePanel.addMessage(msg); - }, - - updateTitleSoon: function() - { - if (this.updateTitleTimeout) - return; - this.updateTitleTimeout = setTimeout(this.updateTitle.bind(this), 0); - }, - - updateTitle: function() - { - delete this.updateTitleTimeout; - - var title = this.displayName; - - var info = ""; - if (this.domain && (!WebInspector.mainResource || (WebInspector.mainResource && this.domain !== WebInspector.mainResource.domain))) - info = this.domain; - - if (this.path && this.lastPathComponent) { - var lastPathComponentIndex = this.path.lastIndexOf("/" + this.lastPathComponent); - if (lastPathComponentIndex != -1) - info += this.path.substring(0, lastPathComponentIndex); - } - - var fullTitle = ""; - - if (this.errors) - fullTitle += "<span class=\"count errors\">" + (this.errors + this.warnings) + "</span>"; - else if (this.warnings) - fullTitle += "<span class=\"count warnings\">" + this.warnings + "</span>"; - - fullTitle += "<span class=\"title" + (info && info.length ? "" : " only") + "\">" + title.escapeHTML() + "</span>"; - if (info && info.length) - fullTitle += "<span class=\"info\">" + info.escapeHTML() + "</span>"; - - var iconClass = "icon"; - switch (this.category) { - default: - break; - case WebInspector.resourceCategories.images: - case WebInspector.resourceCategories.other: - iconClass = "icon plain"; - break; - case WebInspector.resourceCategories.fonts: - iconClass = "icon font"; - } - - if (!this.finished) - fullTitle += "<div class=\"" + iconClass + "\"><canvas id=\"loadingIcon" + this.identifier + "\" class=\"progress\" width=\"16\" height=\"16\"></canvas></div>"; - else if (this.category === WebInspector.resourceCategories.images) - fullTitle += "<div class=\"" + iconClass + "\"><img class=\"preview\" src=\"" + this.url + "\"></div>"; - else if (this.category === WebInspector.resourceCategories.fonts) { - var uniqueFontName = "WebInspectorFontPreview" + this.identifier; - - this.fontStyleElement = document.createElement("style"); - this.fontStyleElement.textContent = "@font-face { font-family: \"" + uniqueFontName + "\"; src: url(" + this.url + "); }"; - document.getElementsByTagName("head").item(0).appendChild(this.fontStyleElement); - - fullTitle += "<div class=\"" + iconClass + "\"><div class=\"preview\" style=\"font-family: " + uniqueFontName + "\">Ag</div></div>"; - } else - fullTitle += "<div class=\"" + iconClass + "\"></div>"; - - this.listItem.title = fullTitle; - this.listItem.tooltip = this.url; - }, - - updatePanel: function() - { - if (this._panel) { - var current = (WebInspector.currentPanel === this._panel); - - this._panel.detach(); - delete this._panel; - - if (current) - WebInspector.currentPanel = this.panel; - } - }, - - get panel() - { - if (!this._panel) { - if (this.finished && !this.failed) { - switch (this.category) { - case WebInspector.resourceCategories.documents: - this._panel = new WebInspector.DocumentPanel(this); - break; - case WebInspector.resourceCategories.stylesheets: - case WebInspector.resourceCategories.scripts: - this._panel = new WebInspector.SourcePanel(this); - break; - case WebInspector.resourceCategories.images: - this._panel = new WebInspector.ImagePanel(this); - break; - case WebInspector.resourceCategories.fonts: - this._panel = new WebInspector.FontPanel(this); - break; - } - } - - if (!this._panel) - this._panel = new WebInspector.ResourcePanel(this); - } - - return this._panel; - }, - - select: function() - { - WebInspector.navigateToResource(this); - }, - - deselect: function() - { - this.listItem.deselect(true); - if (WebInspector.currentPanel === this._panel) - WebInspector.currentPanel = null; - }, - - attach: function() - { - if (this._panel) - this._panel.attach(); - }, - - detach: function() - { - if (this._panel) - this._panel.detach(); - if (this.fontStyleElement && this.fontStyleElement.parentNode) - this.fontStyleElement.parentNode.removeChild(this.fontStyleElement); - }, - - get errors() - { - if (!("_errors" in this)) - this._errors = 0; - - return this._errors; - }, - - set errors(x) - { - if (this._errors === x) - return; - - this._errors = x; - this.updateTitleSoon(); - }, - - get warnings() - { - if (!("_warnings" in this)) - this._warnings = 0; - - return this._warnings; - }, - - set warnings(x) - { - if (this._warnings === x) - return; - - this._warnings = x; - this.updateTitleSoon(); - } -} - -WebInspector.ResourceTreeElement = function(resource) -{ - TreeElement.call(this, "", resource, false); - this.resource = resource; -} - -WebInspector.ResourceTreeElement.prototype = { - onselect: function() - { - var selectedElement = WebInspector.fileOutline.selectedTreeElement; - if (selectedElement) - selectedElement.deselect(); - this.resource.select(); - }, - - ondeselect: function() - { - this.resource.deselect(); - }, - - onreveal: function() - { - if (!this.listItemElement || !this.treeOutline || !this.treeOutline.childrenListElement) - return; - this.treeOutline.childrenListElement.scrollToElement(this.listItemElement); - } -} - -WebInspector.ResourceTreeElement.prototype.__proto__ = TreeElement.prototype; diff --git a/WebCore/page/inspector/ResourceCategory.js b/WebCore/page/inspector/ResourceCategory.js deleted file mode 100644 index a2f2ba4..0000000 --- a/WebCore/page/inspector/ResourceCategory.js +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.ResourceCategory = function(title, name) -{ - this.name = name; - this.title = title; - this.resources = []; - this.listItem = new WebInspector.ResourceCategoryTreeElement(this); - this.listItem.hidden = true; - WebInspector.fileOutline.appendChild(this.listItem); -} - -WebInspector.ResourceCategory.prototype = { - toString: function() - { - return this.title; - }, - - addResource: function(resource) - { - var a = resource; - var resourcesLength = this.resources.length; - for (var i = 0; i < resourcesLength; ++i) { - var b = this.resources[i]; - if (a._lastPathComponentLowerCase && b._lastPathComponentLowerCase) - if (a._lastPathComponentLowerCase < b._lastPathComponentLowerCase) - break; - else if (a.name && b.name) - if (a.name < b.name) - break; - } - - this.resources.splice(i, 0, resource); - this.listItem.insertChild(resource.listItem, i); - this.listItem.hidden = false; - - resource.attach(); - }, - - removeResource: function(resource) - { - resource.detach(); - - var resourcesLength = this.resources.length; - for (var i = 0; i < resourcesLength; ++i) { - if (this.resources[i] === resource) { - this.resources.splice(i, 1); - break; - } - } - - this.listItem.removeChild(resource.listItem); - - if (!this.resources.length) - this.listItem.hidden = true; - }, - - removeAllResources: function(resource) - { - var resourcesLength = this.resources.length; - for (var i = 0; i < resourcesLength; ++i) - this.resources[i].detach(); - this.resources = []; - this.listItem.removeChildren(); - this.listItem.hidden = true; - } -} - -WebInspector.ResourceCategoryTreeElement = function(category) -{ - TreeElement.call(this, category.title, category, true); -} - -WebInspector.ResourceCategoryTreeElement.prototype = { - selectable: false, - arrowToggleWidth: 20 -} - -WebInspector.ResourceCategoryTreeElement.prototype.__proto__ = TreeElement.prototype; diff --git a/WebCore/page/inspector/SidebarPane.js b/WebCore/page/inspector/SidebarPane.js deleted file mode 100644 index 53f9d6d..0000000 --- a/WebCore/page/inspector/SidebarPane.js +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.SidebarPane = function(title) -{ - this.element = document.createElement("div"); - this.element.className = "pane"; - - this.titleElement = document.createElement("div"); - this.titleElement.className = "title"; - this.titleElement.addEventListener("click", this.toggleExpanded.bind(this), false); - - this.bodyElement = document.createElement("div"); - this.bodyElement.className = "body"; - - this.element.appendChild(this.titleElement); - this.element.appendChild(this.bodyElement); - - this.title = title; - this.growbarVisible = false; - this.expanded = false; -} - -WebInspector.SidebarPane.prototype = { - get title() - { - return this._title; - }, - - set title(x) - { - if (this._title === x) - return; - this._title = x; - this.titleElement.textContent = x; - }, - - get growbarVisible() - { - return this._growbarVisible; - }, - - set growbarVisible(x) - { - if (this._growbarVisible === x) - return; - - this._growbarVisible = x; - - if (x && !this._growbarElement) { - this._growbarElement = document.createElement("div"); - this._growbarElement.className = "growbar"; - this.element.appendChild(this._growbarElement); - } else if (!x && this._growbarElement) { - if (this._growbarElement.parentNode) - this._growbarElement.parentNode(this._growbarElement); - delete this._growbarElement; - } - }, - - get expanded() - { - return this._expanded; - }, - - set expanded(x) - { - if (x) - this.expand(); - else - this.collapse(); - }, - - expand: function() - { - if (this._expanded) - return; - this._expanded = true; - this.element.addStyleClass("expanded"); - if (this.onexpand) - this.onexpand(this); - }, - - collapse: function() - { - if (!this._expanded) - return; - this._expanded = false; - this.element.removeStyleClass("expanded"); - if (this.oncollapse) - this.oncollapse(this); - }, - - toggleExpanded: function() - { - this.expanded = !this.expanded; - } -} diff --git a/WebCore/page/inspector/SourcePanel.js b/WebCore/page/inspector/SourcePanel.js deleted file mode 100644 index 9d4139c..0000000 --- a/WebCore/page/inspector/SourcePanel.js +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.SourcePanel = function(resource, views) -{ - var allViews = [{ title: WebInspector.UIString("Source"), name: "source" }]; - if (views) - allViews = allViews.concat(views); - - WebInspector.ResourcePanel.call(this, resource, allViews); - - this.currentView = this.views.source; - - var sourceView = this.views.source; - - sourceView.messages = []; - sourceView.frameNeedsSetup = true; - - sourceView.frameElement = document.createElement("iframe"); - sourceView.frameElement.setAttribute("viewsource", "true"); - sourceView.contentElement.appendChild(sourceView.frameElement); -} - -WebInspector.SourcePanel.prototype = { - show: function() - { - WebInspector.ResourcePanel.prototype.show.call(this); - this.setupSourceFrameIfNeeded(); - }, - - setupSourceFrameIfNeeded: function() - { - if (this.views.source.frameNeedsSetup) { - this.attach(); - - InspectorController.addSourceToFrame(this.resource.identifier, this.views.source.frameElement); - WebInspector.addMainEventListeners(this.views.source.frameElement.contentDocument); - - var length = this.views.source.messages; - for (var i = 0; i < length; ++i) - this._addMessageToSource(this.views.source.messages[i]); - - delete this.views.source.frameNeedsSetup; - } - }, - - sourceRow: function(lineNumber) - { - this.setupSourceFrameIfNeeded(); - - var doc = this.views.source.frameElement.contentDocument; - var rows = doc.getElementsByTagName("table")[0].rows; - - // Line numbers are a 1-based index, but the rows collection is 0-based. - --lineNumber; - if (lineNumber >= rows.length) - lineNumber = rows.length - 1; - - return rows[lineNumber]; - }, - - showSourceLine: function(lineNumber) - { - var row = this.sourceRow(lineNumber); - if (!row) - return; - this.currentView = this.views.source; - row.scrollIntoView(true); - }, - - addMessageToSource: function(msg) - { - this.views.source.messages.push(msg); - if (!this.views.source.frameNeedsSetup) - this._addMessageToSource(msg); - }, - - _addMessageToSource: function(msg) - { - var row = this.sourceRow(msg.line); - if (!row) - return; - - var doc = this.views.source.frameElement.contentDocument; - var cell = row.getElementsByTagName("td")[1]; - - var errorDiv = cell.lastChild; - if (!errorDiv || errorDiv.nodeName.toLowerCase() !== "div" || !errorDiv.hasStyleClass("webkit-html-message-bubble")) { - errorDiv = doc.createElement("div"); - errorDiv.className = "webkit-html-message-bubble"; - cell.appendChild(errorDiv); - } - - var imageURL; - switch (msg.level) { - case WebInspector.ConsoleMessage.MessageLevel.Error: - errorDiv.addStyleClass("webkit-html-error-message"); - imageURL = "Images/errorIcon.png"; - break; - case WebInspector.ConsoleMessage.MessageLevel.Warning: - errorDiv.addStyleClass("webkit-html-warning-message"); - imageURL = "Images/warningIcon.png"; - break; - } - - var lineDiv = doc.createElement("div"); - lineDiv.className = "webkit-html-message-line"; - errorDiv.appendChild(lineDiv); - - var image = doc.createElement("img"); - image.src = imageURL; - image.className = "webkit-html-message-icon"; - lineDiv.appendChild(image); - - lineDiv.appendChild(doc.createTextNode(msg.message)); - } -} - -WebInspector.SourcePanel.prototype.__proto__ = WebInspector.ResourcePanel.prototype; diff --git a/WebCore/page/inspector/StylesSidebarPane.js b/WebCore/page/inspector/StylesSidebarPane.js deleted file mode 100644 index 915fc1a..0000000 --- a/WebCore/page/inspector/StylesSidebarPane.js +++ /dev/null @@ -1,682 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -WebInspector.StylesSidebarPane = function() -{ - WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles")); -} - -WebInspector.StylesSidebarPane.prototype = { - update: function(node, editedSection) - { - var refresh = false; - - if (!node || node === this.node) - refresh = true; - - if (node && node.nodeType === Node.TEXT_NODE && node.parentNode) - node = node.parentNode; - - if (node && node.nodeType !== Node.ELEMENT_NODE) - node = null; - - if (node) - this.node = node; - else - node = this.node; - - var body = this.bodyElement; - if (!refresh || !node) { - body.removeChildren(); - this.sections = []; - } - - if (!node) - return; - - var styleRules = []; - - if (refresh) { - for (var i = 0; i < this.sections.length; ++i) { - var section = this.sections[i]; - if (section.computedStyle) - section.styleRule.style = node.ownerDocument.defaultView.getComputedStyle(node); - var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle }; - styleRules.push(styleRule); - } - } else { - var computedStyle = node.ownerDocument.defaultView.getComputedStyle(node); - styleRules.push({ computedStyle: true, selectorText: WebInspector.UIString("Computed Style"), style: computedStyle, editable: false }); - - var nodeName = node.nodeName.toLowerCase(); - for (var i = 0; i < node.attributes.length; ++i) { - var attr = node.attributes[i]; - if (attr.style) { - var attrStyle = { style: attr.style, editable: false }; - attrStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", attr.name); - attrStyle.selectorText = nodeName + "[" + attr.name; - if (attr.value.length) - attrStyle.selectorText += "=" + attr.value; - attrStyle.selectorText += "]"; - styleRules.push(attrStyle); - } - } - - if (node.style && node.style.length) { - var inlineStyle = { selectorText: WebInspector.UIString("Inline Style Attribute"), style: node.style }; - inlineStyle.subtitle = WebInspector.UIString("element’s “%s” attribute", "style"); - styleRules.push(inlineStyle); - } - - var matchedStyleRules = node.ownerDocument.defaultView.getMatchedCSSRules(node, "", !Preferences.showUserAgentStyles); - if (matchedStyleRules) { - // Add rules in reverse order to match the cascade order. - for (var i = (matchedStyleRules.length - 1); i >= 0; --i) - styleRules.push(matchedStyleRules[i]); - } - } - - var usedProperties = {}; - var priorityUsed = false; - - // Walk the style rules and make a list of all used and overloaded properties. - for (var i = 0; i < styleRules.length; ++i) { - var styleRule = styleRules[i]; - if (styleRule.computedStyle) - continue; - - styleRule.usedProperties = {}; - - var style = styleRule.style; - for (var j = 0; j < style.length; ++j) { - var name = style[j]; - - if (!priorityUsed && style.getPropertyPriority(name).length) - priorityUsed = true; - - // If the property name is already used by another rule then this rule's - // property is overloaded, so don't add it to the rule's usedProperties. - if (!(name in usedProperties)) - styleRule.usedProperties[name] = true; - - if (name === "font") { - // The font property is not reported as a shorthand. Report finding the individual - // properties so they are visible in computed style. - // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15598 is fixed. - styleRule.usedProperties["font-family"] = true; - styleRule.usedProperties["font-size"] = true; - styleRule.usedProperties["font-style"] = true; - styleRule.usedProperties["font-variant"] = true; - styleRule.usedProperties["font-weight"] = true; - styleRule.usedProperties["line-height"] = true; - } - } - - // Add all the properties found in this style to the used properties list. - // Do this here so only future rules are affect by properties used in this rule. - for (var name in styleRules[i].usedProperties) - usedProperties[name] = true; - } - - if (priorityUsed) { - // Walk the properties again and account for !important. - var foundPriorityProperties = []; - - // Walk in reverse to match the order !important overrides. - for (var i = (styleRules.length - 1); i >= 0; --i) { - if (styleRules[i].computedStyle) - continue; - - var style = styleRules[i].style; - var uniqueProperties = style.getUniqueProperties(); - for (var j = 0; j < uniqueProperties.length; ++j) { - var name = uniqueProperties[j]; - if (style.getPropertyPriority(name).length) { - if (!(name in foundPriorityProperties)) - styleRules[i].usedProperties[name] = true; - else - delete styleRules[i].usedProperties[name]; - foundPriorityProperties[name] = true; - } else if (name in foundPriorityProperties) - delete styleRules[i].usedProperties[name]; - } - } - } - - if (refresh) { - // Walk the style rules and update the sections with new overloaded and used properties. - for (var i = 0; i < styleRules.length; ++i) { - var styleRule = styleRules[i]; - var section = styleRule.section; - section._usedProperties = (styleRule.usedProperties || usedProperties); - section.update((section === editedSection) || styleRule.computedStyle); - } - } else { - // Make a property section for each style rule. - for (var i = 0; i < styleRules.length; ++i) { - var styleRule = styleRules[i]; - var subtitle = styleRule.subtitle; - delete styleRule.subtitle; - - var computedStyle = styleRule.computedStyle; - delete styleRule.computedStyle; - - var ruleUsedProperties = styleRule.usedProperties; - delete styleRule.usedProperties; - - var editable = styleRule.editable; - delete styleRule.editable; - - // Default editable to true if it was omitted. - if (typeof editable === "undefined") - editable = true; - - var section = new WebInspector.StylePropertiesSection(styleRule, subtitle, computedStyle, (ruleUsedProperties || usedProperties), editable); - section.expanded = true; - section.pane = this; - - body.appendChild(section.element); - this.sections.push(section); - } - } - } -} - -WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype; - -WebInspector.StylePropertiesSection = function(styleRule, subtitle, computedStyle, usedProperties, editable) -{ - WebInspector.PropertiesSection.call(this, styleRule.selectorText); - - this.styleRule = styleRule; - this.computedStyle = computedStyle; - this.editable = (editable && !computedStyle); - - // Prevent editing the user agent rules. - if (this.styleRule.parentStyleSheet && !this.styleRule.parentStyleSheet.ownerNode) - this.editable = false; - - this._usedProperties = usedProperties; - - if (computedStyle) { - if (Preferences.showInheritedComputedStyleProperties) - this.element.addStyleClass("show-inherited"); - - var showInheritedLabel = document.createElement("label"); - var showInheritedInput = document.createElement("input"); - showInheritedInput.type = "checkbox"; - showInheritedInput.checked = Preferences.showInheritedComputedStyleProperties; - - var computedStyleSection = this; - var showInheritedToggleFunction = function(event) { - Preferences.showInheritedComputedStyleProperties = showInheritedInput.checked; - if (Preferences.showInheritedComputedStyleProperties) - computedStyleSection.element.addStyleClass("show-inherited"); - else - computedStyleSection.element.removeStyleClass("show-inherited"); - event.stopPropagation(); - }; - - showInheritedLabel.addEventListener("click", showInheritedToggleFunction, false); - - showInheritedLabel.appendChild(showInheritedInput); - showInheritedLabel.appendChild(document.createTextNode(WebInspector.UIString("Show inherited properties"))); - this.subtitleElement.appendChild(showInheritedLabel); - } else { - if (!subtitle) { - if (this.styleRule.parentStyleSheet && this.styleRule.parentStyleSheet.href) { - var url = this.styleRule.parentStyleSheet.href; - subtitle = WebInspector.linkifyURL(url, url.trimURL(WebInspector.mainResource.domain).escapeHTML()); - this.subtitleElement.addStyleClass("file"); - } else if (this.styleRule.parentStyleSheet && !this.styleRule.parentStyleSheet.ownerNode) - subtitle = WebInspector.UIString("user agent stylesheet"); - else - subtitle = WebInspector.UIString("inline stylesheet"); - } - - this.subtitle = subtitle; - } -} - -WebInspector.StylePropertiesSection.prototype = { - get usedProperties() - { - return this._usedProperties || {}; - }, - - set usedProperties(x) - { - this._usedProperties = x; - this.update(); - }, - - isPropertyInherited: function(property) - { - if (!this.computedStyle || !this._usedProperties) - return false; - // These properties should always show for Computed Style. - var alwaysShowComputedProperties = { "display": true, "height": true, "width": true }; - return !(property in this.usedProperties) && !(property in alwaysShowComputedProperties); - }, - - isPropertyOverloaded: function(property, shorthand) - { - if (this.computedStyle || !this._usedProperties) - return false; - - var used = (property in this.usedProperties); - if (used || !shorthand) - return !used; - - // Find out if any of the individual longhand properties of the shorthand - // are used, if none are then the shorthand is overloaded too. - var longhandProperties = this.styleRule.style.getLonghandProperties(property); - for (var j = 0; j < longhandProperties.length; ++j) { - var individualProperty = longhandProperties[j]; - if (individualProperty in this.usedProperties) - return false; - } - - return true; - }, - - update: function(full) - { - if (full || this.computedStyle) { - this.propertiesTreeOutline.removeChildren(); - this.populated = false; - } else { - var child = this.propertiesTreeOutline.children[0]; - while (child) { - child.overloaded = this.isPropertyOverloaded(child.name, child.shorthand); - child = child.traverseNextTreeElement(false, null, true); - } - } - }, - - onpopulate: function() - { - var style = this.styleRule.style; - if (!style.length) - return; - - var foundShorthands = {}; - var uniqueProperties = style.getUniqueProperties(); - uniqueProperties.sort(); - - for (var i = 0; i < uniqueProperties.length; ++i) { - var name = uniqueProperties[i]; - var shorthand = style.getPropertyShorthand(name); - - if (shorthand && shorthand in foundShorthands) - continue; - - if (shorthand) { - foundShorthands[shorthand] = true; - name = shorthand; - } - - var isShorthand = (shorthand ? true : false); - var inherited = this.isPropertyInherited(name); - var overloaded = this.isPropertyOverloaded(name, isShorthand); - - var item = new WebInspector.StylePropertyTreeElement(style, name, isShorthand, inherited, overloaded); - this.propertiesTreeOutline.appendChild(item); - } - } -} - -WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype; - -WebInspector.StylePropertyTreeElement = function(style, name, shorthand, inherited, overloaded) -{ - this.style = style; - this.name = name; - this.shorthand = shorthand; - this._inherited = inherited; - this._overloaded = overloaded; - - // Pass an empty title, the title gets made later in onattach. - TreeElement.call(this, "", null, shorthand); -} - -WebInspector.StylePropertyTreeElement.prototype = { - get inherited() - { - return this._inherited; - }, - - set inherited(x) - { - if (x === this._inherited) - return; - this._inherited = x; - this.updateState(); - }, - - get overloaded() - { - return this._overloaded; - }, - - set overloaded(x) - { - if (x === this._overloaded) - return; - this._overloaded = x; - this.updateState(); - }, - - onattach: function() - { - this.updateTitle(); - }, - - updateTitle: function() - { - // "Nicknames" for some common values that are easier to read. - var valueNicknames = { - "rgb(0, 0, 0)": "black", - "#000": "black", - "#000000": "black", - "rgb(255, 255, 255)": "white", - "#fff": "white", - "#ffffff": "white", - "#FFF": "white", - "#FFFFFF": "white", - "rgba(0, 0, 0, 0)": "transparent", - "rgb(255, 0, 0)": "red", - "rgb(0, 255, 0)": "lime", - "rgb(0, 0, 255)": "blue", - "rgb(255, 255, 0)": "yellow", - "rgb(255, 0, 255)": "magenta", - "rgb(0, 255, 255)": "cyan" - }; - - var priority = (this.shorthand ? this.style.getShorthandPriority(this.name) : this.style.getPropertyPriority(this.name)); - var value = (this.shorthand ? this.style.getShorthandValue(this.name) : this.style.getPropertyValue(this.name)); - var htmlValue = value; - - if (priority && !priority.length) - delete priority; - if (priority) - priority = "!" + priority; - - if (value) { - var urls = value.match(/url\([^)]+\)/); - if (urls) { - for (var i = 0; i < urls.length; ++i) { - var url = urls[i].substring(4, urls[i].length - 1); - htmlValue = htmlValue.replace(urls[i], "url(" + WebInspector.linkifyURL(url) + ")"); - } - } else { - if (value in valueNicknames) - htmlValue = valueNicknames[value]; - htmlValue = htmlValue.escapeHTML(); - } - } else - htmlValue = value = ""; - - this.updateState(); - - var nameElement = document.createElement("span"); - nameElement.className = "name"; - nameElement.textContent = this.name; - - var valueElement = document.createElement("span"); - valueElement.className = "value"; - valueElement.innerHTML = htmlValue; - - if (priority) { - var priorityElement = document.createElement("span"); - priorityElement.className = "priority"; - priorityElement.textContent = priority; - } - - this.listItemElement.removeChildren(); - - this.listItemElement.appendChild(nameElement); - this.listItemElement.appendChild(document.createTextNode(": ")); - this.listItemElement.appendChild(valueElement); - - if (priorityElement) { - this.listItemElement.appendChild(document.createTextNode(" ")); - this.listItemElement.appendChild(priorityElement); - } - - this.listItemElement.appendChild(document.createTextNode(";")); - - if (value) { - // FIXME: this dosen't catch keyword based colors like black and white - var colors = value.match(/((rgb|hsl)a?\([^)]+\))|(#[0-9a-fA-F]{6})|(#[0-9a-fA-F]{3})/g); - if (colors) { - var colorsLength = colors.length; - for (var i = 0; i < colorsLength; ++i) { - var swatchElement = document.createElement("span"); - swatchElement.className = "swatch"; - swatchElement.style.setProperty("background-color", colors[i]); - this.listItemElement.appendChild(swatchElement); - } - } - } - - this.tooltip = this.name + ": " + (valueNicknames[value] || value) + (priority ? " " + priority : ""); - }, - - updateState: function() - { - if (!this.listItemElement) - return; - - var value = (this.shorthand ? this.style.getShorthandValue(this.name) : this.style.getPropertyValue(this.name)); - if (this.style.isPropertyImplicit(this.name) || value === "initial") - this.listItemElement.addStyleClass("implicit"); - else - this.listItemElement.removeStyleClass("implicit"); - - if (this.inherited) - this.listItemElement.addStyleClass("inherited"); - else - this.listItemElement.removeStyleClass("inherited"); - - if (this.overloaded) - this.listItemElement.addStyleClass("overloaded"); - else - this.listItemElement.removeStyleClass("overloaded"); - }, - - onpopulate: function() - { - // Only populate once and if this property is a shorthand. - if (this.children.length || !this.shorthand) - return; - - var longhandProperties = this.style.getLonghandProperties(this.name); - for (var i = 0; i < longhandProperties.length; ++i) { - var name = longhandProperties[i]; - - if (this.treeOutline.section) { - var inherited = this.treeOutline.section.isPropertyInherited(name); - var overloaded = this.treeOutline.section.isPropertyOverloaded(name); - } - - var item = new WebInspector.StylePropertyTreeElement(this.style, name, false, inherited, overloaded); - this.appendChild(item); - } - }, - - ondblclick: function(element, event) - { - this.startEditing(event.target); - }, - - startEditing: function(selectElement) - { - // FIXME: we don't allow editing of longhand properties under a shorthand right now. - if (this.parent.shorthand) - return; - - if (this.editing || (this.treeOutline.section && !this.treeOutline.section.editable)) - return; - - this.editing = true; - this.previousTextContent = this.listItemElement.textContent; - - this.listItemElement.addStyleClass("focusable"); - this.listItemElement.addStyleClass("editing"); - this.wasExpanded = this.expanded; - this.collapse(); - // Lie about out children to prevent toggling on click. - this.hasChildren = false; - - if (!selectElement) - selectElement = this.listItemElement; - - window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1); - - var treeElement = this; - this.listItemElement.blurred = function() { treeElement.commitEditing() }; - this.listItemElement.handleKeyEvent = function(event) { - if (event.keyIdentifier === "Enter") { - treeElement.commitEditing(); - event.preventDefault(); - } else if (event.keyCode === 27) { // Escape key - treeElement.cancelEditing(); - event.preventDefault(); - } - }; - - this.previousFocusElement = WebInspector.currentFocusElement; - WebInspector.currentFocusElement = this.listItemElement; - }, - - endEditing: function() - { - // Revert the changes done in startEditing(). - delete this.listItemElement.blurred; - delete this.listItemElement.handleKeyEvent; - - WebInspector.currentFocusElement = this.previousFocusElement; - delete this.previousFocusElement; - - delete this.previousTextContent; - delete this.editing; - - this.listItemElement.removeStyleClass("focusable"); - this.listItemElement.removeStyleClass("editing"); - this.hasChildren = (this.children.length ? true : false); - if (this.wasExpanded) { - delete this.wasExpanded; - this.expand(); - } - }, - - cancelEditing: function() - { - this.endEditing(); - this.updateTitle(); - }, - - commitEditing: function() - { - var previousContent = this.previousTextContent; - - this.endEditing(); - - var userInput = this.listItemElement.textContent; - if (userInput === previousContent) - return; // nothing changed, so do nothing else - - var userInputLength = userInput.trimWhitespace().length; - - // Create a new element to parse the user input CSS. - var parseElement = document.createElement("span"); - parseElement.setAttribute("style", userInput); - - var userInputStyle = parseElement.style; - if (userInputStyle.length || !userInputLength) { - // The input was parsable or the user deleted everything, so remove the - // original property from the real style declaration. If this represents - // a shorthand remove all the longhand properties. - if (this.shorthand) { - var longhandProperties = this.style.getLonghandProperties(this.name); - for (var i = 0; i < longhandProperties.length; ++i) - this.style.removeProperty(longhandProperties[i]); - } else - this.style.removeProperty(this.name); - } - - if (!userInputLength) { - // The user deleted the everything, so remove the tree element and update. - if (this.treeOutline.section && this.treeOutline.section.pane) - this.treeOutline.section.pane.update(); - this.parent.removeChild(this); - return; - } - - if (!userInputStyle.length) { - // The user typed something, but it didn't parse. Just abort and restore - // the original title for this property. - this.updateTitle(); - return; - } - - // Iterate of the properties on the test element's style declaration and - // add them to the real style declaration. We take care to move shorthands. - var foundShorthands = {}; - var uniqueProperties = userInputStyle.getUniqueProperties(); - for (var i = 0; i < uniqueProperties.length; ++i) { - var name = uniqueProperties[i]; - var shorthand = userInputStyle.getPropertyShorthand(name); - - if (shorthand && shorthand in foundShorthands) - continue; - - if (shorthand) { - var value = userInputStyle.getShorthandValue(shorthand); - var priority = userInputStyle.getShorthandPriority(shorthand); - foundShorthands[shorthand] = true; - } else { - var value = userInputStyle.getPropertyValue(name); - var priority = userInputStyle.getPropertyPriority(name); - } - - // Set the property on the real style declaration. - this.style.setProperty((shorthand || name), value, priority); - } - - if (this.treeOutline.section && this.treeOutline.section.pane) - this.treeOutline.section.pane.update(null, this.treeOutline.section); - else if (this.treeOutline.section) - this.treeOutline.section.update(true); - else - this.updateTitle(); // FIXME: this will not show new properties. But we don't hit his case yet. - } -} - -WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototype; diff --git a/WebCore/page/inspector/WebKit.qrc b/WebCore/page/inspector/WebKit.qrc deleted file mode 100644 index 0a5e164..0000000 --- a/WebCore/page/inspector/WebKit.qrc +++ /dev/null @@ -1,137 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource prefix="/webkit/inspector"> - <file>ConsolePanel.js</file> - <file>NetworkPanel.js</file> - <file>Resource.js</file> - <file>ResourceCategory.js</file> - <file>ResourcePanel.js</file> - <file>inspector.css</file> - <file>inspector.html</file> - <file>inspector.js</file> - <file>treeoutline.js</file> - <file>utilities.js</file> - <file>Images/alternateTableRows.png</file> - <file>Images/attachedShadow.png</file> - <file>Images/backNormal.png</file> - <file>Images/bottomShadow.png</file> - <file>Images/breadcrumbBackground.png</file> - <file>Images/checker.png</file> - <file>Images/console.png</file> - <file>Images/darkShadow.png</file> - <file>Images/database.png</file> - <file>Images/databaseBrowserViewNormal.png</file> - <file>Images/databaseBrowserViewNormalSelected.png</file> - <file>Images/databaseBrowserViewSmall.png</file> - <file>Images/databaseBrowserViewSmallSelected.png</file> - <file>Images/databaseQueryViewNormal.png</file> - <file>Images/databaseQueryViewNormalSelected.png</file> - <file>Images/databaseQueryViewSmall.png</file> - <file>Images/databaseQueryViewSmallSelected.png</file> - <file>Images/disclosureDownPressed.png</file> - <file>Images/disclosureRightDown.png</file> - <file>Images/disclosureRightPressed.png</file> - <file>Images/document.png</file> - <file>Images/domViewNormal.png</file> - <file>Images/domViewNormalSelected.png</file> - <file>Images/domViewSmall.png</file> - <file>Images/domViewSmallSelected.png</file> - <file>Images/downTriangle.png</file> - <file>Images/errorIcon.png</file> - <file>Images/errorMediumIcon.png</file> - <file>Images/folder.png</file> - <file>Images/forwardNormal.png</file> - <file>Images/glossyHeader.png</file> - <file>Images/glossyHeaderPressed.png</file> - <file>Images/goArrow.png</file> - <file>Images/gradient.png</file> - <file>Images/gradientHighlight.png</file> - <file>Images/gradientHighlightBottom.png</file> - <file>Images/hideStatusWidget.png</file> - <file>Images/hideStatusWidgetPressed.png</file> - <file>Images/network.png</file> - <file>Images/paneBottomGrow.png</file> - <file>Images/paneBottomGrowActive.png</file> - <file>Images/paneGrowHandleLine.png</file> - <file>Images/paneHeader.png</file> - <file>Images/paneHeaderActive.png</file> - <file>Images/plainDocument.png</file> - <file>Images/popupArrows.png</file> - <file>Images/popupArrowsBlack.png</file> - <file>Images/reload.png</file> - <file>Images/rightTriangle.png</file> - <file>Images/segment.png</file> - <file>Images/segmentEnd.png</file> - <file>Images/segmentHover.png</file> - <file>Images/segmentHoverEnd.png</file> - <file>Images/segmentSelected.png</file> - <file>Images/segmentSelectedEnd.png</file> - <file>Images/showStatusWidget.png</file> - <file>Images/showStatusWidgetPressed.png</file> - <file>Images/sidbarItemBackground.png</file> - <file>Images/sidebarActionWidget.png</file> - <file>Images/sidebarActionWidgetPressed.png</file> - <file>Images/sidebarAttachWidget.png</file> - <file>Images/sidebarAttachWidgetPressed.png</file> - <file>Images/sidebarDetachWidget.png</file> - <file>Images/sidebarDetachWidgetPressed.png</file> - <file>Images/sidebarResizeWidget.png</file> - <file>Images/sidebarSelection.png</file> - <file>Images/sidebarSelectionBlurred.png</file> - <file>Images/sidebarSelectionBlurredTall.png</file> - <file>Images/sidebarSelectionGray.png</file> - <file>Images/sidebarSelectionGrayTall.png</file> - <file>Images/sidebarSelectionTall.png</file> - <file>Images/sidebarStatusAreaBackground.png</file> - <file>Images/sourceViewNormal.png</file> - <file>Images/sourceViewNormalSelected.png</file> - <file>Images/sourceViewSmall.png</file> - <file>Images/sourceViewSmallSelected.png</file> - <file>Images/splitviewDimple.png</file> - <file>Images/splitviewDividerBackground.png</file> - <file>Images/tab.png</file> - <file>Images/tabSelected.png</file> - <file>Images/timelinePillBlue.png</file> - <file>Images/timelinePillGray.png</file> - <file>Images/timelinePillGreen.png</file> - <file>Images/timelinePillOrange.png</file> - <file>Images/timelinePillPurple.png</file> - <file>Images/timelinePillRed.png</file> - <file>Images/timelinePillYellow.png</file> - <file>Images/tipBalloon.png</file> - <file>Images/tipBalloonBottom.png</file> - <file>Images/tipIcon.png</file> - <file>Images/tipIconPressed.png</file> - <file>Images/toggleDown.png</file> - <file>Images/toggleUp.png</file> - <file>Images/toolbarBackground.png</file> - <file>Images/toolbarBackgroundInactive.png</file> - <file>Images/toolbarButtonNormal.png</file> - <file>Images/toolbarButtonNormalInactive.png</file> - <file>Images/toolbarButtonNormalPressed.png</file> - <file>Images/toolbarButtonNormalSelected.png</file> - <file>Images/toolbarButtonNormalSelectedInactive.png</file> - <file>Images/toolbarButtonSmall.png</file> - <file>Images/toolbarButtonSmallInactive.png</file> - <file>Images/toolbarButtonSmallPressed.png</file> - <file>Images/toolbarButtonSmallSelected.png</file> - <file>Images/toolbarButtonSmallSelectedInactive.png</file> - <file>Images/toolbarPopupButtonNormal.png</file> - <file>Images/toolbarPopupButtonNormalInactive.png</file> - <file>Images/toolbarPopupButtonNormalPressed.png</file> - <file>Images/toolbarPopupButtonSmall.png</file> - <file>Images/toolbarPopupButtonSmallInactive.png</file> - <file>Images/toolbarPopupButtonSmallPressed.png</file> - <file>Images/toolbarSplitButtonDividerNormal.png</file> - <file>Images/toolbarSplitButtonDividerNormalInactive.png</file> - <file>Images/toolbarSplitButtonDividerSmall.png</file> - <file>Images/toolbarSplitButtonDividerSmallInactive.png</file> - <file>Images/treeDownTriangleBlack.png</file> - <file>Images/treeDownTriangleWhite.png</file> - <file>Images/treeLeftTriangleBlack.png</file> - <file>Images/treeRightTriangleBlack.png</file> - <file>Images/treeRightTriangleWhite.png</file> - <file>Images/warningIcon.png</file> - <file>Images/warningMediumIcon.png</file> - <file>Images/warningsErrors.png</file> -</qresource> -</RCC> diff --git a/WebCore/page/inspector/inspector.css b/WebCore/page/inspector/inspector.css deleted file mode 100644 index 2ba3208..0000000 --- a/WebCore/page/inspector/inspector.css +++ /dev/null @@ -1,2169 +0,0 @@ -/* - * Copyright (C) 2006, 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -body { - -webkit-user-select: none; - cursor: default; - height: 100%; - width: 100%; - overflow: hidden; - font-family: Lucida Grande, sans-serif; - margin: 0; - -webkit-text-size-adjust: none; -} - -iframe, a img { - border: none; -} - -img { - -webkit-user-drag: none; -} - -.focused .selected { - background-color: rgb(56, 121, 217); -} - -.blurred .selected, body.inactive .selected { - background-color: rgb(212, 212, 212); -} - -.hidden { - display: none; -} - -#toolbar { - position: absolute; - top: 0; - left: 0; - right: 0; - height: 32px; - background-color: rgb(245, 245, 250); - background-image: url(Images/toolbarBackground.png); - background-repeat: repeat-x; - background-position: top; - border-bottom: 1px solid rgb(80, 80, 80); - padding: 2px 8px; - -webkit-box-sizing: border-box; - -webkit-background-size: auto 135%; -} - -body.detached.platform-mac-leopard #toolbar { - background: transparent !important; -} - -body.inactive #toolbar { - background-image: url(Images/toolbarBackgroundInactive.png); - border-bottom: 1px solid rgb(64%, 64%, 64%); -} - -body.attached #toolbar { - height: 28px; - border-top: 1px solid rgb(80, 80, 80); - background-image: url(Images/darkShadow.png), url(Images/toolbarBackground.png); - background-position: center -2px, top; - -webkit-background-size: auto auto, auto 135%; -} - -body.attached.inactive #toolbar { - background-image: url(Images/darkShadow.png), url(Images/toolbarBackgroundInactive.png); - background-position: center -3px, top; - border-top: 1px solid rgb(100, 100, 100); - border-bottom: 1px solid rgb(64%, 64%, 64%); -} - -#toolbar button, #toolbar button:disabled:active { - border-width: 3px 3px 4px 3px; - border-style: none; - border-color: transparent; - background-color: transparent; - -webkit-border-image: url(Images/toolbarButtonNormal.png) 3 3 4 3; - height: 23px; - -webkit-box-sizing: border-box; - vertical-align: middle; - line-height: 10px; -} - -#toolbar button:focus { - outline: none; -} - -#toolbar button:active { - -webkit-border-image: url(Images/toolbarButtonNormalPressed.png) 3 3 4 3; -} - -#toolbar button.selected { - -webkit-border-image: url(Images/toolbarButtonNormalSelected.png) 3 3 4 3; -} - -body.inactive #toolbar button:active { - -webkit-border-image: url(Images/toolbarButtonNormalPressedInactive.png) 3 3 4 3; -} - -body.inactive #toolbar button.selected { - -webkit-border-image: url(Images/toolbarButtonNormalSelectedInactive.png) 3 3 4 3; -} - -body.inactive #toolbar button, body.inactive #toolbar button:disabled:active { - -webkit-border-image: url(Images/toolbarButtonNormalInactive.png) 3 3 4 3; -} - -body.attached #toolbar button { - height: 19px; - line-height: 7px; - -webkit-border-image: url(Images/toolbarButtonSmall.png) 3 3 4 3; -} - -body.attached #toolbar button:active { - -webkit-border-image: url(Images/toolbarButtonSmallPressed.png) 3 3 4 3; -} - -body.attached #toolbar button.selected { - -webkit-border-image: url(Images/toolbarButtonSmallSelected.png) 3 3 4 3; -} - -body.attached.inactive #toolbar button:active { - -webkit-border-image: url(Images/toolbarButtonSmallPressedInactive.png) 3 3 4 3; -} - -body.attached.inactive #toolbar button.selected { - -webkit-border-image: url(Images/toolbarButtonSmallSelectedInactive.png) 3 3 4 3; -} - -body.attached.inactive #toolbar button, body.inactive #toolbar button:disabled:active { - -webkit-border-image: url(Images/toolbarButtonSmallInactive.png) 3 3 4 3; -} - -#toolbar select, #toolbar select:disabled:active { - background-color: transparent; - border-width: 3px 10px 4px 3px; - border-color: transparent; - -webkit-border-image: url(Images/toolbarPopupButtonNormal.png) 3 10 4 3; - height: 23px; - font-size: 10px; - padding-left: 8px; - padding-right: 6px; - -webkit-box-sizing: border-box; - -webkit-border-radius: 0; - -webkit-appearance: none; - vertical-align: middle; -} - -#toolbar select:focus { - outline: none; -} - -#toolbar select:active { - -webkit-border-image: url(Images/toolbarPopupButtonNormalPressed.png) 3 10 4 3; -} - -body.inactive #toolbar select:active { - -webkit-border-image: url(Images/toolbarPopupButtonNormalPressedInactive.png) 3 10 4 3; -} - -body.inactive #toolbar select, body.inactive #toolbar select:disabled:active { - -webkit-border-image: url(Images/toolbarPopupButtonNormalInactive.png) 3 10 4 3; -} - -body.attached #toolbar select, #toolbar select:disabled:active { - -webkit-border-image: url(Images/toolbarPopupButtonSmall.png) 3 10 4 3; - height: 19px; -} - -body.attached #toolbar select:active { - -webkit-border-image: url(Images/toolbarPopupButtonSmallPressed.png) 3 10 4 3; -} - -body.attached.inactive #toolbar select:active { - -webkit-border-image: url(Images/toolbarPopupButtonSmallPressedInactive.png) 3 10 4 3; -} - -body.attached.inactive #toolbar select, body.inactive #toolbar select:disabled:active { - -webkit-border-image: url(Images/toolbarPopupButtonSmallInactive.png) 3 10 4 3; -} - -#toolbar .split-button-divider { - width: 1px; - height: 23px; - content: url(Images/toolbarSplitButtonDividerNormal.png); - vertical-align: middle; -} - -body.inactive #toolbar .split-button-divider { - content: url(Images/toolbarSplitButtonDividerNormalInactive.png); -} - -body.attached #toolbar .split-button-divider { - height: 19px; - content: url(Images/toolbarSplitButtonDividerSmall.png); -} - -body.attached.inactive #toolbar .split-button-divider { - content: url(Images/toolbarSplitButtonDividerSmallInactive.png); -} - -#toolbar .split-button { - padding: 0; - width: 26px; -} - -body.attached #toolbar .split-button { - width: 20px; -} - -#toolbar .split-button.middle { - border-left: transparent none 0 !important; - border-right: transparent none 0 !important; -} - -#toolbar .split-button.first { - border-right: transparent none 0 !important; -} - -#toolbar .split-button.last { - border-left: transparent none 0 !important; -} - -#back img { - content: url(Images/backNormal.png); - vertical-align: middle; - margin-top: -1px; - width: 8px; - height: 10px; -} - -body.attached #back img { - content: url(Images/treeLeftTriangleBlack.png); - margin-top: -1px; - margin-left: -1px; - width: 8px; - height: 8px; -} - -#back:disabled img, #forward:disabled img { - opacity: 0.45; -} - -#forward img { - content: url(Images/forwardNormal.png); - vertical-align: middle; - margin-top: -1px; - margin-left: 1px; - width: 8px; - height: 10px; -} - -body.attached #forward img { - content: url(Images/treeRightTriangleBlack.png); - margin-top: -1px; - width: 8px; - height: 8px; -} - -.view-button-source img { - content: url(Images/sourceViewNormal.png); - vertical-align: middle; - margin-top: 1px; - margin-left: -1px; - width: 11px; - height: 11px; -} - -.view-button-source.selected img { - content: url(Images/sourceViewNormalSelected.png); -} - -body.attached .view-button-source img { - content: url(Images/sourceViewSmall.png); - width: 8px; - height: 8px; -} - -body.attached .view-button-source.selected img { - content: url(Images/sourceViewSmallSelected.png); -} - -.view-button-dom img { - content: url(Images/domViewNormal.png); - vertical-align: middle; - margin-top: 1px; - margin-left: 3px; - width: 11px; - height: 11px; -} - -.view-button-dom.selected img { - content: url(Images/domViewNormalSelected.png); -} - -body.attached .view-button-dom img { - content: url(Images/domViewSmall.png); - width: 10px; - height: 8px; -} - -body.attached .view-button-dom.selected img { - content: url(Images/domViewSmallSelected.png); -} - -#toolbarButtons { - position: absolute; - left: 200px; - padding-left: 8px; -} - -#search { - float: right; - width: 210px; - font-size: 16px; -} - -body.attached #search { - font-size: 12px; -} - -#searchResults { - position: absolute; - top: -100px; - left: 0; - right: 0; - height: 100px; - z-index: -1; - background-color: white; - border-bottom: 1px solid rgb(180, 180, 180); - overflow-y: auto; - overflow-x: hidden; - -webkit-box-sizing: border-box; -} - -.search-results-section { - color: gray; - width: 28px; - float: left; - margin-left: -45px; - text-align: right; - font-size: 10px; - margin-top: 1px; - white-space: nowrap; -} - -.selected .search-results-section { - color: rgba(255, 255, 255, 0.8); -} - -body.inactive .focused .selected .search-results-section { - color: rgba(0, 0, 0, 0.5); -} - -.blurred .selected .search-results-section { - color: rgba(0, 0, 0, 0.5); -} - -#searchResults > ol > ol > li { - padding-left: 45px; - white-space: nowrap; -} - -.search-matched-string { - background-color: #ff8; -} - -.selected .search-matched-string { - background-color: transparent; -} - -#sidebar { - position: absolute; - top: 32px; - left: 0; - bottom: 0; - width: 200px; - background-color: rgb(214, 221, 229); - border-right: 1px solid rgb(64%, 64%, 64%); - -webkit-box-sizing: border-box; -} - -body.inactive #sidebar { - background-color: rgb(232, 232, 232); -} - -body.attached #sidebar { - top: 28px; -} - -#statusbar { - position: absolute; - padding: 0; - left: 0; - right: 0; - bottom: 0; - height: 21px; - border-top: 1px solid #bbb; - -webkit-box-sizing: border-box; - background-image: url(Images/sidebarStatusAreaBackground.png); - background-position: right, center; - background-repeat: no-repeat, repeat-x; -} - -#statusbar #sidebarResizeWidget { - display: block; - float: right; - width: 17px; - height: 20px; - background: url(Images/sidebarResizeWidget.png) right no-repeat; - cursor: col-resize; -} - -#statusbar button { - -webkit-apearance: none; - vertical-align: top; - border: 0; - width: 32px; - height: 20px; - margin: 0; - margin-left: -1px; - padding: 0; -} - -#statusbar button:focus { - outline: none; -} - -#statusbar button.action { - background-image: url(Images/sidebarActionWidget.png); -} - -#statusbar button.action:active { - background-image: url(Images/sidebarActionWidgetPressed.png); -} - -body.detached #attachToggle { - background-image: url(Images/sidebarAttachWidget.png); -} - -body.detached #attachToggle:active { - background-image: url(Images/sidebarAttachWidgetPressed.png); -} - -body.attached #attachToggle { - background-image: url(Images/sidebarDetachWidget.png); -} - -body.attached #attachToggle:active { - background-image: url(Images/sidebarDetachWidgetPressed.png); -} - -#status { - overflow: hidden; - position: absolute; - bottom: 21px; - left: 0; - width: 100%; - height: 78px; - padding: 2px 0; - margin: 0; - border-top: 1px solid rgb(64%, 64%, 64%); - -webkit-box-sizing: border-box; - list-style: none; - font-size: 11px; - -webkit-transition: bottom 250ms ease-in-out; -} - -#status li { - position: relative; - height: 37px; - -webkit-box-sizing: border-box; -} - -#status li.selected { - background-image: url(Images/sidebarSelectionTall.png); - background-repeat: repeat-x; - background-position: center; - background-color: transparent !important; - color: white; - font-weight: bold; - text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0; -} - -#status .icon { - position: absolute; - top: 2px; - left: 15px; - width: 32px; - height: 32px; - background-repeat: no-repeat; - background-position: center center; -} - -#status .icon.console { - background-image: url(Images/console.png); -} - -#status .icon.network { - background-image: url(Images/network.png); -} - -#status .title { - -webkit-box-sizing: border-box; - position: relative; - top: 5px; - padding-left: 58px; - right: 5px; - display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -#status .title.only { - top: 10px; -} - -#status .info { - -webkit-box-sizing: border-box; - position: relative; - margin-top: 6px; - padding-left: 58px; - right: 5px; - display: block; - font-size: 9px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -#list { - overflow-x: hidden; - overflow-y: auto; - position: absolute; - top: 0; - left: 0; - bottom: 99px; - width: 100%; - padding: 2px 0; - margin: 0; - -webkit-box-sizing: border-box; - list-style: none; - font-size: 11px; - -webkit-transition: bottom 250ms ease-in-out; -} - -#list > li { - height: 26px; - color: rgb(96, 110, 128); - text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0; - font-weight: bold; - line-height: 20px; - text-indent: 20px; - background-image: url(Images/rightTriangle.png); - background-repeat: no-repeat; - background-position: 10px 6px; - text-transform: uppercase; -} - -#list > ol + li { - margin-top: 5px; -} - -#list > li + li { - margin-top: 5px; -} - -#list > li.expanded { - background-image: url(Images/downTriangle.png); - background-position: 10px 7px; -} - -#list > ol { - display: none; - list-style: none; - padding: 0; - margin: 0; -} - -#list > ol.expanded { - display: block; -} - -#list > ol > li { - position: relative; - height: 37px; - -webkit-box-sizing: border-box; -} - -#list > ol > li:-webkit-drag { - background: transparent !important; - color: black !important; - font-weight: normal !important; -} - -#sidebar li:-webkit-drag .count { - display: none; -} - -#list .icon { - position: absolute; - top: 2px; - left: 15px; - width: 32px; - height: 32px; - background-image: url(Images/document.png); - background-repeat: no-repeat; - background-position: center center; -} - -#list .icon.database { - background-image: url(Images/database.png); -} - -#list .icon.plain { - background-image: url(Images/plainDocument.png); -} - -#list .icon.font { - background-image: url(Images/plainDocument.png); -} - -#list .icon.font .preview { - overflow: hidden; - text-align: center; - font-size: 14px; - line-height: 14px; - font-weight: normal; - color: black; - text-shadow: none; -} - -#list .icon .preview { - margin: auto; - display: block; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - max-width: 20px; - max-height: 22px; - -webkit-box-sizing: border-box; - border-top: 6px solid transparent; -} - -#list .icon .progress { - margin: auto; - display: block; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - -#list .title { - -webkit-box-sizing: border-box; - position: relative; - top: 5px; - padding-left: 58px; - right: 5px; - display: block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -#list .title.only { - top: 10px; -} - -#sidebar li .count { - float: right; - margin-top: 11px; - margin-right: 6px; - font-family: Helvetica, sans-serif; - font-weight: bold; - font-size: 11px; - line-height: 10px; - -webkit-border-radius: 7px; - color: white; - text-shadow: none; - background-image: url(Images/gradientHighlight.png), url(Images/gradient.png); - -webkit-background-size: auto 100%, auto 100%; - background-position: center; - padding: 2px 4px; - text-align: center; - text-indent: 0; - min-width: 20px; - -webkit-box-sizing: border-box; -} - -#sidebar li .count.warnings { - background-color: orange; -} - -#sidebar li .count.errors { - background-color: red; -} - -#list .info { - -webkit-box-sizing: border-box; - position: relative; - margin-top: 6px; - padding-left: 58px; - right: 5px; - display: block; - font-size: 9px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -#list li.selected { - background-image: url(Images/sidebarSelectionTall.png); - background-repeat: repeat-x; - background-position: center; - background-color: transparent !important; - color: white; - font-weight: bold; - text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0; -} - -#sidebar.blurred li.selected { - background-image: url(Images/sidebarSelectionBlurredTall.png); -} - -body.inactive #sidebar li.selected { - background-image: url(Images/sidebarSelectionGrayTall.png); -} - -#main { - position: absolute; - top: 32px; - left: 200px; - right: 0; - bottom: 0; - overflow: hidden; - background-color: white; - z-index: -100; -} - -body.attached #main { - top: 28px; -} - -#panels { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - overflow: hidden; - z-index: -100; -} - -.panel { - display: none; - overflow: hidden; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} - -.panel.selected { - display: block; - background-color: transparent !important; -} - -.content { - display: none; - -webkit-user-select: text; - cursor: auto; - overflow: none; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; -} - -.content.selected { - display: block; - background-color: transparent !important; -} - -.panel.font { - font-size: 60px; - white-space: pre-wrap; - word-wrap: break-word; - text-align: center; -} - -.panel.font .preview { - position: absolute; - margin-top: auto; - margin-bottom: auto; - top: 0; - left: 0; - right: 0; - bottom: 0; -} - -.panel.image { - position: relative; - width: 100%; - height: 100%; -} - -.panel.image > .image { - position: relative; - -webkit-box-sizing: border-box; - height: 70%; - padding: 20px; -} - -.panel.image > .info { - position: relative; - -webkit-box-sizing: border-box; - height: 30%; - padding-top: 10px; - overflow: auto; - font-size: 11px; -} - -.panel.image img { - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - max-width: 80%; - max-height: 80%; - background-image: url(Images/checker.png); - -webkit-box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.5); -} - -.panel.image .title { - text-align: center; - font-size: 13px; -} - -.panel.image .infoList { - margin: 0; -} - -.panel.image .infoList dt { - font-weight: bold; - display: inline-block; - width: 50%; - text-align: right; -} - -.panel.image .infoList dd { - -webkit-box-sizing: border-box; - display: inline-block; - padding-left: 10px; - width: 50%; - text-align: left; - margin: 0; -} - -.panel.image .infoList dd::after { - white-space: pre; - content: "\A"; -} - -.content.network { -} - -.content.other { - font-family: Monaco, monospace; - font-size: 10px; - white-space: pre-wrap; - padding: 6px; -} - -.content.side { - display: block; - overflow: hidden; - position: absolute; - top: 0; - left: 0; - right: 225px; - bottom: 0; -} - -.content.source iframe { - width: 100%; - height: 100%; -} - -.content.tree { - display: block; - overflow: auto; - padding: 0; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 21px; -} - -.sidebar { - position: absolute; - top: 0; - right: 0; - bottom: 0; - width: 225px; - background-color: rgb(232, 232, 232); - border-left: 1px solid rgb(64%, 64%, 64%); - -webkit-box-sizing: border-box; - -webkit-user-select: none; - cursor: default; - overflow: auto; - padding: 0; -} - -.crumbs { - -webkit-user-select: none; - cursor: default; - -webkit-box-sizing: border-box; - position: absolute; - left: 0; - right: 0; - bottom: 0; - height: 21px; - background-image: url(Images/breadcrumbBackground.png); - background-repeat: repeat-x; - border-top: 1px solid #bbb; - font-size: 11px; - line-height: 19px; - text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0; - color: rgb(20, 20, 20); - overflow: hidden; -} - -.crumbs > div { - position: absolute; -} - -.crumbs .crumb { - -webkit-box-sizing: border-box; - height: 20px; - border-width: 0 11px 0 0; - -webkit-border-image: url(Images/segment.png) 0 11 0 0; - margin-right: -11px; - padding-left: 15px; - padding-right: 2px; - white-space: nowrap; - float: right; -} - -.crumbs .crumb.collapsed > * { - display: none; -} - -.crumbs .crumb.collapsed::before { - content: "\2026"; /* ellipses */ - font-weight: bold; -} - -.crumbs .crumb.compact .extra { - display: none; -} - -.crumbs .crumb.dimmed { - color: rgba(0, 0, 0, 0.45); -} - -.crumbs .crumb.start { - padding-left: 7px; -} - -.crumbs .crumb.end { - border-width: 0 2px 0 0; - padding-right: 6px; - -webkit-border-image: url(Images/segmentEnd.png) 0 2 0 0; -} - -.crumbs .crumb.selected { - -webkit-border-image: url(Images/segmentSelected.png) 0 11 0 0; - background-color: transparent !important; - color: black; -} - -.crumbs .crumb.selected:hover { - -webkit-border-image: url(Images/segmentSelected.png) 0 11 0 0; -} - -.crumbs .crumb.selected.end, .crumbs .crumb.selected.end:hover { - -webkit-border-image: url(Images/segmentSelectedEnd.png) 0 2 0 0; -} - -.crumbs .crumb:hover { - -webkit-border-image: url(Images/segmentHover.png) 0 11 0 0; - color: black; -} - -.crumbs .crumb.dimmed:hover { - -webkit-border-image: url(Images/segmentHover.png) 0 11 0 0; - color: rgba(0, 0, 0, 0.75); -} - -.crumbs .crumb.end:hover { - -webkit-border-image: url(Images/segmentHoverEnd.png) 0 2 0 0; -} - -.outline-disclosure li .selection { - display: none; - position: absolute; - left: 0; - right: 0; - height: 15px; - z-index: -1; -} - -.outline-disclosure li.selected .selection { - display: block; -} - -.content.tree > ol, #searchResults > ol { - position: relative; - padding: 2px 6px !important; - margin: 0; - color: black; - -webkit-user-select: none; - cursor: default; - min-width: 100%; - -webkit-box-sizing: border-box; -} - -.outline-disclosure, .outline-disclosure ol { - list-style-type: none; - font-size: 11px; - -webkit-padding-start: 12px; - margin: 0; -} - -.outline-disclosure li { - padding: 0 0 2px 14px; - -webkit-box-sizing: border-box; - margin-top: 1px; - margin-bottom: 1px; - word-wrap: break-word; - text-indent: -2px -} - -.blurred .outline-disclosure li.selected, body.inactive .outline-disclosure li.selected { - background-color: transparent !important; - color: black; -} - -.outline-disclosure li.selected { - background-color: transparent !important; - color: white; -} - -.outline-disclosure li.parent { - text-indent: -12px -} - -.content.tree li .webkit-html-tag.close { - margin-left: -12px; -} - -.outline-disclosure li.parent::before { - content: url(Images/treeRightTriangleBlack.png); - float: left; - width: 8px; - height: 8px; - margin-top: 1px; - padding-right: 2px; -} - -.blurred .outline-disclosure li.parent.selected::before, body.inactive .outline-disclosure li.parent.selected::before { - content: url(Images/treeRightTriangleBlack.png); -} - -.outline-disclosure li.parent.selected::before { - content: url(Images/treeRightTriangleWhite.png); -} - -.blurred .outline-disclosure li.parent.expanded.selected::before, body.inactive .outline-disclosure li.parent.expanded.selected::before { - content: url(Images/treeDownTriangleBlack.png); -} - -.outline-disclosure li.parent.expanded:before { - content: url(Images/treeDownTriangleBlack.png); -} - -.outline-disclosure li.parent.expanded.selected::before { - content: url(Images/treeDownTriangleWhite.png); -} - -.outline-disclosure ol.children { - display: none; -} - -.outline-disclosure ol.children.expanded { - display: block; -} - -.webkit-html-comment { - /* Keep this in sync with view-source.css (.webkit-html-comment) */ - color: rgb(35, 110, 37); -} - -.webkit-html-tag { - /* Keep this in sync with view-source.css (.webkit-html-tag) */ - color: rgb(136, 18, 128); -} - -.webkit-html-attribute-name { - /* Keep this in sync with view-source.css (.webkit-html-attribute-name) */ - color: rgb(153, 69, 0); -} - -.webkit-html-attribute-value { - /* Keep this in sync with view-source.css (.webkit-html-attribute-value) */ - color: rgb(26, 26, 166); -} - -.webkit-html-external-link, .webkit-html-resource-link { - /* Keep this in sync with view-source.css (.webkit-html-external-link, .webkit-html-resource-link) */ - color: #00e; -} - -.webkit-html-external-link { - /* Keep this in sync with view-source.css (.webkit-html-external-link) */ - text-decoration: none; -} - -.webkit-html-external-link:hover { - /* Keep this in sync with view-source.css (.webkit-html-external-link:hover) */ - text-decoration: underline; -} - -body:not(.inactive) .focused .outline-disclosure li.selected * { - color: inherit; -} - -.section { - display: block; - -webkit-box-shadow: rgba(0, 0, 0, .5) 2px 2px 5px; - -webkit-border-radius: 8px; - background-color: white; - font-size: 11px; - margin-bottom: 8px; -} - -.section .header { - padding: 2px 8px 4px; - border: 2px solid rgba(255, 255, 255, 0.5); - background-color: rgb(214, 221, 229); - background-image: url(Images/gradient.png); - background-repeat: repeat-x; - background-position: bottom; - -webkit-background-size: auto 100%; - -webkit-border-radius: 8px; - text-shadow: rgba(255, 255, 255, 0.75) 0 1px 0; -} - -.section.expanded .header { - border-bottom: 2px ridge rgba(214, 221, 229, 0.5); - -webkit-border-top-right-radius: 8px; - -webkit-border-top-left-radius: 8px; - -webkit-border-bottom-right-radius: 0; - -webkit-border-bottom-left-radius: 0; -} - -.section .header .title { - font-weight: bold; - word-wrap: break-word; -} - -.section .header label { - display: none; -} - -.section.expanded .header label { - display: inline; -} - -.section .header input[type=checkbox] { - height: 1em; - width: 1em; - margin-left: 0; - margin-top: 0; - margin-bottom: 0; - vertical-align: top; -} - -.section .header .subtitle { - margin-top: 2px; - font-size: 10px; - word-wrap: break-word; -} - -.section .header .subtitle a { - color: inherit; -} - -.section .properties { - display: none; - margin: 0; - padding: 2px 6px 5px; - list-style: none; -} - -.section.expanded .properties { - display: block; -} - -.section .properties li { - margin-left: 10px; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - -webkit-user-select: text; - cursor: auto; - outline: none; -} - -.section .properties li.parent { - margin-left: 0; -} - -.section .properties li.selected { - background-color: transparent !important; -} - -.section .properties ol { - display: none; - margin: 0; - -webkit-padding-start: 12px; - list-style: none; -} - -.section .properties ol.expanded { - display: block; -} - -.section .properties li.parent::before { - content: url(Images/treeRightTriangleBlack.png); - opacity: 0.75; - float: left; - width: 8px; - height: 8px; - margin-top: 0; - padding-right: 2px; - -webkit-user-select: none; - cursor: default; -} - -.section .properties li.parent.expanded::before { - content: url(Images/treeDownTriangleBlack.png); - margin-top: 1px; -} - -.section .properties li.editing { - -webkit-box-shadow: rgba(0, 0, 0, .5) 3px 3px 4px; - outline: 1px solid rgb(66%, 66%, 66%); - background-color: white; - -webkit-user-modify: read-write-plaintext-only; - text-overflow: clip; - margin-left: 8px; - padding-left: 2px; - margin-bottom: -1px; - padding-bottom: 1px; - text-decoration: none !important; - opacity: 1.0 !important; -} - -.section .properties li.editing.parent::before { - display: none; -} - -.section .properties li.editing * { - color: black !important; -} - -.section .properties .overloaded { - text-decoration: line-through; -} - -.section .properties .implicit, .section .properties .inherited { - opacity: 0.5; -} - -.section:not(.show-inherited) .properties .inherited { - display: none; -} - -.section .properties .name { - color: rgb(136, 19, 145); -} - -.section .properties .value.dimmed { - color: rgb(100, 100, 100); -} - -.section .properties .number { - color: blue; -} - -.section .properties .priority { - color: rgb(128, 0, 0); -} - -.section .properties .keyword { - color: rgb(136, 19, 79); -} - -.section .properties .color { - color: rgb(118, 15, 21); -} - -.swatch { - display: inline-block; - vertical-align: middle; - margin-left: 4px; - width: 0.75em; - height: 0.75em; - border: 1px solid rgb(180, 180, 180); -} - -.pane { - margin-top: 1px; -} - -.pane > .title { - background-image: url(Images/paneHeader.png); - background-repeat: repeat-x; - background-position: bottom; - -webkit-background-size: auto 100%; - height: 14px; - padding: 0 6px; - border-top: 1px solid rgb(129, 129, 129); - border-bottom: 1px solid rgb(129, 129, 129); - font-weight: bold; - font-size: 11px; - color: rgb(85, 85, 85); -} - -.pane > .title:active { - background-image: url(Images/paneHeaderActive.png); -} - -.pane > .title::before { - content: url(Images/treeRightTriangleBlack.png); - opacity: 0.75; - float: left; - width: 8px; - height: 8px; - margin-right: 3px; - margin-top: 0; -} - -.pane.expanded > .title::before { - margin-top: 1px; - content: url(Images/treeDownTriangleBlack.png); -} - -.pane > .body { - position: relative; - padding: 8px; - display: none; - overflow: auto; -} - -.pane.expanded > .body, .pane.expanded > .growbar { - display: block; -} - -.pane > .growbar { - display: none; - background-image: url(Images/paneGrowHandleLine.png), url(Images/paneBottomGrow.png); - background-repeat: no-repeat, repeat-x; - background-position: center center, bottom; - -webkit-background-size: auto 100%, auto 100%; - height: 5px; -} - -.metrics { - font-size: 10px; - text-align: center; - white-space: nowrap; -} - -.metrics .label { - position: absolute; - margin-top: -10px; - font-size: 9px; - color: grey; - background-color: rgb(232, 232, 232); - margin-left: 3px; - padding-left: 2px; - padding-right: 2px; -} - -.metrics .margin { - border: 1px dashed; - display: inline-block; - -webkit-box-sizing: border-box; - padding: 3px; - margin: 3px; -} - -.metrics .border { - border: 1px black solid; - display: inline-block; - vertical-align: middle; - -webkit-box-sizing: border-box; - padding: 3px; - margin: 3px; -} - -.metrics .padding { - border: 1px grey dashed; - display: inline-block; - vertical-align: middle; - -webkit-box-sizing: border-box; - padding: 3px; - margin: 3px; -} - -.metrics .content { - position: static; - border: 1px grey solid; - display: inline-block; - vertical-align: middle; - -webkit-box-sizing: border-box; - padding: 3px; - margin: 3px; - min-width: 80px; - text-align: center; - overflow: visible; -} - -.metrics .left { - display: inline-block; - text-align: center; - vertical-align: middle; - -webkit-box-sizing: border-box; -} - -.metrics .right { - display: inline-block; - text-align: center; - vertical-align: middle; - -webkit-box-sizing: border-box; -} - -.metrics .top { - text-align: center; -} - -.metrics .bottom { - text-align: center; -} - -.console-message-list { - list-style: none; - margin: 0; - padding: 0; - position: absolute; - top: 0; - bottom: 20px; - left: 0; - right: 0; - overflow: auto; - -webkit-user-select: text; - cursor: auto; -} - -.console-prompt { - font-family: monospace; - font-size: 11px; - margin: 0; - padding: 2px 0 0; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 18px; - resize: none; - outline: none; - border: none; - border-top: 1px solid rgb(64%, 64%, 64%); -} - -.console-message, .console-command { - font-size: 10px; - margin: 0; - padding: 3px 3px 3px 24px; - border-bottom: 1px solid rgb(75%, 75%, 75%); - word-break: break-word; - position: relative; -} - -.console-command a:hover { - text-decoration: underline; - cursor: pointer; -} - -.console-message-message { - font-size: 11px; - white-space: pre-wrap; -} - -.console-message-url { - color: rgb(33%, 33%, 33%); - cursor: pointer; -} - -.console-message-url::after { - content: url(Images/goArrow.png); - margin-left: 3px; - width: 12px; - height: 12px; - vertical-align: middle; - opacity: 0.75; - -webkit-user-select: none; -} - -.console-message-url:hover { - color: rgb(15%, 15%, 15%); -} - -.console-message-url:hover::after { - opacity: 1; -} - -.console-error-level::before { - content: url(Images/errorMediumIcon.png); - position: absolute; - left: 5px; - top: 2px; - -webkit-user-select: none; -} - -.console-warning-level::before { - content: url(Images/warningMediumIcon.png); - position: absolute; - left: 4px; - top: 2px; - -webkit-user-select: none; -} - -.console-command-input, .console-command-output { - font-size: 11px; - white-space: pre-wrap; -} - -.console-command-input::before { - content: ">"; - font-weight: bold; - font-size: 15px; - color: blue; - position: absolute; - left: 8px; - top: 1px; - -webkit-user-select: none; -} - -.view-button-browse img { - content: url(Images/databaseBrowserViewNormal.png); - vertical-align: middle; - margin-top: -1px; - margin-left: 1px; - width: 11px; - height: 11px; -} - -.view-button-browse.selected img { - content: url(Images/databaseBrowserViewNormalSelected.png); -} - -body.attached .view-button-browse img { - content: url(Images/databaseBrowserViewSmall.png); - width: 11px; - height: 8px; - margin-top: 1px; - margin-left: 2px; -} - -body.attached .view-button-browse.selected img { - content: url(Images/databaseBrowserViewSmallSelected.png); -} - -.view-button-query img { - content: url(Images/databaseQueryViewNormal.png); - vertical-align: middle; - margin-top: -1px; - margin-left: -1px; - width: 11px; - height: 11px; -} - -.view-button-query.selected img { - content: url(Images/databaseQueryViewNormalSelected.png); -} - -body.attached .view-button-query img { - content: url(Images/databaseQueryViewSmall.png); - width: 10px; - height: 8px; - margin-top: 1px; -} - -body.attached .view-button-query.selected img { - content: url(Images/databaseQueryViewSmallSelected.png); -} - -.database-table-reload { - padding-left: 0; - padding-right: 0; - width: 28px; - margin-left: 6px; -} - -body.attached .database-table-reload { - width: 20px; -} - -.database-table-reload img { - content: url(Images/reload.png); - vertical-align: middle; - margin-top: -2px; - width: 10px; - height: 13px; -} - -.query.content { - bottom: 21px; -} - -.browse.content { - font-size: 10px; - overflow-y: auto; - overflow-x: hidden; - bottom: 21px; -} - -.browse.content .database-result-table { - border: none; -} - -.browse.content .database-table-empty, .browse.content .database-table-error { - position: absolute; - top: 0; - bottom: 25%; - left: 0; - right: 0; - font-size: 24px; - color: rgb(75%, 75%, 75%); - margin-top: auto; - margin-bottom: auto; - height: 50px; - line-height: 26px; - text-align: center; - font-weight: bold; - padding: 10px; - white-space: pre-wrap; -} - -.browse.content .database-table-error { - color: rgb(66%, 33%, 33%); -} - -.database-browse-table { - height: 100%; -} - -.database-result-table .database-result-filler-row { - height: auto; -} - -.database-result-table .database-result-filler-row.alternate td { - background-position-y: 16px; -} - -.database-result-filler-row td { - background-image: url(Images/alternateTableRows.png); -} - -.database-table-select { - margin-left: 6px; - max-width: 150px; - min-width: 75px; -} - -.database-command-list { - list-style: none; - margin: 0; - padding: 0; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - overflow-y: auto; - overflow-x: hidden; -} - -.database-prompt { - font-family: monospace; - font-size: 11px; - margin: 0; - padding: 2px 0 0; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 18px; - resize: none; - outline: none; - border: none; - border-top: 1px solid rgb(64%, 64%, 64%); -} - -.database-command { - font-size: 10px; - margin: 0; - padding: 5px; - border-bottom: 1px solid rgb(75%, 75%, 75%); - word-break: break-word; - position: relative; -} - -.database-command a:hover { - text-decoration: underline; - cursor: pointer; -} - -.database-command-query { - font-family: monospace; - font-size: 11px; - white-space: pre-wrap; -} - -.database-command-result { - margin-top: 3px; -} - -.database-command-result.error { - color: red; -} - -.database-result-table { - border: 1px solid #aaa; - table-layout: fixed; - border-spacing: 0; - border-collapse: collapse; - width: 100%; - -webkit-box-sizing: border-box; -} - -.database-result-table th { - text-align: left; - background: url(Images/glossyHeader.png) repeat-x; - border-right: 1px solid #aaa; - height: 15px; - -webkit-box-sizing: border-box; - border-bottom: 1px solid #aaa; - font-weight: normal; - vertical-align: middle; - padding: 0 4px; - white-space: nowrap; -} - -.database-result-table tr { - height: 16px; -} - -.database-result-table tr.alternate { - background-color: rgb(236, 243, 254); -} - -.database-result-table td { - vertical-align: top; - padding: 2px 4px; - -webkit-box-sizing: border-box; - white-space: nowrap; - border-right: 1px solid #aaa; -} - -.database-result-table td > div, .database-result-table th > div { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.network-timeline { - position: absolute; - top: 0; - bottom: 99px; - left: 0; - right: 0; - font-family: Lucida Grande, sans-serif; - font-size: 11px; -} - -.network-divider { - width: 1px; - height: 100%; - position: absolute; - background-color: rgba(0, 0, 0, 0.1); -} - -.network-divider.last { - background-color: rgb(66%, 66%, 66%); -} - -.network-divider-label { - position: absolute; - top: 2px; - right: 3px; - font-size: 9px; - color: rgb(50%, 50%, 50%); -} - -.network-dividers { - position: absolute; - left: 153px; - right: 20px; - bottom: 0; - top: 0; - z-index: -100; - border-left: 1px solid rgb(66%, 66%, 66%); - -webkit-box-sizing: border-box; -} - -.network-resources { - position: absolute; - width: 100%; - overflow-y: overlay; - overflow-x: hidden; - border-top: 1px solid rgb(66%, 66%, 66%); - top: 15px; - bottom: 0; -} - -.network-title { - position: relative; - height: 18px; -} - -.network-title:hover { - background-color: rgba(0, 0, 200, 0.1); -} - -.network-info { - background-color: rgb(225, 225, 235); - background-image: url(Images/attachedShadow.png), url(Images/bottomShadow.png); - background-repeat: repeat-x; - background-position: top, bottom; - overflow: hidden; - -webkit-user-select: text; - cursor: auto; -} - -.network-info table { - font-size: 11px; - margin: 5px 15px 5px 5px; -} - -.network-info th { - width: 145px; -} - -.network-info thead th { - text-align: right; -} - -.network-info tbody th { - white-space: nowrap; - text-align: right; - font-weight: bold; - color: rgba(0, 0, 0, 0.5); - vertical-align: top; -} - -.network-info td { - word-break: break-word; - white-space: normal; -} - -.network-file { - position: absolute; - left: 5px; - height: 100%; - width: 145px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - line-height: 18px; - -webkit-user-select: text; -} - -.network-file a { - color: inherit; - text-decoration: none; -} - -.network-file a:hover { - text-decoration: underline; -} - -.network-area { - position: absolute; - left: 162px; - right: 28px; - height: 100%; -} - -.network-bar { - position: absolute; - top: 0; - bottom: 0; - margin: auto -7px; - border-width: 6px 7px 6px 7px; - height: 13px; - min-width: 14px; - -webkit-box-sizing: border-box; - opacity: 0.8; - -webkit-border-image: url(Images/timelinePillGray.png) 6 7 6 7; -} - -.network-bar.network-category-documents { - -webkit-border-image: url(Images/timelinePillBlue.png) 6 7 6 7; -} - -.network-bar.network-category-stylesheets { - -webkit-border-image: url(Images/timelinePillGreen.png) 6 7 6 7; -} - -.network-bar.network-category-images { - -webkit-border-image: url(Images/timelinePillPurple.png) 6 7 6 7; -} - -.network-bar.network-category-fonts { - -webkit-border-image: url(Images/timelinePillYellow.png) 6 7 6 7; -} - -.network-bar.network-category-scripts { - -webkit-border-image: url(Images/timelinePillOrange.png) 6 7 6 7; -} - -.network-summary { - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 99px; - background-color: rgb(101, 111, 130); - background-image: url(Images/darkShadow.png), url(Images/gradientHighlightBottom.png); - background-repeat: repeat-x; - background-position: top, bottom; -} - -.network-graph-area { - padding-top: 20px; - position: absolute; - margin: auto; - top: 0; - bottom: 0; - right: 0; - left: 0; - width: 575px; - white-space: nowrap; - color: white; - text-shadow: black 0px 1px 1px; -} - -.network-graph-label { - height: 38px; - display: inline-block; - vertical-align: top; - margin-right: 5px; - margin-top: -2px; - text-align: right; -} - -.network-graph-side { - position: relative; - display: inline-block; - vertical-align: top; -} - -.network-graph-legend-total { - margin-top: 12px; - padding-right: 5px; -} - -.network-graph-legend-total .network-graph-legend-label { - text-align: right; -} - -.network-graph-mode { - -webkit-appearance: none; - background-color: transparent; - border: none; - font-weight: bold; - font-size: 12px; - height: 18px; - line-height: 11px; - text-align: right; - vertical-align: middle; - padding: 2px 16px 2px 8px; - margin: 0; - background-image: url(Images/popupArrows.png); - background-position: right center; - background-repeat: no-repeat; - color: inherit; - border: 1px solid transparent; - text-shadow: black 0px 2px 2px; -} - -.network-graph-mode:focus { - outline: none; -} - -.network-graph-mode:hover { - -webkit-border-radius: 9px; - background-color: rgba(0, 0, 0, 0.2); - border: 1px solid white; - -webkit-box-shadow: black 0px 1px 1px; -} - -.network-graph-legend { - margin-top: -8px; - text-align: center; -} - -.network-graph-legend-item { - display: inline-block; - font-weight: bold; - margin-right: 15px; - vertical-align: top; -} - -.network-graph-legend-label { - display: inline-block; - text-align: left; -} - -.network-graph-legend-header { - font-size: 12px; - text-transform: capitalize; -} - -.network-graph-legend-value { - font-size: 10px; -} - -.network-graph-legend-swatch { - vertical-align: top; - margin-top: 1px; - margin-right: 3px; -} - -.network-summary-graph { - vertical-align: middle; -} - -.tip-button { - background-image: url(Images/tipIcon.png); - border: none; - width: 16px; - height: 16px; - float: right; - background-color: transparent; - margin-top: 1px; -} - -.tip-button:active { - background-image: url(Images/tipIconPressed.png); -} - -.tip-balloon { - position: absolute; - left: 145px; - top: -5px; - z-index: 1000; - border-width: 51px 15px 18px 37px; - -webkit-border-image: url(Images/tipBalloon.png) 51 15 18 37; - width: 265px; -} - -.tip-balloon.bottom { - position: absolute; - left: 145px; - top: auto; - bottom: -7px; - z-index: 1000; - border-width: 18px 15px 51px 37px; - -webkit-border-image: url(Images/tipBalloonBottom.png) 18 15 51 37; -} - -.tip-balloon-content { - margin-top: -40px; - margin-bottom: -2px; - margin-left: 2px; -} - -.tip-balloon.bottom .tip-balloon-content { - margin-top: -10px; - margin-bottom: -35px; -} - -.sidebar-resizer-vertical { - position: absolute; - top: 0; - bottom: 0; - width: 5px; - z-index: 100; - cursor: col-resize; -} - -.sidebar-resizer-vertical-left { - left: 197px; -} - -.sidebar-resizer-vertical-right { - right: 222px; -} - -#searchResultsResizer { - position: absolute; - height: 5px; - left: 0; - right: 0; - cursor: row-resize; -} diff --git a/WebCore/page/inspector/inspector.html b/WebCore/page/inspector/inspector.html deleted file mode 100644 index 3ba0300..0000000 --- a/WebCore/page/inspector/inspector.html +++ /dev/null @@ -1,75 +0,0 @@ -<!-- -Copyright (C) 2006, 2007 Apple 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: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. 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. -3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. ---> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> - <link rel="stylesheet" type="text/css" href="inspector.css" /> - <script type="text/javascript" src="utilities.js"></script> - <script type="text/javascript" src="treeoutline.js"></script> - <script type="text/javascript" src="inspector.js"></script> - <script type="text/javascript" src="Resource.js"></script> - <script type="text/javascript" src="ResourceCategory.js"></script> - <script type="text/javascript" src="Database.js"></script> - <script type="text/javascript" src="SidebarPane.js"></script> - <script type="text/javascript" src="PropertiesSection.js"></script> - <script type="text/javascript" src="MetricsSidebarPane.js"></script> - <script type="text/javascript" src="PropertiesSidebarPane.js"></script> - <script type="text/javascript" src="StylesSidebarPane.js"></script> - <script type="text/javascript" src="Panel.js"></script> - <script type="text/javascript" src="ResourcePanel.js"></script> - <script type="text/javascript" src="SourcePanel.js"></script> - <script type="text/javascript" src="ConsolePanel.js"></script> - <script type="text/javascript" src="DatabasePanel.js"></script> - <script type="text/javascript" src="DocumentPanel.js"></script> - <script type="text/javascript" src="FontPanel.js"></script> - <script type="text/javascript" src="ImagePanel.js"></script> - <script type="text/javascript" src="NetworkPanel.js"></script> -</head> -<body class="detached"> - <div id="toolbar"> - <button id="back" class="split-button first"><img></button><img class="split-button-divider"><button id="forward" class="split-button last"><img></button> - <span id="toolbarButtons"></span> - <input id="search" type="search" autosave="inspectorSearch" results="20" incremental="incremental" onsearch="WebInspector.performSearch(this.value)"> - </div> - <div id="sidebar" class="focusable focused"> - <ol id="list"></ol> - <ol id="status"></ol> - <div id="statusbar"> - <button id="attachToggle"></button> - <span id="sidebarResizeWidget"></span> - </div> - <div id="sidebarResizer" class="sidebar-resizer-vertical sidebar-resizer-vertical-left"></div> - </div> - <div id="main" class="focusable blurred"> - <div id="searchResults" class="focusable hidden"></div> - <div id="searchResultsResizer" class="hidden"></div> - <div id="panels"></div> - </div> -</body> -</html> diff --git a/WebCore/page/inspector/inspector.js b/WebCore/page/inspector/inspector.js deleted file mode 100644 index 04e78e6..0000000 --- a/WebCore/page/inspector/inspector.js +++ /dev/null @@ -1,1186 +0,0 @@ -/* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com). - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -var Preferences = { - ignoreWhitespace: true, - showUserAgentStyles: true, - maxInlineTextChildLength: 80, - maxTextSearchResultLength: 80, - showInheritedComputedStyleProperties: false, - showMissingLocalizedStrings: false -} - -var WebInspector = { - resources: [], - resourceURLMap: {}, - backForwardList: [], - searchResultsHeight: 100, - localizedStrings: {}, - missingLocalizedStrings: {}, - - get consolePanel() - { - if (!this._consolePanel) - this._consolePanel = new WebInspector.ConsolePanel(); - - return this._consolePanel; - }, - - get networkPanel() - { - if (!this._networkPanel) - this._networkPanel = new WebInspector.NetworkPanel(); - - return this._networkPanel; - }, - - get currentBackForwardIndex() - { - if (this._currentBackForwardIndex === undefined) - this._currentBackForwardIndex = -1; - - return this._currentBackForwardIndex; - }, - - set currentBackForwardIndex(x) - { - if (this._currentBackForwardIndex === x) - return; - - this._currentBackForwardIndex = x; - this.updateBackForwardButtons(); - }, - - get currentFocusElement() - { - return this._currentFocusElement; - }, - - set currentFocusElement(x) - { - if (!x || this._currentFocusElement === x) - return; - - if (this._currentFocusElement) { - this._currentFocusElement.removeStyleClass("focused"); - this._currentFocusElement.addStyleClass("blurred"); - if (this._currentFocusElement.blurred) - this._currentFocusElement.blurred(); - } - - this._currentFocusElement = x; - - if (x) { - x.addStyleClass("focused"); - x.removeStyleClass("blurred"); - if (this._currentFocusElement.focused) - this._currentFocusElement.focused(); - } - }, - - get currentPanel() - { - return this._currentPanel; - }, - - set currentPanel(x) - { - if (this._currentPanel === x) - return; - - if (this._currentPanel) - this._currentPanel.hide(); - - this._currentPanel = x; - - if (x) - x.show(); - }, - - get attached() - { - return this._attached; - }, - - set attached(x) - { - if (this._attached === x) - return; - - this._attached = x; - - var body = document.body; - if (x) { - InspectorController.attach(); - body.removeStyleClass("detached"); - body.addStyleClass("attached"); - } else { - InspectorController.detach(); - body.removeStyleClass("attached"); - body.addStyleClass("detached"); - } - }, - - get showingSearchResults() - { - return this._showingSearchResults; - }, - - set showingSearchResults(x) - { - if (this._showingSearchResults === x) - return; - - this._showingSearchResults = x; - - var resultsContainer = document.getElementById("searchResults"); - var searchResultsResizer = document.getElementById("searchResultsResizer"); - - if (x) { - resultsContainer.removeStyleClass("hidden"); - searchResultsResizer.removeStyleClass("hidden"); - - var animations = [ - {element: resultsContainer, end: {top: 0}}, - {element: searchResultsResizer, end: {top: WebInspector.searchResultsHeight - 3}}, - {element: document.getElementById("panels"), end: {top: WebInspector.searchResultsHeight}} - ]; - - WebInspector.animateStyle(animations, 250); - } else { - searchResultsResizer.addStyleClass("hidden"); - - var animations = [ - {element: resultsContainer, end: {top: -WebInspector.searchResultsHeight}}, - {element: searchResultsResizer, end: {top: 0}}, - {element: document.getElementById("panels"), end: {top: 0}} - ]; - - var animationFinished = function() - { - resultsContainer.addStyleClass("hidden"); - resultsContainer.removeChildren(); - delete this.searchResultsTree; - }; - - WebInspector.animateStyle(animations, 250, animationFinished); - } - } -} - -WebInspector.loaded = function() -{ - var platform = InspectorController.platform(); - document.body.addStyleClass("platform-" + platform); - - this.fileOutline = new TreeOutline(document.getElementById("list")); - this.fileOutline.expandTreeElementsWhenArrowing = true; - - this.statusOutline = new TreeOutline(document.getElementById("status")); - this.statusOutline.expandTreeElementsWhenArrowing = true; - - this.resourceCategories = { - documents: new WebInspector.ResourceCategory(WebInspector.UIString("documents"), "documents"), - stylesheets: new WebInspector.ResourceCategory(WebInspector.UIString("stylesheets"), "stylesheets"), - images: new WebInspector.ResourceCategory(WebInspector.UIString("images"), "images"), - scripts: new WebInspector.ResourceCategory(WebInspector.UIString("scripts"), "scripts"), - fonts: new WebInspector.ResourceCategory(WebInspector.UIString("fonts"), "fonts"), - databases: new WebInspector.ResourceCategory(WebInspector.UIString("databases"), "databases"), - other: new WebInspector.ResourceCategory(WebInspector.UIString("other"), "other") - }; - - this.Tips = { - ResourceNotCompressed: {id: 0, message: WebInspector.UIString("You could save bandwidth by having your web server compress this transfer with gzip or zlib.")} - }; - - this.Warnings = { - IncorrectMIMEType: {id: 0, message: WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s.")} - }; - - this.consoleListItem = new WebInspector.ConsoleStatusTreeElement(WebInspector.consolePanel); - this.statusOutline.appendChild(this.consoleListItem); - - this.networkListItem = new WebInspector.StatusTreeElement(WebInspector.UIString("Network"), "network", WebInspector.networkPanel); - this.statusOutline.appendChild(this.networkListItem); - - this.resourceCategories.documents.listItem.expand(); - - this.currentFocusElement = document.getElementById("sidebar"); - - this.addMainEventListeners(document); - - window.addEventListener("unload", this.windowUnload.bind(this), true); - window.addEventListener("resize", this.windowResize.bind(this), true); - - document.addEventListener("mousedown", this.changeFocus.bind(this), true); - document.addEventListener("focus", this.changeFocus.bind(this), true); - document.addEventListener("keydown", this.documentKeyDown.bind(this), true); - document.addEventListener("beforecopy", this.documentCanCopy.bind(this), true); - document.addEventListener("copy", this.documentCopy.bind(this), true); - - document.getElementById("back").title = WebInspector.UIString("Show previous panel."); - document.getElementById("forward").title = WebInspector.UIString("Show next panel."); - - document.getElementById("search").setAttribute("placeholder", WebInspector.UIString("Search")); - - document.getElementById("back").addEventListener("click", this.back.bind(this), true); - document.getElementById("forward").addEventListener("click", this.forward.bind(this), true); - this.updateBackForwardButtons(); - - document.getElementById("attachToggle").addEventListener("click", this.toggleAttach.bind(this), true); - - document.getElementById("sidebarResizeWidget").addEventListener("mousedown", this.sidebarResizerDragStart, true); - document.getElementById("sidebarResizer").addEventListener("mousedown", this.sidebarResizerDragStart, true); - document.getElementById("searchResultsResizer").addEventListener("mousedown", this.searchResultsResizerDragStart, true); - - if (platform === "mac-leopard") - document.getElementById("toolbar").addEventListener("mousedown", this.toolbarDragStart, true); - - document.body.addStyleClass("detached"); - - InspectorController.loaded(); -} - -var windowLoaded = function() -{ - var localizedStringsURL = InspectorController.localizedStringsURL(); - if (localizedStringsURL) { - var localizedStringsScriptElement = document.createElement("script"); - localizedStringsScriptElement.addEventListener("load", WebInspector.loaded.bind(WebInspector), false); - localizedStringsScriptElement.type = "text/javascript"; - localizedStringsScriptElement.src = localizedStringsURL; - document.getElementsByTagName("head").item(0).appendChild(localizedStringsScriptElement); - } else - WebInspector.loaded(); - - window.removeEventListener("load", windowLoaded, false); - delete windowLoaded; -}; - -window.addEventListener("load", windowLoaded, false); - -WebInspector.windowUnload = function(event) -{ - InspectorController.windowUnloading(); -} - -WebInspector.windowResize = function(event) -{ - if (this.currentPanel && this.currentPanel.resize) - this.currentPanel.resize(); -} - -WebInspector.windowFocused = function(event) -{ - if (event.target.nodeType === Node.DOCUMENT_NODE) - document.body.removeStyleClass("inactive"); -} - -WebInspector.windowBlured = function(event) -{ - if (event.target.nodeType === Node.DOCUMENT_NODE) - document.body.addStyleClass("inactive"); -} - -WebInspector.changeFocus = function(event) -{ - var nextFocusElement; - - var current = event.target; - while (current) { - if (current.nodeName.toLowerCase() === "input") - nextFocusElement = current; - current = current.parentNode; - } - - if (!nextFocusElement) - nextFocusElement = event.target.firstParentWithClass("focusable"); - - this.currentFocusElement = nextFocusElement; -} - -WebInspector.documentClick = function(event) -{ - var anchor = event.target.firstParentOrSelfWithNodeName("a"); - if (!anchor || !anchor.hasStyleClass("webkit-html-resource-link")) - return; - - if (WebInspector.showResourceForURL(anchor.getAttribute("href"))) { - event.preventDefault(); - event.stopPropagation(); - } -} - -WebInspector.documentKeyDown = function(event) -{ - if (!this.currentFocusElement) - return; - if (this.currentFocusElement.handleKeyEvent) - this.currentFocusElement.handleKeyEvent(event); - else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "KeyDown"]) - WebInspector[this.currentFocusElement.id + "KeyDown"](event); -} - -WebInspector.documentCanCopy = function(event) -{ - if (!this.currentFocusElement) - return; - // Calling preventDefault() will say "we support copying, so enable the Copy menu". - if (this.currentFocusElement.handleCopyEvent) - event.preventDefault(); - else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"]) - event.preventDefault(); -} - -WebInspector.documentCopy = function(event) -{ - if (!this.currentFocusElement) - return; - if (this.currentFocusElement.handleCopyEvent) - this.currentFocusElement.handleCopyEvent(event); - else if (this.currentFocusElement.id && this.currentFocusElement.id.length && WebInspector[this.currentFocusElement.id + "Copy"]) - WebInspector[this.currentFocusElement.id + "Copy"](event); -} - -WebInspector.sidebarKeyDown = function(event) -{ - var nextSelectedElement; - - if (this.fileOutline.selectedTreeElement) { - if (!this.fileOutline.handleKeyEvent(event) && event.keyIdentifier === "Down" && !event.altKey) { - var nextSelectedElement = this.statusOutline.children[0]; - while (nextSelectedElement && !nextSelectedElement.selectable) - nextSelectedElement = nextSelectedElement.traverseNextTreeElement(false); - } - } else if (this.statusOutline.selectedTreeElement) { - if (!this.statusOutline.handleKeyEvent(event) && event.keyIdentifier === "Up" && !event.altKey) { - var nextSelectedElement = this.fileOutline.children[0]; - var lastSelectable = null; - - while (nextSelectedElement) { - if (nextSelectedElement.selectable) - lastSelectable = nextSelectedElement; - nextSelectedElement = nextSelectedElement.traverseNextTreeElement(false); - } - - nextSelectedElement = lastSelectable; - } - } - - if (nextSelectedElement) { - nextSelectedElement.reveal(); - nextSelectedElement.select(); - - event.preventDefault(); - event.stopPropagation(); - } -} - -WebInspector.sidebarCopy = function(event) -{ - event.clipboardData.clearData(); - event.preventDefault(); - - var selectedElement = this.fileOutline.selectedTreeElement; - if (!selectedElement || !selectedElement.representedObject || !selectedElement.representedObject.url) - return; - - event.clipboardData.setData("URL", this.fileOutline.selectedTreeElement.representedObject.url); -} - -WebInspector.mainKeyDown = function(event) -{ - if (this.currentPanel && this.currentPanel.handleKeyEvent) - this.currentPanel.handleKeyEvent(event); -} - -WebInspector.mainCopy = function(event) -{ - if (this.currentPanel && this.currentPanel.handleCopyEvent) - this.currentPanel.handleCopyEvent(event); -} - -WebInspector.searchResultsKeyDown = function(event) -{ - if (this.searchResultsTree) - this.searchResultsTree.handleKeyEvent(event); -} - -WebInspector.animateStyle = function(animations, duration, callback, complete) -{ - if (complete === undefined) - complete = 0; - var slice = (1000 / 30); // 30 frames per second - - var defaultUnit = "px"; - var propertyUnit = {opacity: ""}; - - for (var i = 0; i < animations.length; ++i) { - var animation = animations[i]; - var element = null; - var start = null; - var current = null; - var end = null; - for (key in animation) { - if (key === "element") - element = animation[key]; - else if (key === "start") - start = animation[key]; - else if (key === "current") - current = animation[key]; - else if (key === "end") - end = animation[key]; - } - - if (!element || !end) - continue; - - var computedStyle = element.ownerDocument.defaultView.getComputedStyle(element); - if (!start) { - start = {}; - for (key in end) - start[key] = parseInt(computedStyle.getPropertyValue(key)); - animation.start = start; - } else if (complete == 0) - for (key in start) - element.style.setProperty(key, start[key] + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); - - if (!current) { - current = {}; - for (key in start) - current[key] = start[key]; - animation.current = current; - } - - function cubicInOut(t, b, c, d) - { - if ((t/=d/2) < 1) return c/2*t*t*t + b; - return c/2*((t-=2)*t*t + 2) + b; - } - - var style = element.style; - for (key in end) { - var startValue = start[key]; - var currentValue = current[key]; - var endValue = end[key]; - if ((complete + slice) < duration) { - var delta = (endValue - startValue) / (duration / slice); - var newValue = cubicInOut(complete, startValue, endValue - startValue, duration); - style.setProperty(key, newValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); - current[key] = newValue; - } else { - style.setProperty(key, endValue + (key in propertyUnit ? propertyUnit[key] : defaultUnit)); - } - } - } - - if (complete < duration) - setTimeout(WebInspector.animateStyle, slice, animations, duration, callback, complete + slice); - else if (callback) - callback(); -} - -WebInspector.toggleAttach = function() -{ - this.attached = !this.attached; -} - -WebInspector.toolbarDragStart = function(event) -{ - var toolbar = document.getElementById("toolbar"); - if (event.target !== toolbar || WebInspector.attached) - return; - - toolbar.lastScreenX = event.screenX; - toolbar.lastScreenY = event.screenY; - - document.addEventListener("mousemove", WebInspector.toolbarDrag, true); - document.addEventListener("mouseup", WebInspector.toolbarDragEnd, true); - document.body.style.cursor = "default"; - - event.preventDefault(); -} - -WebInspector.toolbarDragEnd = function(event) -{ - var toolbar = document.getElementById("toolbar"); - delete toolbar.lastScreenX; - delete toolbar.lastScreenY; - - document.removeEventListener("mousemove", WebInspector.toolbarDrag, true); - document.removeEventListener("mouseup", WebInspector.toolbarDragEnd, true); - document.body.style.removeProperty("cursor"); - - event.preventDefault(); -} - -WebInspector.toolbarDrag = function(event) -{ - var toolbar = document.getElementById("toolbar"); - - var x = event.screenX - toolbar.lastScreenX; - var y = event.screenY - toolbar.lastScreenY; - - toolbar.lastScreenX = event.screenX; - toolbar.lastScreenY = event.screenY; - - // We cannot call window.moveBy here because it restricts the movement of the window - // at the edges. - InspectorController.moveByUnrestricted(x, y); - - event.preventDefault(); -} - -WebInspector.sidebarResizerDragStart = function(event) -{ - WebInspector.elementDragStart(document.getElementById("sidebar"), WebInspector.sidebarResizerDrag, WebInspector.sidebarResizerDragEnd, event, "col-resize"); -} - -WebInspector.sidebarResizerDragEnd = function(event) -{ - WebInspector.elementDragEnd(document.getElementById("sidebar"), WebInspector.sidebarResizerDrag, WebInspector.sidebarResizerDragEnd, event); -} - -WebInspector.sidebarResizerDrag = function(event) -{ - var x = event.pageX; - - // FIXME: We can should come up with a better hueristic for constraining the size of the sidebar. - var newWidth = Number.constrain(x, 100, window.innerWidth - 100); - - document.getElementById("sidebar").style.width = newWidth + "px"; - document.getElementById("sidebarResizer").style.left = (newWidth - 3) + "px"; - document.getElementById("main").style.left = newWidth + "px"; - document.getElementById("toolbarButtons").style.left = newWidth + "px"; - - if (WebInspector.currentPanel && WebInspector.currentPanel.resize) - WebInspector.currentPanel.resize(); - - event.preventDefault(); -} - -WebInspector.searchResultsResizerDragStart = function(event) -{ - WebInspector.elementDragStart(document.getElementById("searchResults"), WebInspector.searchResultsResizerDrag, WebInspector.searchResultsResizerDragEnd, event, "row-resize"); -} - -WebInspector.searchResultsResizerDragEnd = function(event) -{ - WebInspector.elementDragEnd(document.getElementById("searchResults"), WebInspector.searchResultsResizerDrag, WebInspector.searchResultsResizerDragEnd, event); -} - -WebInspector.searchResultsResizerDrag = function(event) -{ - var y = event.pageY - document.getElementById("main").offsetTop; - var newHeight = Number.constrain(y, 100, window.innerHeight - 100); - - WebInspector.searchResultsHeight = newHeight; - - document.getElementById("searchResults").style.height = WebInspector.searchResultsHeight + "px"; - document.getElementById("panels").style.top = newHeight + "px"; - document.getElementById("searchResultsResizer").style.top = (newHeight - 3) + "px"; - - event.preventDefault(); -} - -WebInspector.elementDragStart = function(element, dividerDrag, elementDragEnd, event, cursor) -{ - if (WebInspector.draggingElement) - return elementDragEnd(event); - - WebInspector.draggingElement = true; - - document.addEventListener("mousemove", dividerDrag, true); - document.addEventListener("mouseup", elementDragEnd, true); - document.body.style.cursor = cursor; - - event.preventDefault(); -} - -WebInspector.elementDragEnd = function(element, dividerDrag, elementDragEnd, event) -{ - document.removeEventListener("mousemove", dividerDrag, true); - document.removeEventListener("mouseup", elementDragEnd, true); - document.body.style.removeProperty("cursor"); - - delete WebInspector.draggingElement; - - event.preventDefault(); -} - -WebInspector.back = function() -{ - if (this.currentBackForwardIndex <= 0) { - console.error("Can't go back from index " + this.currentBackForwardIndex); - return; - } - - this.navigateToPanel(this.backForwardList[--this.currentBackForwardIndex], null, true); -} - -WebInspector.forward = function() -{ - if (this.currentBackForwardIndex >= this.backForwardList.length - 1) { - console.error("Can't go forward from index " + this.currentBackForwardIndex); - return; - } - - this.navigateToPanel(this.backForwardList[++this.currentBackForwardIndex], null, true); -} - -WebInspector.updateBackForwardButtons = function() -{ - var index = this.currentBackForwardIndex; - - document.getElementById("back").disabled = index <= 0; - document.getElementById("forward").disabled = index >= this.backForwardList.length - 1; -} - -WebInspector.showConsole = function() -{ - this.navigateToPanel(WebInspector.consolePanel); -} - -WebInspector.showTimeline = function() -{ - this.navigateToPanel(WebInspector.networkPanel); -} - -WebInspector.addResource = function(resource) -{ - this.resources.push(resource); - - if (resource.mainResource) - this.mainResource = resource; - - if (resource.url) { - this.resourceURLMap[resource.url] = resource; - this.networkPanel.addResourceToTimeline(resource); - } -} - -WebInspector.removeResource = function(resource) -{ - resource.detach(); - - resource.category.removeResource(resource); - - if (resource.url) - delete this.resourceURLMap[resource.url]; - - var resourcesLength = this.resources.length; - for (var i = 0; i < resourcesLength; ++i) { - if (this.resources[i] === resource) { - this.resources.splice(i, 1); - break; - } - } -} - -WebInspector.clearResources = function() -{ - for (var category in this.resourceCategories) - this.resourceCategories[category].removeAllResources(); - this.resources = []; - this.backForwardList = []; - this.currentBackForwardIndex = -1; - delete this.mainResource; -} - -WebInspector.clearDatabaseResources = function() -{ - this.resourceCategories.databases.removeAllResources(); -} - -WebInspector.resourceURLChanged = function(resource, oldURL) -{ - delete this.resourceURLMap[oldURL]; - this.resourceURLMap[resource.url] = resource; -} - -WebInspector.addMessageToConsole = function(msg) -{ - this.consolePanel.addMessage(msg); - switch (msg.level) { - case WebInspector.ConsoleMessage.MessageLevel.Warning: - ++this.consoleListItem.warnings; - break; - case WebInspector.ConsoleMessage.MessageLevel.Error: - ++this.consoleListItem.errors; - break; - } -} - -WebInspector.clearConsoleMessages = function() -{ - this.consolePanel.clearMessages(); - this.consoleListItem.warnings = this.consoleListItem.errors = 0; -} - -WebInspector.clearNetworkTimeline = function() -{ - if (this._networkPanel) - this._networkPanel.clearTimeline(); -} - -WebInspector.drawLoadingPieChart = function(canvas, percent) { - var g = canvas.getContext("2d"); - var darkColor = "rgb(122, 168, 218)"; - var lightColor = "rgb(228, 241, 251)"; - var cx = 8; - var cy = 8; - var r = 7; - - g.beginPath(); - g.arc(cx, cy, r, 0, Math.PI * 2, false); - g.closePath(); - - g.lineWidth = 1; - g.strokeStyle = darkColor; - g.fillStyle = lightColor; - g.fill(); - g.stroke(); - - var startangle = -Math.PI / 2; - var endangle = startangle + (percent * Math.PI * 2); - - g.beginPath(); - g.moveTo(cx, cy); - g.arc(cx, cy, r, startangle, endangle, false); - g.closePath(); - - g.fillStyle = darkColor; - g.fill(); -} - -WebInspector.updateFocusedNode = function(node) -{ - if (!node) - // FIXME: Should we deselect if null is passed in? - return; - - for (var i = 0; i < this.resourceCategories.documents.resources.length; ++i) { - var resource = this.resourceCategories.documents.resources[i]; - if (resource.documentNode !== node.ownerDocument) - continue; - - this.navigateToPanel(resource.panel, "dom"); - resource.panel.focusedDOMNode = node; - - this.currentFocusElement = document.getElementById("main"); - - break; - } -} - -WebInspector.resourceForURL = function(url) -{ - for (var resourceURL in this.resourceURLMap) { - if (resourceURL.hasSubstring(url)) - return this.resourceURLMap[resourceURL]; - } - - return null; -} - -WebInspector.showResourceForURL = function(url) -{ - var resource = this.resourceForURL(url); - if (!resource) - return false; - - this.navigateToResource(resource); - return true; -} - -WebInspector.linkifyURL = function(url, linkText, classes, isExternal) -{ - if (linkText === undefined) - linkText = url.escapeHTML(); - classes = (classes === undefined) ? "" : classes + " "; - classes += isExternal ? "webkit-html-external-link" : "webkit-html-resource-link"; - var link = "<a href=\"" + url + "\" class=\"" + classes + "\" title=\"" + url + "\" target=\"_blank\">" + linkText + "</a>"; - return link; -} - -WebInspector.addMainEventListeners = function(doc) -{ - doc.defaultView.addEventListener("focus", function(event) { WebInspector.windowFocused(event) }, true); - doc.defaultView.addEventListener("blur", function(event) { WebInspector.windowBlured(event) }, true); - doc.addEventListener("click", function(event) { WebInspector.documentClick(event) }, true); -} - -WebInspector.performSearch = function(query) -{ - if (!query || !query.length) { - this.showingSearchResults = false; - return; - } - - var resultsContainer = document.getElementById("searchResults"); - resultsContainer.removeChildren(); - - var isXPath = query.indexOf("/") !== -1; - - var xpathQuery; - if (isXPath) - xpathQuery = query; - else { - var escapedQuery = query.escapeCharacters("'"); - xpathQuery = "//*[contains(name(),'" + escapedQuery + "') or contains(@*,'" + escapedQuery + "')] | //text()[contains(.,'" + escapedQuery + "')] | //comment()[contains(.,'" + escapedQuery + "')]"; - } - - var resourcesToSearch = [].concat(this.resourceCategories.documents.resources, this.resourceCategories.stylesheets.resources, this.resourceCategories.scripts.resources, this.resourceCategories.other.resources); - - var files = []; - for (var i = 0; i < resourcesToSearch.length; ++i) { - var resource = resourcesToSearch[i]; - - var sourceResults = []; - if (!isXPath && "source" in resource.panel.views) { - resource.panel.setupSourceFrameIfNeeded(); - sourceResults = InspectorController.search(resource.panel.views.source.frameElement.contentDocument, query); - } - - var domResults = []; - const searchResultsProperty = "__includedInInspectorSearchResults"; - function addNodesToDOMResults(nodes, length, getItem) - { - for (var i = 0; i < length; ++i) { - var node = getItem(nodes, i); - if (searchResultsProperty in node) - continue; - node[searchResultsProperty] = true; - domResults.push(node); - } - } - - function cleanUpDOMResultsNodes() - { - for (var i = 0; i < domResults.length; ++i) - delete domResults[i][searchResultsProperty]; - } - - if (resource.category === this.resourceCategories.documents) { - var doc = resource.documentNode; - try { - var result = Document.prototype.evaluate.call(doc, xpathQuery, doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - addNodesToDOMResults(result, result.snapshotLength, function(l, i) { return l.snapshotItem(i); }); - } catch(err) { - // ignore any exceptions. the query might be malformed, but we allow that. - } - - var result = Document.prototype.querySelectorAll.call(doc, query); - addNodesToDOMResults(result, result.length, function(l, i) { return l.item(i); }); - - cleanUpDOMResultsNodes(); - } - - if ((!sourceResults || !sourceResults.length) && !domResults.length) - continue; - - files.push({resource: resource, sourceResults: sourceResults, domResults: domResults}); - } - - if (!files.length) - return; - - this.showingSearchResults = true; - - var fileList = document.createElement("ol"); - fileList.className = "outline-disclosure"; - resultsContainer.appendChild(fileList); - - this.searchResultsTree = new TreeOutline(fileList); - this.searchResultsTree.expandTreeElementsWhenArrowing = true; - - var sourceResultSelected = function(element) - { - var selection = window.getSelection(); - selection.removeAllRanges(); - selection.addRange(element.representedObject.range); - - WebInspector.navigateToPanel(element.representedObject.panel, "source"); - element.representedObject.line.scrollIntoView(true); - resultsContainer.scrollToElement(element.listItemElement); - } - - var domResultSelected = function(element) - { - WebInspector.navigateToPanel(element.representedObject.panel, "dom"); - element.representedObject.panel.focusedDOMNode = element.representedObject.node; - resultsContainer.scrollToElement(element.listItemElement); - } - - for (var i = 0; i < files.length; ++i) { - var file = files[i]; - - var fileItem = new TreeElement(file.resource.displayName, {}, true); - fileItem.expanded = true; - fileItem.selectable = false; - this.searchResultsTree.appendChild(fileItem); - - if (file.sourceResults && file.sourceResults.length) { - for (var j = 0; j < file.sourceResults.length; ++j) { - var range = file.sourceResults[j]; - - var line = range.startContainer; - while (line.parentNode && line.nodeName.toLowerCase() != "tr") - line = line.parentNode; - var lineRange = file.resource.panel.views.source.frameElement.contentDocument.createRange(); - lineRange.selectNodeContents(line); - - // Don't include any error bubbles in the search result - var end = line.lastChild.lastChild; - if (end.nodeName.toLowerCase() == "div" && end.hasStyleClass("webkit-html-message-bubble")) { - while (end && end.nodeName.toLowerCase() == "div" && end.hasStyleClass("webkit-html-message-bubble")) - end = end.previousSibling; - lineRange.setEndAfter(end); - } - - var beforeRange = file.resource.panel.views.source.frameElement.contentDocument.createRange(); - beforeRange.setStart(lineRange.startContainer, lineRange.startOffset); - beforeRange.setEnd(range.startContainer, range.startOffset); - - var afterRange = file.resource.panel.views.source.frameElement.contentDocument.createRange(); - afterRange.setStart(range.endContainer, range.endOffset); - afterRange.setEnd(lineRange.endContainer, lineRange.endOffset); - - var beforeText = beforeRange.toString().trimLeadingWhitespace(); - var text = range.toString(); - var afterText = afterRange.toString().trimTrailingWhitespace(); - - var length = beforeText.length + text.length + afterText.length; - if (length > Preferences.maxTextSearchResultLength) { - var beforeAfterLength = (Preferences.maxTextSearchResultLength - text.length) / 2; - if (beforeText.length > beforeAfterLength) - beforeText = "\u2026" + beforeText.substr(-beforeAfterLength); - if (afterText.length > beforeAfterLength) - afterText = afterText.substr(0, beforeAfterLength) + "\u2026"; - } - - var title = "<div class=\"selection selected\"></div>"; - if (j == 0) - title += "<div class=\"search-results-section\">" + WebInspector.UIString("Source") + "</div>"; - title += beforeText.escapeHTML() + "<span class=\"search-matched-string\">" + text.escapeHTML() + "</span>" + afterText.escapeHTML(); - var item = new TreeElement(title, {panel: file.resource.panel, line: line, range: range}, false); - item.onselect = sourceResultSelected; - fileItem.appendChild(item); - } - } - - if (file.domResults.length) { - for (var j = 0; j < file.domResults.length; ++j) { - var node = file.domResults[j]; - var title = "<div class=\"selection selected\"></div>"; - if (j == 0) - title += "<div class=\"search-results-section\">" + WebInspector.UIString("DOM") + "</div>"; - title += nodeTitleInfo.call(node).title; - var item = new TreeElement(title, {panel: file.resource.panel, node: node}, false); - item.onselect = domResultSelected; - fileItem.appendChild(item); - } - } - } -} - -WebInspector.navigateToResource = function(resource) -{ - this.navigateToPanel(resource.panel); -} - -WebInspector.navigateToPanel = function(panel, view, fromBackForwardAction) -{ - if (this.currentPanel === panel) { - if (panel && view) - panel.currentView = view; - return; - } - - if (!fromBackForwardAction) { - var oldIndex = this.currentBackForwardIndex; - if (oldIndex >= 0) - this.backForwardList.splice(oldIndex + 1, this.backForwardList.length - oldIndex); - this.currentBackForwardIndex++; - this.backForwardList.push(panel); - } - - this.currentPanel = panel; - if (panel && view) - panel.currentView = view; -} - -WebInspector.UIString = function(string) -{ - if (string in this.localizedStrings) - string = this.localizedStrings[string]; - else { - if (!(string in this.missingLocalizedStrings)) { - console.error("Localized string \"" + string + "\" not found."); - this.missingLocalizedStrings[string] = true; - } - - if (Preferences.showMissingLocalizedStrings) - string += " (not localized)"; - } - - return String.vsprintf(string, Array.prototype.slice.call(arguments, 1)); -} - -WebInspector.StatusTreeElement = function(title, iconClass, panel) -{ - TreeElement.call(this, "<span class=\"title only\">" + title + "</span><span class=\"icon " + iconClass + "\"></span>", null, false); - this.panel = panel; -} - -WebInspector.StatusTreeElement.prototype = { - onselect: function() - { - var selectedElement = WebInspector.fileOutline.selectedTreeElement; - if (selectedElement) - selectedElement.deselect(); - if (this.panel) - WebInspector.navigateToPanel(this.panel); - }, - - ondeselect: function() - { - if (this.panel) - this.panel.hide(); - } -} - -WebInspector.StatusTreeElement.prototype.__proto__ = TreeElement.prototype; - -WebInspector.ConsoleStatusTreeElement = function(panel) -{ - WebInspector.StatusTreeElement.call(this, WebInspector.UIString("Console"), "console", panel); -} - -WebInspector.ConsoleStatusTreeElement.prototype = { - get warnings() - { - if (!("_warnings" in this)) - this._warnings = 0; - - return this._warnings; - }, - - set warnings(x) - { - if (this._warnings === x) - return; - - this._warnings = x; - - this._updateTitle(); - }, - - get errors() - { - if (!("_errors" in this)) - this._errors = 0; - - return this._errors; - }, - - set errors(x) - { - if (this._errors === x) - return; - - this._errors = x; - - this._updateTitle(); - }, - - _updateTitle: function() - { - var title = "<span class=\"title"; - if (!this.warnings && !this.errors) - title += " only"; - title += "\">" + WebInspector.UIString("Console") + "</span><span class=\"icon console\"></span>"; - - if (this.warnings || this.errors) { - title += "<span class=\"info\">"; - if (this.errors) { - title += this.errors + " error"; - if (this.errors > 1) - title += "s"; - } - if (this.warnings) { - if (this.errors) - title += ", "; - title += this.warnings + " warning"; - if (this.warnings > 1) - title += "s"; - } - title += "</span>"; - } - - this.title = title; - } -} - -WebInspector.ConsoleStatusTreeElement.prototype.__proto__ = WebInspector.StatusTreeElement.prototype; - -// This table maps MIME types to the Resource.Types which are valid for them. -// The following line: -// "text/html": {0: 1}, -// means that text/html is a valid MIME type for resources that have type -// WebInspector.Resource.Type.Document (which has a value of 0). -WebInspector.MIMETypes = { - "text/html": {0: true}, - "text/xml": {0: true}, - "text/plain": {0: true}, - "application/xhtml+xml": {0: true}, - "text/css": {1: true}, - "text/xsl": {1: true}, - "image/jpeg": {2: true}, - "image/png": {2: true}, - "image/gif": {2: true}, - "image/bmp": {2: true}, - "image/x-icon": {2: true}, - "image/x-xbitmap": {2: true}, - "font/ttf": {3: true}, - "font/opentype": {3: true}, - "application/x-font-type1": {3: true}, - "application/x-font-ttf": {3: true}, - "application/x-truetype-font": {3: true}, - "text/javascript": {4: true}, - "text/ecmascript": {4: true}, - "application/javascript": {4: true}, - "application/ecmascript": {4: true}, - "application/x-javascript": {4: true}, - "text/javascript1.1": {4: true}, - "text/javascript1.2": {4: true}, - "text/javascript1.3": {4: true}, - "text/jscript": {4: true}, - "text/livescript": {4: true}, -} diff --git a/WebCore/page/inspector/treeoutline.js b/WebCore/page/inspector/treeoutline.js deleted file mode 100644 index 228136a..0000000 --- a/WebCore/page/inspector/treeoutline.js +++ /dev/null @@ -1,728 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -function TreeOutline(listNode) -{ - this.children = []; - this.selectedTreeElement = null; - this._childrenListNode = listNode; - this._childrenListNode.removeChildren(); - this._knownTreeElements = []; - this._treeElementsExpandedState = []; - this.expandTreeElementsWhenArrowing = false; - this.root = true; - this.hasChildren = false; - this.expanded = true; - this.selected = false; - this.treeOutline = this; -} - -TreeOutline._knownTreeElementNextIdentifier = 1; - -TreeOutline._appendChild = function(child) -{ - if (!child) - throw("child can't be undefined or null"); - - var lastChild = this.children[this.children.length - 1]; - if (lastChild) { - lastChild.nextSibling = child; - child.previousSibling = lastChild; - } else { - child.previousSibling = null; - child.nextSibling = null; - } - - this.children.push(child); - this.hasChildren = true; - child.parent = this; - child.treeOutline = this.treeOutline; - child.treeOutline._rememberTreeElement(child); - - var current = child.children[0]; - while (current) { - current.treeOutline = this.treeOutline; - current.treeOutline._rememberTreeElement(current); - current = current.traverseNextTreeElement(false, child, true); - } - - if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined) - child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier]; - - if (!this._childrenListNode) { - this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol"); - this._childrenListNode.parentTreeElement = this; - this._childrenListNode.addStyleClass("children"); - if (this.hidden) - this._childrenListNode.addStyleClass("hidden"); - } - - child._attach(); -} - -TreeOutline._insertChild = function(child, index) -{ - if (!child) - throw("child can't be undefined or null"); - - var previousChild = (index > 0 ? this.children[index - 1] : null); - if (previousChild) { - previousChild.nextSibling = child; - child.previousSibling = previousChild; - } else { - child.previousSibling = null; - } - - var nextChild = this.children[index]; - if (nextChild) { - nextChild.previousSibling = child; - child.nextSibling = nextChild; - } else { - child.nextSibling = null; - } - - this.children.splice(index, 0, child); - this.hasChildren = true; - child.parent = this; - child.treeOutline = this.treeOutline; - child.treeOutline._rememberTreeElement(child); - - var current = child.children[0]; - while (current) { - current.treeOutline = this.treeOutline; - current.treeOutline._rememberTreeElement(current); - current = current.traverseNextTreeElement(false, child, true); - } - - if (child.hasChildren && child.treeOutline._treeElementsExpandedState[child.identifier] !== undefined) - child.expanded = child.treeOutline._treeElementsExpandedState[child.identifier]; - - if (!this._childrenListNode) { - this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol"); - this._childrenListNode.parentTreeElement = this; - this._childrenListNode.addStyleClass("children"); - if (this.hidden) - this._childrenListNode.addStyleClass("hidden"); - } - - child._attach(); -} - -TreeOutline._removeChild = function(child) -{ - if (!child) - throw("child can't be undefined or null"); - - for (var i = 0; i < this.children.length; ++i) { - if (this.children[i] === child) { - this.children.splice(i, 1); - break; - } - } - - child.deselect(); - - if (child.previousSibling) - child.previousSibling.nextSibling = child.nextSibling; - if (child.nextSibling) - child.nextSibling.previousSibling = child.previousSibling; - - if (child.treeOutline) - child.treeOutline._forgetTreeElement(child); - child._detach(); - child.treeOutline = null; - child.parent = null; - child.nextSibling = null; - child.previousSibling = null; -} - -TreeOutline._removeChildren = function() -{ - for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i]; - child.deselect(); - if (child.treeOutline) - child.treeOutline._forgetTreeElement(child); - child._detach(); - child.treeOutline = null; - child.parent = null; - child.nextSibling = null; - child.previousSibling = null; - } - - this.children = []; - - if (this._childrenListNode) - this._childrenListNode.offsetTop; // force layout -} - -TreeOutline._removeChildrenRecursive = function() -{ - var childrenToRemove = this.children; - - var child = this.children[0]; - while (child) { - if (child.children.length) - childrenToRemove = childrenToRemove.concat(child.children); - child = child.traverseNextTreeElement(false, this, true); - } - - for (var i = 0; i < childrenToRemove.length; ++i) { - var child = childrenToRemove[i]; - child.deselect(); - if (child.treeOutline) - child.treeOutline._forgetTreeElement(child); - child._detach(); - child.children = []; - child.treeOutline = null; - child.parent = null; - child.nextSibling = null; - child.previousSibling = null; - } - - this.children = []; -} - -TreeOutline.prototype._rememberTreeElement = function(element) -{ - if (!this._knownTreeElements[element.identifier]) - this._knownTreeElements[element.identifier] = []; - - // check if the element is already known - var elements = this._knownTreeElements[element.identifier]; - for (var i = 0; i < elements.length; ++i) - if (elements[i] === element) - return; - - // add the element - elements.push(element); -} - -TreeOutline.prototype._forgetTreeElement = function(element) -{ - if (!this._knownTreeElements[element.identifier]) - return; - - var elements = this._knownTreeElements[element.identifier]; - for (var i = 0; i < elements.length; ++i) { - if (elements[i] === element) { - elements.splice(i, 1); - break; - } - } -} - -TreeOutline.prototype.findTreeElement = function(representedObject, isAncestor, getParent) -{ - if (!representedObject) - return null; - - if ("__treeElementIdentifier" in representedObject) { - var elements = this._knownTreeElements[representedObject.__treeElementIdentifier]; - if (elements) { - for (var i = 0; i < elements.length; ++i) - if (elements[i].representedObject === representedObject) - return elements[i]; - } - } - - if (!isAncestor || !(isAncestor instanceof Function) || !getParent || !(getParent instanceof Function)) - return null; - - var item; - var found = false; - for (var i = 0; i < this.children.length; ++i) { - item = this.children[i]; - if (item.representedObject === representedObject || isAncestor(item.representedObject, representedObject)) { - found = true; - break; - } - } - - if (!found) - return null; - - var ancestors = []; - var currentObject = representedObject; - while (currentObject) { - ancestors.unshift(currentObject); - if (currentObject === item.representedObject) - break; - currentObject = getParent(currentObject); - } - - for (var i = 0; i < ancestors.length; ++i) { - item = this.findTreeElement(ancestors[i], isAncestor, getParent); - if (ancestors[i] !== representedObject && item && item.onpopulate) - item.onpopulate(item); - } - - return item; -} - -TreeOutline.prototype.handleKeyEvent = function(event) -{ - if (!this.selectedTreeElement || event.shiftKey || event.metaKey || event.ctrlKey) - return false; - - var handled = false; - var nextSelectedElement; - if (event.keyIdentifier === "Up" && !event.altKey) { - nextSelectedElement = this.selectedTreeElement.traversePreviousTreeElement(true); - while (nextSelectedElement && !nextSelectedElement.selectable) - nextSelectedElement = nextSelectedElement.traversePreviousTreeElement(!this.expandTreeElementsWhenArrowing); - handled = nextSelectedElement ? true : false; - } else if (event.keyIdentifier === "Down" && !event.altKey) { - nextSelectedElement = this.selectedTreeElement.traverseNextTreeElement(true); - while (nextSelectedElement && !nextSelectedElement.selectable) - nextSelectedElement = nextSelectedElement.traverseNextTreeElement(!this.expandTreeElementsWhenArrowing); - handled = nextSelectedElement ? true : false; - } else if (event.keyIdentifier === "Left") { - if (this.selectedTreeElement.expanded) { - if (event.altKey) - this.selectedTreeElement.collapseRecursively(); - else - this.selectedTreeElement.collapse(); - handled = true; - } else if (this.selectedTreeElement.parent && !this.selectedTreeElement.parent.root) { - handled = true; - if (this.selectedTreeElement.parent.selectable) { - nextSelectedElement = this.selectedTreeElement.parent; - handled = nextSelectedElement ? true : false; - } else if (this.selectedTreeElement.parent) - this.selectedTreeElement.parent.collapse(); - } - } else if (event.keyIdentifier === "Right") { - if (!this.selectedTreeElement.revealed()) { - this.selectedTreeElement.reveal(); - handled = true; - } else if (this.selectedTreeElement.hasChildren) { - handled = true; - if (this.selectedTreeElement.expanded) { - nextSelectedElement = this.selectedTreeElement.children[0]; - handled = nextSelectedElement ? true : false; - } else { - if (event.altKey) - this.selectedTreeElement.expandRecursively(); - else - this.selectedTreeElement.expand(); - } - } - } - - if (nextSelectedElement) { - nextSelectedElement.reveal(); - nextSelectedElement.select(); - } - - if (handled) { - event.preventDefault(); - event.stopPropagation(); - } - - return handled; -} - -TreeOutline.prototype.expand = function() -{ - // this is the root, do nothing -} - -TreeOutline.prototype.collapse = function() -{ - // this is the root, do nothing -} - -TreeOutline.prototype.revealed = function() -{ - return true; -} - -TreeOutline.prototype.reveal = function() -{ - // this is the root, do nothing -} - -TreeOutline.prototype.appendChild = TreeOutline._appendChild; -TreeOutline.prototype.insertChild = TreeOutline._insertChild; -TreeOutline.prototype.removeChild = TreeOutline._removeChild; -TreeOutline.prototype.removeChildren = TreeOutline._removeChildren; -TreeOutline.prototype.removeChildrenRecursive = TreeOutline._removeChildrenRecursive; - -function TreeElement(title, representedObject, hasChildren) -{ - this._title = title; - this.representedObject = (representedObject || {}); - - if (this.representedObject.__treeElementIdentifier) - this.identifier = this.representedObject.__treeElementIdentifier; - else { - this.identifier = TreeOutline._knownTreeElementNextIdentifier++; - this.representedObject.__treeElementIdentifier = this.identifier; - } - - this._hidden = false; - this.expanded = false; - this.selected = false; - this.hasChildren = hasChildren; - this.children = []; - this.treeOutline = null; - this.parent = null; - this.previousSibling = null; - this.nextSibling = null; - this._listItemNode = null; -} - -TreeElement.prototype = { - selectable: true, - arrowToggleWidth: 10, - - get listItemElement() { - return this._listItemNode; - }, - - get childrenListElement() { - return this._childrenListNode; - }, - - get title() { - return this._title; - }, - - set title(x) { - this._title = x; - if (this._listItemNode) - this._listItemNode.innerHTML = x; - }, - - get tooltip() { - return this._tooltip; - }, - - set tooltip(x) { - this._tooltip = x; - if (this._listItemNode) - this._listItemNode.title = x ? x : ""; - }, - - get hidden() { - return this._hidden; - }, - - set hidden(x) { - if (this._hidden === x) - return; - - this._hidden = x; - - if (x) { - if (this._listItemNode) - this._listItemNode.addStyleClass("hidden"); - if (this._childrenListNode) - this._childrenListNode.addStyleClass("hidden"); - } else { - if (this._listItemNode) - this._listItemNode.removeStyleClass("hidden"); - if (this._childrenListNode) - this._childrenListNode.removeStyleClass("hidden"); - } - } -} - -TreeElement.prototype.appendChild = TreeOutline._appendChild; -TreeElement.prototype.insertChild = TreeOutline._insertChild; -TreeElement.prototype.removeChild = TreeOutline._removeChild; -TreeElement.prototype.removeChildren = TreeOutline._removeChildren; -TreeElement.prototype.removeChildrenRecursive = TreeOutline._removeChildrenRecursive; - -TreeElement.prototype._attach = function() -{ - if (!this._listItemNode || this.parent.refreshChildren) { - if (this._listItemNode && this._listItemNode.parentNode) - this._listItemNode.parentNode.removeChild(this._listItemNode); - - this._listItemNode = this.treeOutline._childrenListNode.ownerDocument.createElement("li"); - this._listItemNode.treeElement = this; - this._listItemNode.innerHTML = this._title; - this._listItemNode.title = this._tooltip ? this._tooltip : ""; - - if (this.hidden) - this._listItemNode.addStyleClass("hidden"); - if (this.hasChildren) - this._listItemNode.addStyleClass("parent"); - if (this.expanded) - this._listItemNode.addStyleClass("expanded"); - if (this.selected) - this._listItemNode.addStyleClass("selected"); - - this._listItemNode.addEventListener("mousedown", TreeElement.treeElementSelected, false); - this._listItemNode.addEventListener("click", TreeElement.treeElementToggled, false); - this._listItemNode.addEventListener("dblclick", TreeElement.treeElementDoubleClicked, false); - - if (this.onattach) - this.onattach(this); - } - - this.parent._childrenListNode.insertBefore(this._listItemNode, (this.nextSibling ? this.nextSibling._listItemNode : null)); - if (this._childrenListNode) - this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling); - if (this.selected) - this.select(); - if (this.expanded) - this.expand(); -} - -TreeElement.prototype._detach = function() -{ - if (this._listItemNode && this._listItemNode.parentNode) - this._listItemNode.parentNode.removeChild(this._listItemNode); - if (this._childrenListNode && this._childrenListNode.parentNode) - this._childrenListNode.parentNode.removeChild(this._childrenListNode); -} - -TreeElement.treeElementSelected = function(event) -{ - var element = event.currentTarget; - if (!element || !element.treeElement || !element.treeElement.selectable) - return; - - if (event.offsetX > element.treeElement.arrowToggleWidth || !element.treeElement.hasChildren) - element.treeElement.select(); -} - -TreeElement.treeElementToggled = function(event) -{ - var element = event.currentTarget; - if (!element || !element.treeElement) - return; - - if (event.offsetX <= element.treeElement.arrowToggleWidth && element.treeElement.hasChildren) { - if (element.treeElement.expanded) { - if (event.altKey) - element.treeElement.collapseRecursively(); - else - element.treeElement.collapse(); - } else { - if (event.altKey) - element.treeElement.expandRecursively(); - else - element.treeElement.expand(); - } - } -} - -TreeElement.treeElementDoubleClicked = function(event) -{ - var element = event.currentTarget; - if (!element || !element.treeElement) - return; - - if (element.treeElement.ondblclick) - element.treeElement.ondblclick(element.treeElement, event); - else if (element.treeElement.hasChildren && !element.treeElement.expanded) - element.treeElement.expand(); -} - -TreeElement.prototype.collapse = function() -{ - if (this._listItemNode) - this._listItemNode.removeStyleClass("expanded"); - if (this._childrenListNode) - this._childrenListNode.removeStyleClass("expanded"); - - this.expanded = false; - if (this.treeOutline) - this.treeOutline._treeElementsExpandedState[this.identifier] = true; - - if (this.oncollapse) - this.oncollapse(this); -} - -TreeElement.prototype.collapseRecursively = function() -{ - var item = this; - while (item) { - if (item.expanded) - item.collapse(); - item = item.traverseNextTreeElement(false, this, true); - } -} - -TreeElement.prototype.expand = function() -{ - if (!this.hasChildren || (this.expanded && !this.refreshChildren && this._childrenListNode)) - return; - - if (!this._childrenListNode || this.refreshChildren) { - if (this._childrenListNode && this._childrenListNode.parentNode) - this._childrenListNode.parentNode.removeChild(this._childrenListNode); - - this._childrenListNode = this.treeOutline._childrenListNode.ownerDocument.createElement("ol"); - this._childrenListNode.parentTreeElement = this; - this._childrenListNode.addStyleClass("children"); - - if (this.hidden) - this._childrenListNode.addStyleClass("hidden"); - - if (this.onpopulate) - this.onpopulate(this); - - for (var i = 0; i < this.children.length; ++i) - this.children[i]._attach(); - - delete this.refreshChildren; - } - - if (this._listItemNode) { - this._listItemNode.addStyleClass("expanded"); - if (this._childrenListNode.parentNode != this._listItemNode.parentNode) - this.parent._childrenListNode.insertBefore(this._childrenListNode, this._listItemNode.nextSibling); - } - - if (this._childrenListNode) - this._childrenListNode.addStyleClass("expanded"); - - this.expanded = true; - if (this.treeOutline) - this.treeOutline._treeElementsExpandedState[this.identifier] = true; - - if (this.onexpand) - this.onexpand(this); -} - -TreeElement.prototype.expandRecursively = function() -{ - var item = this; - while (item) { - item.expand(); - item = item.traverseNextTreeElement(false, this); - } -} - -TreeElement.prototype.reveal = function() -{ - var currentAncestor = this.parent; - while (currentAncestor && !currentAncestor.root) { - if (!currentAncestor.expanded) - currentAncestor.expand(); - currentAncestor = currentAncestor.parent; - } - - if (this.onreveal) - this.onreveal(this); -} - -TreeElement.prototype.revealed = function() -{ - var currentAncestor = this.parent; - while (currentAncestor && !currentAncestor.root) { - if (!currentAncestor.expanded) - return false; - currentAncestor = currentAncestor.parent; - } - - return true; -} - -TreeElement.prototype.select = function(supressOnSelect) -{ - if (!this.treeOutline || !this.selectable || this.selected) - return; - - if (this.treeOutline.selectedTreeElement) - this.treeOutline.selectedTreeElement.deselect(); - - this.selected = true; - this.treeOutline.selectedTreeElement = this; - if (this._listItemNode) - this._listItemNode.addStyleClass("selected"); - - if (this.onselect && !supressOnSelect) - this.onselect(this); -} - -TreeElement.prototype.deselect = function(supressOnDeselect) -{ - if (!this.treeOutline || this.treeOutline.selectedTreeElement !== this || !this.selected) - return; - - this.selected = false; - this.treeOutline.selectedTreeElement = null; - if (this._listItemNode) - this._listItemNode.removeStyleClass("selected"); - - if (this.ondeselect && !supressOnDeselect) - this.ondeselect(this); -} - -TreeElement.prototype.traverseNextTreeElement = function(skipHidden, stayWithin, dontPopulate) -{ - if (!dontPopulate && this.hasChildren && this.onpopulate) - this.onpopulate(this); - - var element = skipHidden ? (this.revealed() ? this.children[0] : null) : this.children[0]; - if (element && (!skipHidden || (skipHidden && this.expanded))) - return element; - - if (this === stayWithin) - return null; - - element = skipHidden ? (this.revealed() ? this.nextSibling : null) : this.nextSibling; - if (element) - return element; - - element = this; - while (element && !element.root && !(skipHidden ? (element.revealed() ? element.nextSibling : null) : element.nextSibling) && element.parent !== stayWithin) - element = element.parent; - - if (!element) - return null; - - return (skipHidden ? (element.revealed() ? element.nextSibling : null) : element.nextSibling); -} - -TreeElement.prototype.traversePreviousTreeElement = function(skipHidden, dontPopulate) -{ - var element = skipHidden ? (this.revealed() ? this.previousSibling : null) : this.previousSibling; - if (!dontPopulate && element && element.hasChildren && element.onpopulate) - element.onpopulate(element); - - while (element && (skipHidden ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1])) { - if (!dontPopulate && element.hasChildren && element.onpopulate) - element.onpopulate(element); - element = (skipHidden ? (element.revealed() && element.expanded ? element.children[element.children.length - 1] : null) : element.children[element.children.length - 1]); - } - - if (element) - return element; - - if (!this.parent || this.parent.root) - return null; - - return this.parent; -} diff --git a/WebCore/page/inspector/utilities.js b/WebCore/page/inspector/utilities.js deleted file mode 100644 index a92fe4d..0000000 --- a/WebCore/page/inspector/utilities.js +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -Object.type = function(obj) -{ - if (obj === null) - return "null"; - - var type = typeof obj; - if (type !== "object" && type !== "function") - return type; - - if (obj instanceof String) - return "string"; - if (obj instanceof Array) - return "array"; - if (obj instanceof Boolean) - return "boolean"; - if (obj instanceof Number) - return "number"; - if (obj instanceof Date) - return "date"; - if (obj instanceof RegExp) - return "regexp"; - if (obj instanceof Error) - return "error"; - return type; -} - -Object.describe = function(obj, abbreviated) -{ - switch (Object.type(obj)) { - case "object": - return Object.prototype.toString.call(obj).replace(/^\[object (.*)\]$/i, "$1"); - case "array": - return "[" + obj.toString() + "]"; - case "string": - if (obj.length > 100) - return "\"" + obj.substring(0, 100) + "\u2026\""; - return "\"" + obj + "\""; - case "function": - var objectText = String(obj); - if (!/^function /.test(objectText)) - objectText = (type2 == "object") ? type1 : type2; - else if (abbreviated) - objectText = /.*/.exec(obj)[0].replace(/ +$/g, ""); - return objectText; - case "regexp": - return String(obj).replace(/([\\\/])/g, "\\$1").replace(/\\(\/[gim]*)$/, "$1").substring(1); - default: - return String(obj); - } -} - -Object.sortedProperties = function(obj) -{ - var properties = []; - for (var prop in obj) - properties.push(prop); - properties.sort(); - return properties; -} - -Function.prototype.bind = function(thisObject) -{ - var func = this; - var args = Array.prototype.slice.call(arguments, 1); - return function() { return func.apply(thisObject, args.concat(Array.prototype.slice.call(arguments, 0))) }; -} - -Element.prototype.removeStyleClass = function(className) -{ - // Test for the simple case before using a RegExp. - if (this.className === className) { - this.className = ""; - return; - } - - var regex = new RegExp("(^|\\s+)" + className.escapeForRegExp() + "($|\\s+)"); - if (regex.test(this.className)) - this.className = this.className.replace(regex, " "); -} - -Element.prototype.addStyleClass = function(className) -{ - if (className && !this.hasStyleClass(className)) - this.className += (this.className.length ? " " + className : className); -} - -Element.prototype.hasStyleClass = function(className) -{ - if (!className) - return false; - // Test for the simple case before using a RegExp. - if (this.className === className) - return true; - var regex = new RegExp("(^|\\s)" + className.escapeForRegExp() + "($|\\s)"); - return regex.test(this.className); -} - -Element.prototype.scrollToElement = function(element) -{ - if (!element || !this.isAncestor(element)) - return; - - var offsetTop = 0; - var current = element - while (current && current !== this) { - offsetTop += current.offsetTop; - current = current.offsetParent; - } - - if (this.scrollTop > offsetTop) - this.scrollTop = offsetTop; - else if ((this.scrollTop + this.offsetHeight) < (offsetTop + element.offsetHeight)) - this.scrollTop = offsetTop - this.offsetHeight + element.offsetHeight; -} - -Node.prototype.firstParentOrSelfWithNodeName = function(nodeName) -{ - for (var node = this; node && (node !== document); node = node.parentNode) - if (node.nodeName.toLowerCase() === nodeName.toLowerCase()) - return node; - return null; -} - -Node.prototype.firstParentOrSelfWithClass = function(className) -{ - for (var node = this; node && (node !== document); node = node.parentNode) - if (node.nodeType === Node.ELEMENT_NODE && node.hasStyleClass(className)) - return node; - return null; -} - -Node.prototype.firstParentWithClass = function(className) -{ - if (!this.parentNode) - return null; - return this.parentNode.firstParentOrSelfWithClass(className); -} - -Element.prototype.query = function(query) -{ - return document.evaluate(query, this, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; -} - -Element.prototype.removeChildren = function() -{ - while (this.firstChild) - this.removeChild(this.firstChild); -} - -Element.prototype.firstChildSkippingWhitespace = firstChildSkippingWhitespace; -Element.prototype.lastChildSkippingWhitespace = lastChildSkippingWhitespace; - -Node.prototype.isWhitespace = isNodeWhitespace; -Node.prototype.nodeTypeName = nodeTypeName; -Node.prototype.displayName = nodeDisplayName; -Node.prototype.contentPreview = nodeContentPreview; -Node.prototype.isAncestor = isAncestorNode; -Node.prototype.isDescendant = isDescendantNode; -Node.prototype.firstCommonAncestor = firstCommonNodeAncestor; -Node.prototype.nextSiblingSkippingWhitespace = nextSiblingSkippingWhitespace; -Node.prototype.previousSiblingSkippingWhitespace = previousSiblingSkippingWhitespace; -Node.prototype.traverseNextNode = traverseNextNode; -Node.prototype.traversePreviousNode = traversePreviousNode; -Node.prototype.onlyTextChild = onlyTextChild; -Node.prototype.titleInfo = nodeTitleInfo; - -String.prototype.hasSubstring = function(string, caseInsensitive) -{ - if (!caseInsensitive) - return this.indexOf(string) !== -1; - return this.match(new RegExp(string.escapeForRegExp(), "i")); -} - -String.prototype.escapeCharacters = function(chars) -{ - var foundChar = false; - for (var i = 0; i < chars.length; ++i) { - if (this.indexOf(chars.charAt(i)) !== -1) { - foundChar = true; - break; - } - } - - if (!foundChar) - return this; - - var result = ""; - for (var i = 0; i < this.length; ++i) { - if (chars.indexOf(this.charAt(i)) !== -1) - result += "\\"; - result += this.charAt(i); - } - - return result; -} - -String.prototype.escapeForRegExp = function() -{ - return this.escapeCharacters("^[]{}()\\.$*+?|"); -} - -String.prototype.escapeHTML = function() -{ - return this.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); -} - -String.prototype.collapseWhitespace = function() -{ - return this.replace(/[\s\xA0]+/g, " "); -} - -String.prototype.trimLeadingWhitespace = function() -{ - return this.replace(/^[\s\xA0]+/g, ""); -} - -String.prototype.trimTrailingWhitespace = function() -{ - return this.replace(/[\s\xA0]+$/g, ""); -} - -String.prototype.trimWhitespace = function() -{ - return this.replace(/^[\s\xA0]+|[\s\xA0]+$/g, ""); -} - -String.prototype.trimURL = function(baseURLDomain) -{ - var result = this.replace(new RegExp("^http[s]?:\/\/", "i"), ""); - if (baseURLDomain) - result = result.replace(new RegExp("^" + baseURLDomain.escapeForRegExp(), "i"), ""); - return result; -} - -CSSStyleDeclaration.prototype.getShorthandValue = function(shorthandProperty) -{ - var value = this.getPropertyValue(shorthandProperty); - if (!value) { - // Some shorthands (like border) return a null value, so compute a shorthand value. - // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15823 is fixed. - - var foundProperties = {}; - for (var i = 0; i < this.length; ++i) { - var individualProperty = this[i]; - if (individualProperty in foundProperties || this.getPropertyShorthand(individualProperty) !== shorthandProperty) - continue; - - var individualValue = this.getPropertyValue(individualProperty); - if (this.isPropertyImplicit(individualProperty) || individualValue === "initial") - continue; - - foundProperties[individualProperty] = true; - - if (!value) - value = ""; - else if (value.length) - value += " "; - value += individualValue; - } - } - return value; -} - -CSSStyleDeclaration.prototype.getShorthandPriority = function(shorthandProperty) -{ - var priority = this.getPropertyPriority(shorthandProperty); - if (!priority) { - for (var i = 0; i < this.length; ++i) { - var individualProperty = this[i]; - if (this.getPropertyShorthand(individualProperty) !== shorthandProperty) - continue; - priority = this.getPropertyPriority(individualProperty); - break; - } - } - return priority; -} - -CSSStyleDeclaration.prototype.getLonghandProperties = function(shorthandProperty) -{ - var properties = []; - var foundProperties = {}; - - for (var i = 0; i < this.length; ++i) { - var individualProperty = this[i]; - if (individualProperty in foundProperties || this.getPropertyShorthand(individualProperty) !== shorthandProperty) - continue; - foundProperties[individualProperty] = true; - properties.push(individualProperty); - } - - return properties; -} - -CSSStyleDeclaration.prototype.getUniqueProperties = function() -{ - var properties = []; - var foundProperties = {}; - - for (var i = 0; i < this.length; ++i) { - var property = this[i]; - if (property in foundProperties) - continue; - foundProperties[property] = true; - properties.push(property); - } - - return properties; -} - -function isNodeWhitespace() -{ - if (!this || this.nodeType !== Node.TEXT_NODE) - return false; - if (!this.nodeValue.length) - return true; - return this.nodeValue.match(/^[\s\xA0]+$/); -} - -function nodeTypeName() -{ - if (!this) - return "(unknown)"; - - switch (this.nodeType) { - case Node.ELEMENT_NODE: return "Element"; - case Node.ATTRIBUTE_NODE: return "Attribute"; - case Node.TEXT_NODE: return "Text"; - case Node.CDATA_SECTION_NODE: return "Character Data"; - case Node.ENTITY_REFERENCE_NODE: return "Entity Reference"; - case Node.ENTITY_NODE: return "Entity"; - case Node.PROCESSING_INSTRUCTION_NODE: return "Processing Instruction"; - case Node.COMMENT_NODE: return "Comment"; - case Node.DOCUMENT_NODE: return "Document"; - case Node.DOCUMENT_TYPE_NODE: return "Document Type"; - case Node.DOCUMENT_FRAGMENT_NODE: return "Document Fragment"; - case Node.NOTATION_NODE: return "Notation"; - } - - return "(unknown)"; -} - -function nodeDisplayName() -{ - if (!this) - return ""; - - switch (this.nodeType) { - case Node.DOCUMENT_NODE: - return "Document"; - - case Node.ELEMENT_NODE: - var name = "<" + this.nodeName.toLowerCase(); - - if (this.hasAttributes()) { - var value = this.getAttribute("id"); - if (value) - name += " id=\"" + value + "\""; - value = this.getAttribute("class"); - if (value) - name += " class=\"" + value + "\""; - if (this.nodeName.toLowerCase() === "a") { - value = this.getAttribute("name"); - if (value) - name += " name=\"" + value + "\""; - value = this.getAttribute("href"); - if (value) - name += " href=\"" + value + "\""; - } else if (this.nodeName.toLowerCase() === "img") { - value = this.getAttribute("src"); - if (value) - name += " src=\"" + value + "\""; - } else if (this.nodeName.toLowerCase() === "iframe") { - value = this.getAttribute("src"); - if (value) - name += " src=\"" + value + "\""; - } else if (this.nodeName.toLowerCase() === "input") { - value = this.getAttribute("name"); - if (value) - name += " name=\"" + value + "\""; - value = this.getAttribute("type"); - if (value) - name += " type=\"" + value + "\""; - } else if (this.nodeName.toLowerCase() === "form") { - value = this.getAttribute("action"); - if (value) - name += " action=\"" + value + "\""; - } - } - - return name + ">"; - - case Node.TEXT_NODE: - if (isNodeWhitespace.call(this)) - return "(whitespace)"; - return "\"" + this.nodeValue + "\""; - - case Node.COMMENT_NODE: - return "<!--" + this.nodeValue + "-->"; - } - - return this.nodeName.toLowerCase().collapseWhitespace(); -} - -function nodeContentPreview() -{ - if (!this || !this.hasChildNodes || !this.hasChildNodes()) - return ""; - - var limit = 0; - var preview = ""; - - // always skip whitespace here - var currentNode = traverseNextNode.call(this, true, this); - while (currentNode) { - if (currentNode.nodeType === Node.TEXT_NODE) - preview += currentNode.nodeValue.escapeHTML(); - else - preview += nodeDisplayName.call(currentNode).escapeHTML(); - - currentNode = traverseNextNode.call(currentNode, true, this); - - if (++limit > 4) { - preview += "…"; // ellipsis - break; - } - } - - return preview.collapseWhitespace(); -} - -function isAncestorNode(ancestor) -{ - if (!this || !ancestor) - return false; - - var currentNode = ancestor.parentNode; - while (currentNode) { - if (this === currentNode) - return true; - currentNode = currentNode.parentNode; - } - - return false; -} - -function isDescendantNode(descendant) -{ - return isAncestorNode.call(descendant, this); -} - -function firstCommonNodeAncestor(node) -{ - if (!this || !node) - return; - - var node1 = this.parentNode; - var node2 = node.parentNode; - - if ((!node1 || !node2) || node1 !== node2) - return null; - - while (node1 && node2) { - if (!node1.parentNode || !node2.parentNode) - break; - if (node1 !== node2) - break; - - node1 = node1.parentNode; - node2 = node2.parentNode; - } - - return node1; -} - -function nextSiblingSkippingWhitespace() -{ - if (!this) - return; - var node = this.nextSibling; - while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node)) - node = node.nextSibling; - return node; -} - -function previousSiblingSkippingWhitespace() -{ - if (!this) - return; - var node = this.previousSibling; - while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node)) - node = node.previousSibling; - return node; -} - -function firstChildSkippingWhitespace() -{ - if (!this) - return; - var node = this.firstChild; - while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node)) - node = nextSiblingSkippingWhitespace.call(node); - return node; -} - -function lastChildSkippingWhitespace() -{ - if (!this) - return; - var node = this.lastChild; - while (node && node.nodeType === Node.TEXT_NODE && isNodeWhitespace.call(node)) - node = previousSiblingSkippingWhitespace.call(node); - return node; -} - -function traverseNextNode(skipWhitespace, stayWithin) -{ - if (!this) - return; - - var node = skipWhitespace ? firstChildSkippingWhitespace.call(this) : this.firstChild; - if (node) - return node; - - if (stayWithin && this === stayWithin) - return null; - - node = skipWhitespace ? nextSiblingSkippingWhitespace.call(this) : this.nextSibling; - if (node) - return node; - - node = this; - while (node && !(skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling) && (!stayWithin || !node.parentNode || node.parentNode !== stayWithin)) - node = node.parentNode; - if (!node) - return null; - - return skipWhitespace ? nextSiblingSkippingWhitespace.call(node) : node.nextSibling; -} - -function traversePreviousNode(skipWhitespace) -{ - if (!this) - return; - var node = skipWhitespace ? previousSiblingSkippingWhitespace.call(this) : this.previousSibling; - while (node && (skipWhitespace ? lastChildSkippingWhitespace.call(node) : node.lastChild) ) - node = skipWhitespace ? lastChildSkippingWhitespace.call(node) : node.lastChild; - if (node) - return node; - return this.parentNode; -} - -function onlyTextChild(ignoreWhitespace) -{ - if (!this) - return null; - - var firstChild = ignoreWhitespace ? firstChildSkippingWhitespace.call(this) : this.firstChild; - if (!firstChild || firstChild.nodeType !== Node.TEXT_NODE) - return null; - - var sibling = ignoreWhitespace ? nextSiblingSkippingWhitespace.call(firstChild) : firstChild.nextSibling; - return sibling ? null : firstChild; -} - -function nodeTitleInfo(hasChildren, linkify) -{ - var info = {title: "", hasChildren: hasChildren}; - - switch (this.nodeType) { - case Node.DOCUMENT_NODE: - info.title = "Document"; - break; - - case Node.ELEMENT_NODE: - info.title = "<span class=\"webkit-html-tag\"><" + this.nodeName.toLowerCase().escapeHTML(); - - if (this.hasAttributes()) { - for (var i = 0; i < this.attributes.length; ++i) { - var attr = this.attributes[i]; - var value = attr.value.escapeHTML(); - value = value.replace(/([\/;:\)\]\}])/g, "$1​"); - - info.title += " <span class=\"webkit-html-attribute-name\">" + attr.name.escapeHTML() + "</span>=​"; - - if (linkify && (attr.name === "src" || attr.name === "href")) - info.title += linkify(attr.value, value, "webkit-html-attribute-value", this.nodeName.toLowerCase() == "a"); - else - info.title += "<span class=\"webkit-html-attribute-value\">\"" + value + "\"</span>"; - } - } - info.title += "></span>​"; - - // If this element only has a single child that is a text node, - // just show that text and the closing tag inline rather than - // create a subtree for them - - var textChild = onlyTextChild.call(this, Preferences.ignoreWhitespace); - var showInlineText = textChild && textChild.textContent.length < Preferences.maxInlineTextChildLength; - - if (showInlineText) { - info.title += textChild.nodeValue.escapeHTML() + "​<span class=\"webkit-html-tag\"></" + this.nodeName.toLowerCase().escapeHTML() + "></span>"; - info.hasChildren = false; - } - break; - - case Node.TEXT_NODE: - if (isNodeWhitespace.call(this)) - info.title = "(whitespace)"; - else - info.title = "\"" + this.nodeValue.escapeHTML() + "\""; - break - - case Node.COMMENT_NODE: - info.title = "<span class=\"webkit-html-comment\"><!--" + this.nodeValue.escapeHTML() + "--></span>"; - break; - - default: - info.title = this.nodeName.toLowerCase().collapseWhitespace().escapeHTML(); - } - - return info; -} - -Number.secondsToString = function(seconds) -{ - var ms = seconds * 1000; - if (ms < 1000) - return Math.round(ms) + "ms"; - - if (seconds < 60) - return (Math.round(seconds * 100) / 100) + "s"; - - var minutes = seconds / 60; - if (minutes < 60) - return (Math.round(minutes * 10) / 10) + "min"; - - var hours = minutes / 60; - if (hours < 24) - return (Math.round(hours * 10) / 10) + "hrs"; - - var days = hours / 24; - return (Math.round(days * 10) / 10) + " days"; -} - -Number.bytesToString = function(bytes) -{ - if (bytes < 1024) - return bytes + "B"; - - var kilobytes = bytes / 1024; - if (kilobytes < 1024) - return (Math.round(kilobytes * 100) / 100) + "KB"; - - var megabytes = kilobytes / 1024; - return (Math.round(megabytes * 1000) / 1000) + "MB"; -} - -Number.constrain = function(num, min, max) -{ - if (num < min) - num = min; - else if (num > max) - num = max; - return num; -} - -HTMLTextAreaElement.prototype.moveCursorToEnd = function() -{ - var length = this.value.length; - this.setSelectionRange(length, length); -} - -String.sprintf = function(format) -{ - return String.vsprintf(format, Array.prototype.slice.call(arguments, 1)); -} - -String.vsprintf = function(format, substitutions) -{ - if (!format || !substitutions || !substitutions.length) - return format; - - var result = ""; - var substitutionIndex = 0; - - var index = 0; - for (var precentIndex = format.indexOf("%", index); precentIndex !== -1; precentIndex = format.indexOf("%", index)) { - result += format.substring(index, precentIndex); - index = precentIndex + 1; - - if (format[index] === "%") { - result += "%"; - ++index; - continue; - } - - if (!isNaN(format[index])) { - // The first character is a number, it might be a substitution index. - var number = parseInt(format.substring(index)); - while (!isNaN(format[index])) - ++index; - // If the number is greater than zero and ends with a "$", - // then this is a substitution index. - if (number > 0 && format[index] === "$") { - substitutionIndex = (number - 1); - ++index; - } - } - - var precision = -1; - if (format[index] === ".") { - // This is a precision specifier. If no digit follows the ".", - // then the precision should be zero. - ++index; - precision = parseInt(format.substring(index)); - if (isNaN(precision)) - precision = 0; - while (!isNaN(format[index])) - ++index; - } - - if (substitutionIndex >= substitutions.length) { - // If there are not enough substitutions for the current substitutionIndex - // just output the format specifier literally and move on. - console.error("String.vsprintf(\"" + format + "\", \"" + substitutions.join("\", \"") + "\"): not enough substitution arguments. Had " + substitutions.length + " but needed " + (substitutionIndex + 1) + ", so substitution was skipped."); - index = precentIndex + 1; - result += "%"; - continue; - } - - switch (format[index]) { - case "d": - var substitution = parseInt(substitutions[substitutionIndex]); - result += (!isNaN(substitution) ? substitution : 0); - break; - case "f": - var substitution = parseFloat(substitutions[substitutionIndex]); - if (substitution && precision > -1) - substitution = substitution.toFixed(precision); - result += (!isNaN(substitution) ? substitution : (precision > -1 ? Number(0).toFixed(precision) : 0)); - break; - default: - // Encountered an unsupported format character, treat as a string. - console.warn("String.vsprintf(\"" + format + "\", \"" + substitutions.join("\", \"") + "\"): unsupported format character \u201C" + format[index] + "\u201D. Treating as a string."); - // Fall through to treat this like a string. - case "s": - result += substitutions[substitutionIndex]; - break; - } - - ++substitutionIndex; - ++index; - } - - result += format.substring(index); - - return result; -} diff --git a/WebCore/page/mac/AXObjectCacheMac.mm b/WebCore/page/mac/AXObjectCacheMac.mm new file mode 100644 index 0000000..10ae1cc --- /dev/null +++ b/WebCore/page/mac/AXObjectCacheMac.mm @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#import "config.h" +#import "AXObjectCache.h" + +#import "AccessibilityObject.h" +#import "AccessibilityObjectWrapper.h" +#import "RenderObject.h" +#import "WebCoreViewFactory.h" + +#import <wtf/PassRefPtr.h> + +// The simple Cocoa calls in this file don't throw exceptions. + +namespace WebCore { + +void AXObjectCache::detachWrapper(AccessibilityObject* obj) +{ + [obj->wrapper() detach]; + [obj->wrapper() release]; + obj->setWrapper(0); +} + +void AXObjectCache::attachWrapper(AccessibilityObject* obj) +{ + obj->setWrapper([[AccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj]); +} + +void AXObjectCache::postNotification(RenderObject* renderer, const String& message) +{ + if (!renderer) + return; + + // notifications for text input objects are sent to that object + // all others are sent to the top WebArea + RefPtr<AccessibilityObject> obj = get(renderer)->observableObject(); + if (!obj) + obj = get(renderer->document()->renderer()); + + if (!obj) + return; + + NSAccessibilityPostNotification(obj->wrapper(), message); +} + +void AXObjectCache::postNotificationToElement(RenderObject* renderer, const String& message) +{ + // send the notification to the specified element itself, not one of its ancestors + if (!renderer) + return; + + RefPtr<AccessibilityObject> obj = get(renderer); + if (!obj) + return; + + NSAccessibilityPostNotification(obj->wrapper(), message); +} + +void AXObjectCache::handleFocusedUIElementChanged() +{ + [[WebCoreViewFactory sharedFactory] accessibilityHandleFocusChanged]; +} + +} diff --git a/WebCore/page/mac/AccessibilityObjectMac.mm b/WebCore/page/mac/AccessibilityObjectMac.mm new file mode 100644 index 0000000..872e108 --- /dev/null +++ b/WebCore/page/mac/AccessibilityObjectMac.mm @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +#import "config.h" +#import "AccessibilityObject.h" + +#import "AccessibilityObjectWrapper.h" + +namespace WebCore { + +bool AccessibilityObject::accessibilityIgnoreAttachment() const +{ + return [[wrapper() attachmentView] accessibilityIsIgnored]; +} + +} // WebCore diff --git a/WebCore/page/AnimationController.h b/WebCore/page/mac/AccessibilityObjectWrapper.h index fda1143..5a55fe0 100644 --- a/WebCore/page/AnimationController.h +++ b/WebCore/page/mac/AccessibilityObjectWrapper.h @@ -1,18 +1,18 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Apple 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: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * 2. 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. + * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -25,33 +25,31 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#import <wtf/RefPtr.h> -#ifndef AnimationController_h -#define AnimationController_h +#ifdef __OBJC__ +@class WebCoreTextMarker; +@class WebCoreTextMarkerRange; +#else +class WebCoreTextMarker; +class WebCoreTextMarkerRange; +#endif namespace WebCore { + class AccessibilityObject; + class VisiblePosition; +} -class AnimationControllerPrivate; -class Frame; -class RenderObject; -class RenderStyle; - -class AnimationController +@interface AccessibilityObjectWrapper : NSObject { -public: - AnimationController(Frame*); - ~AnimationController(); - - void cancelImplicitAnimations(RenderObject*); - RenderStyle* updateImplicitAnimations(RenderObject*, RenderStyle* newStyle); - - void suspendAnimations(); - void resumeAnimations(); - -private: - AnimationControllerPrivate* m_data; -}; - + WebCore::AccessibilityObject* m_object; } + +- (id)initWithAccessibilityObject:(WebCore::AccessibilityObject*)axObject; +- (void)detach; +- (WebCore::AccessibilityObject*)accessibilityObject; -#endif +- (NSView*)attachmentView; + +@end diff --git a/WebCore/page/mac/AccessibilityObjectWrapper.mm b/WebCore/page/mac/AccessibilityObjectWrapper.mm new file mode 100644 index 0000000..2104ca9 --- /dev/null +++ b/WebCore/page/mac/AccessibilityObjectWrapper.mm @@ -0,0 +1,1996 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. + */ + +#import "config.h" +#import "AccessibilityObjectWrapper.h" + +#import "AXObjectCache.h" +#import "AccessibilityListBox.h" +#import "AccessibilityList.h" +#import "AccessibilityRenderObject.h" +#import "AccessibilityTable.h" +#import "AccessibilityTableCell.h" +#import "AccessibilityTableRow.h" +#import "AccessibilityTableColumn.h" +#import "ColorMac.h" +#import "Frame.h" +#import "HTMLAnchorElement.h" +#import "HTMLAreaElement.h" +#import "HTMLImageElement.h" +#import "HTMLInputElement.h" +#import "HTMLTextAreaElement.h" +#import "LocalizedStrings.h" +#import "RenderTextControl.h" +#import "RenderView.h" +#import "RenderWidget.h" +#import "SelectionController.h" +#import "SimpleFontData.h" +#import "TextIterator.h" +#import "WebCoreFrameView.h" +#import "WebCoreObjCExtras.h" +#import "WebCoreViewFactory.h" +#import "htmlediting.h" +#import "visible_units.h" + +using namespace WebCore; +using namespace HTMLNames; +using namespace std; + +// Cell Tables +#ifndef NSAccessibilitySelectedCellsAttribute +#define NSAccessibilitySelectedCellsAttribute @"AXSelectedCells" +#endif + +#ifndef NSAccessibilityVisibleCellsAttribute +#define NSAccessibilityVisibleCellsAttribute @"AXVisibleCells" +#endif + +#ifndef NSAccessibilityRowHeaderUIElementsAttribute +#define NSAccessibilityRowHeaderUIElementsAttribute @"AXRowHeaderUIElements" +#endif + +#ifndef NSAccessibilityRowIndexRangeAttribute +#define NSAccessibilityRowIndexRangeAttribute @"AXRowIndexRange" +#endif + +#ifndef NSAccessibilityColumnIndexRangeAttribute +#define NSAccessibilityColumnIndexRangeAttribute @"AXColumnIndexRange" +#endif + +#ifndef NSAccessibilityCellForColumnAndRowParameterizedAttribute +#define NSAccessibilityCellForColumnAndRowParameterizedAttribute @"AXCellForColumnAndRow" +#endif + +#ifndef NSAccessibilityCellRole +#define NSAccessibilityCellRole @"AXCell" +#endif + +// Lists +#ifndef NSAccessibilityContentListSubrole +#define NSAccessibilityContentListSubrole @"AXContentList" +#endif + +#ifndef NSAccessibilityDefinitionListSubrole +#define NSAccessibilityDefinitionListSubrole @"AXDefinitionList" +#endif + +#ifdef BUILDING_ON_TIGER +typedef unsigned NSUInteger; +#endif + +@interface NSObject (WebKitAccessibilityArrayCategory) + +- (NSUInteger)accessibilityIndexOfChild:(id)child; +- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute; +- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; + +@end + +@implementation AccessibilityObjectWrapper + +#ifndef BUILDING_ON_TIGER ++ (void)initialize +{ + WebCoreObjCFinalizeOnMainThread(self); +} +#endif + +- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject +{ + [super init]; + + m_object = axObject; + return self; +} + +- (void)unregisterUniqueIdForUIElement +{ + [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self]; +} + +- (void)detach +{ + // Send unregisterUniqueIdForUIElement unconditionally because if it is + // ever accidently not done (via other bugs in our AX implementation) you + // end up with a crash like <rdar://problem/4273149>. It is safe and not + // expensive to send even if the object is not registered. + [self unregisterUniqueIdForUIElement]; + m_object = 0; +} + +- (AccessibilityObject*)accessibilityObject +{ + return m_object; +} + +- (NSView*)attachmentView +{ + ASSERT(m_object->isAttachment()); + Widget* widget = m_object->widgetForAttachmentView(); + if (!widget) + return nil; + return widget->platformWidget(); +} + +static WebCoreTextMarker* textMarkerForVisiblePosition(const VisiblePosition& visiblePos) +{ + if (visiblePos.isNull()) + return nil; + + Position deepPos = visiblePos.deepEquivalent(); + Node* domNode = deepPos.node(); + ASSERT(domNode); + if (!domNode) + return nil; + + if (domNode->isHTMLElement()) + if (static_cast<HTMLElement*>(domNode)->isPasswordField()) + return nil; + + // locate the renderer, which must exist for a visible dom node + RenderObject* renderer = domNode->renderer(); + ASSERT(renderer); + + // find or create an accessibility object for this renderer + AXObjectCache* cache = renderer->document()->axObjectCache(); + RefPtr<AccessibilityObject> obj = cache->get(renderer); + + // create a text marker, adding an ID for the AccessibilityObject if needed + TextMarkerData textMarkerData; + + // The compiler can add padding to this struct. + // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence. + bzero(&textMarkerData, sizeof(TextMarkerData)); + textMarkerData.axID = obj.get()->axObjectID(); + textMarkerData.node = domNode; + textMarkerData.offset = deepPos.offset(); + textMarkerData.affinity = visiblePos.affinity(); + return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)]; +} + +static VisiblePosition visiblePositionForTextMarker(WebCoreTextMarker* textMarker) +{ + TextMarkerData textMarkerData; + + if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)]) + return VisiblePosition(); + + VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity); + Position deepPos = visiblePos.deepEquivalent(); + if (deepPos.isNull()) + return VisiblePosition(); + + RenderObject* renderer = deepPos.node()->renderer(); + if (!renderer) + return VisiblePosition(); + + AXObjectCache* cache = renderer->document()->axObjectCache(); + if (!cache->isIDinUse(textMarkerData.axID)) + return VisiblePosition(); + + if (deepPos.node() != textMarkerData.node || deepPos.offset() != textMarkerData.offset) + return VisiblePosition(); + + return visiblePos; +} + +static VisiblePosition visiblePositionForStartOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange) +{ + return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]); +} + +static VisiblePosition visiblePositionForEndOfTextMarkerRange(WebCoreTextMarkerRange* textMarkerRange) +{ + return visiblePositionForTextMarker([[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]); +} + +static WebCoreTextMarkerRange* textMarkerRangeFromMarkers(WebCoreTextMarker* textMarker1, WebCoreTextMarker* textMarker2) +{ + if (!textMarker1 || !textMarker2) + return nil; + + return [[WebCoreViewFactory sharedFactory] textMarkerRangeWithStart:textMarker1 end:textMarker2]; +} + +static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, NSString* attribute, NSFont* font, NSRange range) +{ + NSDictionary* dict; + + if (font) { + dict = [NSDictionary dictionaryWithObjectsAndKeys: + [font fontName] , NSAccessibilityFontNameKey, + [font familyName] , NSAccessibilityFontFamilyKey, + [font displayName] , NSAccessibilityVisibleNameKey, + [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey, + nil]; + + [attrString addAttribute:attribute value:dict range:range]; + } else + [attrString removeAttribute:attribute range:range]; + +} + +static CGColorRef CreateCGColorIfDifferent(NSColor* nsColor, CGColorRef existingColor) +{ + // get color information assuming NSDeviceRGBColorSpace + NSColor* rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + if (rgbColor == nil) + rgbColor = [NSColor blackColor]; + CGFloat components[4]; + [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]]; + + // create a new CGColorRef to return + CGColorSpaceRef cgColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + CGColorRef cgColor = CGColorCreate(cgColorSpace, components); + CGColorSpaceRelease(cgColorSpace); + CFMakeCollectable(cgColor); + + // check for match with existing color + if (existingColor && CGColorEqualToColor(cgColor, existingColor)) + cgColor = nil; + + return cgColor; +} + +static void AXAttributeStringSetColor(NSMutableAttributedString* attrString, NSString* attribute, NSColor* color, NSRange range) +{ + if (color) { + CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil]; + CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor); + if (cgColor) { + [attrString addAttribute:attribute value:(id)cgColor range:range]; + CGColorRelease(cgColor); + } + } else + [attrString removeAttribute:attribute range:range]; +} + +static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range) +{ + if (number) + [attrString addAttribute:attribute value:number range:range]; + else + [attrString removeAttribute:attribute range:range]; +} + +static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) +{ + RenderStyle* style = renderer->style(); + + // set basic font info + AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().primaryFont()->getNSFont(), range); + + // set basic colors + AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttribute, nsColor(style->color()), range); + AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, nsColor(style->backgroundColor()), range); + + // set super/sub scripting + EVerticalAlign alignment = style->verticalAlign(); + if (alignment == SUB) + AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range); + else if (alignment == SUPER) + AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range); + else + [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range]; + + // set shadow + if (style->textShadow()) + AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range); + else + [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range]; + + // set underline and strikethrough + int decor = style->textDecorationsInEffect(); + if ((decor & UNDERLINE) == 0) { + [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range]; + [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range]; + } + + if ((decor & LINE_THROUGH) == 0) { + [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range]; + [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range]; + } + + if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) { + // find colors using quirk mode approach (strict mode would use current + // color for all but the root line box, which would use getTextDecorationColors) + Color underline, overline, linethrough; + renderer->getTextDecorationColors(decor, underline, overline, linethrough); + + if ((decor & UNDERLINE) != 0) { + AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range); + AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, nsColor(underline), range); + } + + if ((decor & LINE_THROUGH) != 0) { + AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range); + AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, nsColor(linethrough), range); + } + } +} + +static int blockquoteLevel(RenderObject* renderer) +{ + int result = 0; + for (Node* node = renderer->element(); node; node = node->parent()) { + if (node->hasTagName(blockquoteTag)) + result += 1; + } + + return result; +} + +static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) +{ + int quoteLevel = blockquoteLevel(renderer); + + if (quoteLevel) + [attrString addAttribute:@"AXBlockQuoteLevel" value:[NSNumber numberWithInt:quoteLevel] range:range]; + else + [attrString removeAttribute:@"AXBlockQuoteLevel" range:range]; +} + +static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, Node* node, int offset, NSRange range) +{ + Vector<DocumentMarker> markers = node->renderer()->document()->markersForNode(node); + Vector<DocumentMarker>::iterator markerIt = markers.begin(); + + unsigned endOffset = (unsigned)offset + range.length; + for ( ; markerIt != markers.end(); markerIt++) { + DocumentMarker marker = *markerIt; + + if (marker.type != DocumentMarker::Spelling) + continue; + + if (marker.endOffset <= (unsigned)offset) + continue; + + if (marker.startOffset > endOffset) + break; + + // add misspelling attribute for the intersection of the marker and the range + int rStart = range.location + (marker.startOffset - offset); + int rLength = MIN(marker.endOffset, endOffset) - marker.startOffset; + NSRange spellRange = NSMakeRange(rStart, rLength); + AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange); + + if (marker.endOffset > endOffset + 1) + break; + } +} + +static void AXAttributeStringSetHeadingLevel(AccessibilityObject* object, NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range) +{ + int parentHeadingLevel = AccessibilityRenderObject::headingLevel(renderer->parent()->element()); + + if (parentHeadingLevel) + [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range]; + else + [attrString removeAttribute:@"AXHeadingLevel" range:range]; +} + +static AccessibilityObject* AXLinkElementForNode(Node* node) +{ + RenderObject* obj = node->renderer(); + if (!obj) + return 0; + + RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->get(obj); + Element* anchor = axObj->anchorElement(); + if (!anchor) + return 0; + + RenderObject* anchorRenderer = anchor->renderer(); + if (!anchorRenderer) + return 0; + + return anchorRenderer->document()->axObjectCache()->get(anchorRenderer); +} + +static void AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, AccessibilityObject* object, NSRange range) +{ + if (object && object->isAccessibilityRenderObject()) { + // make a serialiazable AX object + + RenderObject* renderer = static_cast<AccessibilityRenderObject*>(object)->renderer(); + if (!renderer) + return; + + Document* doc = renderer->document(); + if (!doc) + return; + + AXObjectCache* cache = doc->axObjectCache(); + if (!cache) + return; + + AXUIElementRef axElement = [[WebCoreViewFactory sharedFactory] AXUIElementForElement:object->wrapper()]; + if (axElement) { + [attrString addAttribute:attribute value:(id)axElement range:range]; + CFRelease(axElement); + } + } else + [attrString removeAttribute:attribute range:range]; +} + +static void AXAttributedStringAppendText(AccessibilityObject* object, NSMutableAttributedString* attrString, Node* node, int offset, const UChar* chars, int length) +{ + // skip invisible text + if (!node->renderer()) + return; + + // easier to calculate the range before appending the string + NSRange attrStringRange = NSMakeRange([attrString length], length); + + // append the string from this node + [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]]; + + // add new attributes and remove irrelevant inherited ones + // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge + // identical colors. Workaround is to not replace an existing color attribute if it matches what we are adding. This also means + // we cannot just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually. + + // remove inherited attachment from prior AXAttributedStringAppendReplaced + [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange]; + + // set new attributes + AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange); + AXAttributeStringSetHeadingLevel(object, attrString, node->renderer(), attrStringRange); + AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange); + AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AXLinkElementForNode(node), attrStringRange); + + // do spelling last because it tends to break up the range + AXAttributeStringSetSpelling(attrString, node, offset, attrStringRange); +} + +static NSString* nsStringForReplacedNode(Node* replacedNode) +{ + // we should always be given a rendered node and a replaced node, but be safe + // replaced nodes are either attachments (widgets) or images + if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) { + ASSERT_NOT_REACHED(); + return nil; + } + + // create an AX object, but skip it if it is not supposed to be seen + RefPtr<AccessibilityObject> obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer()); + if (obj->accessibilityIsIgnored()) + return nil; + + // use the attachmentCharacter to represent the replaced node + const UniChar attachmentChar = NSAttachmentCharacter; + return [NSString stringWithCharacters:&attachmentChar length:1]; +} + +- (NSAttributedString*)doAXAttributedStringForTextMarkerRange:(WebCoreTextMarkerRange*)textMarkerRange +{ + // extract the start and end VisiblePosition + VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(textMarkerRange); + if (startVisiblePosition.isNull()) + return nil; + + VisiblePosition endVisiblePosition = visiblePositionForEndOfTextMarkerRange(textMarkerRange); + if (endVisiblePosition.isNull()) + return nil; + + // iterate over the range to build the AX attributed string + NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init]; + TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get()); + while (!it.atEnd()) { + // locate the node and starting offset for this range + int exception = 0; + Node* node = it.range()->startContainer(exception); + ASSERT(node == it.range()->endContainer(exception)); + int offset = it.range()->startOffset(exception); + + // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) + if (it.length() != 0) { + AXAttributedStringAppendText(m_object, attrString, node, offset, it.characters(), it.length()); + } else { + Node* replacedNode = node->childNode(offset); + NSString *attachmentString = nsStringForReplacedNode(replacedNode); + if (attachmentString) { + NSRange attrStringRange = NSMakeRange([attrString length], [attachmentString length]); + + // append the placeholder string + [[attrString mutableString] appendString:attachmentString]; + + // remove all inherited attributes + [attrString setAttributes:nil range:attrStringRange]; + + // add the attachment attribute + AccessibilityObject* obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer()); + AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange); + } + } + it.advance(); + } + + return [attrString autorelease]; +} + +static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(VisiblePosition startPosition, VisiblePosition endPosition) +{ + WebCoreTextMarker* startTextMarker = textMarkerForVisiblePosition(startPosition); + WebCoreTextMarker* endTextMarker = textMarkerForVisiblePosition(endPosition); + return textMarkerRangeFromMarkers(startTextMarker, endTextMarker); +} + +- (NSArray*)accessibilityActionNames +{ + static NSArray* actionElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil]; + static NSArray* defaultElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityShowMenuAction, nil]; + static NSArray* menuElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityCancelAction, NSAccessibilityPressAction, nil]; + + NSArray *actions; + if (m_object->actionElement()) + actions = actionElementActions; + else if (m_object->isMenuRelated()) + actions = menuElementActions; + else if (m_object->isAttachment()) + actions = [[self attachmentView] accessibilityActionNames]; + else + actions = defaultElementActions; + + return actions; +} + +- (NSArray*)accessibilityAttributeNames +{ + if (m_object->isAttachment()) + return [[self attachmentView] accessibilityAttributeNames]; + + static NSArray* attributes = nil; + static NSArray* anchorAttrs = nil; + static NSArray* webAreaAttrs = nil; + static NSArray* textAttrs = nil; + static NSArray* listBoxAttrs = nil; + static NSArray* rangeAttrs = nil; + static NSArray* commonMenuAttrs = nil; + static NSArray* menuAttrs = nil; + static NSArray* menuBarAttrs = nil; + static NSArray* menuItemAttrs = nil; + static NSArray* menuButtonAttrs = nil; + static NSArray* controlAttrs = nil; + static NSArray* tableAttrs = nil; + static NSArray* tableRowAttrs = nil; + static NSArray* tableColAttrs = nil; + static NSArray* tableCellAttrs = nil; + NSMutableArray* tempArray; + if (attributes == nil) { + attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute, + NSAccessibilitySubroleAttribute, + NSAccessibilityRoleDescriptionAttribute, + NSAccessibilityChildrenAttribute, + NSAccessibilityHelpAttribute, + NSAccessibilityParentAttribute, + NSAccessibilityPositionAttribute, + NSAccessibilitySizeAttribute, + NSAccessibilityTitleAttribute, + NSAccessibilityDescriptionAttribute, + NSAccessibilityValueAttribute, + NSAccessibilityFocusedAttribute, + NSAccessibilityEnabledAttribute, + NSAccessibilityWindowAttribute, + @"AXSelectedTextMarkerRange", + @"AXStartTextMarker", + @"AXEndTextMarker", + @"AXVisited", + NSAccessibilityLinkedUIElementsAttribute, + NSAccessibilitySelectedAttribute, + @"AXBlockQuoteLevel", + NSAccessibilityTopLevelUIElementAttribute, + nil]; + } + if (commonMenuAttrs == nil) { + commonMenuAttrs = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute, + NSAccessibilityRoleDescriptionAttribute, + NSAccessibilityChildrenAttribute, + NSAccessibilityParentAttribute, + NSAccessibilityEnabledAttribute, + NSAccessibilityPositionAttribute, + NSAccessibilitySizeAttribute, + nil]; + } + if (anchorAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityURLAttribute]; + anchorAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (webAreaAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:@"AXLinkUIElements"]; + [tempArray addObject:@"AXLoaded"]; + [tempArray addObject:@"AXLayoutCount"]; + [tempArray addObject:NSAccessibilityURLAttribute]; + webAreaAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (textAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute]; + [tempArray addObject:NSAccessibilitySelectedTextAttribute]; + [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute]; + [tempArray addObject:NSAccessibilityVisibleCharacterRangeAttribute]; + [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute]; + [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; + textAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (listBoxAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; + [tempArray addObject:NSAccessibilityVisibleChildrenAttribute]; + [tempArray addObject:NSAccessibilityOrientationAttribute]; + [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; + listBoxAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (rangeAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityTopLevelUIElementAttribute]; + [tempArray addObject:NSAccessibilityValueAttribute]; + [tempArray addObject:NSAccessibilityMinValueAttribute]; + [tempArray addObject:NSAccessibilityMaxValueAttribute]; + rangeAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (menuBarAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs]; + [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; + [tempArray addObject:NSAccessibilityVisibleChildrenAttribute]; + [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; + menuBarAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (menuAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs]; + [tempArray addObject:NSAccessibilitySelectedChildrenAttribute]; + [tempArray addObject:NSAccessibilityVisibleChildrenAttribute]; + [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; + menuAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (menuItemAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs]; + [tempArray addObject:NSAccessibilityTitleAttribute]; + [tempArray addObject:NSAccessibilityHelpAttribute]; + [tempArray addObject:NSAccessibilitySelectedAttribute]; + [tempArray addObject:(NSString*)kAXMenuItemCmdCharAttribute]; + [tempArray addObject:(NSString*)kAXMenuItemCmdVirtualKeyAttribute]; + [tempArray addObject:(NSString*)kAXMenuItemCmdGlyphAttribute]; + [tempArray addObject:(NSString*)kAXMenuItemCmdModifiersAttribute]; + [tempArray addObject:(NSString*)kAXMenuItemMarkCharAttribute]; + [tempArray addObject:(NSString*)kAXMenuItemPrimaryUIElementAttribute]; + [tempArray addObject:NSAccessibilityServesAsTitleForUIElementsAttribute]; + menuItemAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (menuButtonAttrs == nil) { + menuButtonAttrs = [[NSArray alloc] initWithObjects:NSAccessibilityRoleAttribute, + NSAccessibilityRoleDescriptionAttribute, + NSAccessibilityParentAttribute, + NSAccessibilityPositionAttribute, + NSAccessibilitySizeAttribute, + NSAccessibilityWindowAttribute, + NSAccessibilityTopLevelUIElementAttribute, + NSAccessibilityEnabledAttribute, + NSAccessibilityFocusedAttribute, + NSAccessibilityTitleAttribute, + NSAccessibilityChildrenAttribute, nil]; + } + if (controlAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityTitleUIElementAttribute]; + controlAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (tableAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityRowsAttribute]; + [tempArray addObject:NSAccessibilityVisibleRowsAttribute]; + [tempArray addObject:NSAccessibilityColumnsAttribute]; + [tempArray addObject:NSAccessibilityVisibleColumnsAttribute]; + [tempArray addObject:NSAccessibilityVisibleCellsAttribute]; + [tempArray addObject:(NSString *)kAXColumnHeaderUIElementsAttribute]; + [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute]; + [tempArray addObject:NSAccessibilityHeaderAttribute]; + tableAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (tableRowAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityIndexAttribute]; + tableRowAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (tableColAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityIndexAttribute]; + [tempArray addObject:NSAccessibilityHeaderAttribute]; + [tempArray addObject:NSAccessibilityRowsAttribute]; + [tempArray addObject:NSAccessibilityVisibleRowsAttribute]; + tableColAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (tableCellAttrs == nil) { + tempArray = [[NSMutableArray alloc] initWithArray:attributes]; + [tempArray addObject:NSAccessibilityRowIndexRangeAttribute]; + [tempArray addObject:NSAccessibilityColumnIndexRangeAttribute]; + tableCellAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + + if (m_object->isPasswordField()) + return attributes; + + if (m_object->isWebArea()) + return webAreaAttrs; + + if (m_object->isTextControl()) + return textAttrs; + + if (m_object->isAnchor() || m_object->isImage()) + return anchorAttrs; + + if (m_object->isDataTable()) + return tableAttrs; + if (m_object->isTableRow()) + return tableRowAttrs; + if (m_object->isTableColumn()) + return tableColAttrs; + if (m_object->isTableCell()) + return tableCellAttrs; + + if (m_object->isListBox() || m_object->isList()) + return listBoxAttrs; + + if (m_object->isProgressIndicator() || m_object->isSlider()) + return rangeAttrs; + + if (m_object->isControl()) + return controlAttrs; + + if (m_object->isMenu()) + return menuAttrs; + if (m_object->isMenuBar()) + return menuBarAttrs; + if (m_object->isMenuButton()) + return menuButtonAttrs; + if (m_object->isMenuItem()) + return menuItemAttrs; + + return attributes; +} + +- (VisiblePositionRange)visiblePositionRangeForTextMarkerRange:(WebCoreTextMarkerRange*) textMarkerRange +{ + return VisiblePositionRange(visiblePositionForStartOfTextMarkerRange(textMarkerRange), visiblePositionForEndOfTextMarkerRange(textMarkerRange)); +} + +- (NSArray*)renderWidgetChildren +{ + Widget* widget = m_object->widget(); + if (!widget) + return nil; + return [(widget->getOuterView()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute]; +} + +static void convertToVector(NSArray* array, AccessibilityObject::AccessibilityChildrenVector& vector) +{ + unsigned length = [array count]; + vector.reserveCapacity(length); + for (unsigned i = 0; i < length; ++i) { + AccessibilityObject* obj = [[array objectAtIndex:i] accessibilityObject]; + if (obj) + vector.append(obj); + } +} + +static NSMutableArray* convertToNSArray(const AccessibilityObject::AccessibilityChildrenVector& vector) +{ + unsigned length = vector.size(); + NSMutableArray* array = [NSMutableArray arrayWithCapacity: length]; + for (unsigned i = 0; i < length; ++i) { + ASSERT(vector[i]->wrapper()); + if (vector[i]->wrapper()) + [array addObject:vector[i]->wrapper()]; + } + return array; +} + +- (WebCoreTextMarkerRange*)textMarkerRangeForSelection +{ + Selection selection = m_object->selection(); + if (selection.isNone()) + return nil; + return textMarkerRangeFromVisiblePositions(selection.visibleStart(), selection.visibleEnd()); +} + +- (NSValue*)position +{ + IntRect rect = m_object->elementRect(); + + // The Cocoa accessibility API wants the lower-left corner. + NSPoint point = NSMakePoint(rect.x(), rect.bottom()); + FrameView* frameView = m_object->documentFrameView(); + if (frameView) { + NSView* view = frameView->documentView(); + point = [[view window] convertBaseToScreen: [view convertPoint: point toView:nil]]; + } + + return [NSValue valueWithPoint: point]; +} + +typedef HashMap<int, NSString*> AccessibilityRoleMap; + +static const AccessibilityRoleMap& createAccessibilityRoleMap() +{ + struct RoleEntry { + AccessibilityRole value; + NSString* string; + }; + + static const RoleEntry roles[] = { + { UnknownRole, NSAccessibilityUnknownRole }, + { ButtonRole, NSAccessibilityButtonRole }, + { RadioButtonRole, NSAccessibilityRadioButtonRole }, + { CheckBoxRole, NSAccessibilityCheckBoxRole }, + { SliderRole, NSAccessibilitySliderRole }, + { TabGroupRole, NSAccessibilityTabGroupRole }, + { TextFieldRole, NSAccessibilityTextFieldRole }, + { StaticTextRole, NSAccessibilityStaticTextRole }, + { TextAreaRole, NSAccessibilityTextAreaRole }, + { ScrollAreaRole, NSAccessibilityScrollAreaRole }, + { PopUpButtonRole, NSAccessibilityPopUpButtonRole }, + { MenuButtonRole, NSAccessibilityMenuButtonRole }, + { TableRole, NSAccessibilityTableRole }, + { ApplicationRole, NSAccessibilityApplicationRole }, + { GroupRole, NSAccessibilityGroupRole }, + { RadioGroupRole, NSAccessibilityRadioGroupRole }, + { ListRole, NSAccessibilityListRole }, + { ScrollBarRole, NSAccessibilityScrollBarRole }, + { ValueIndicatorRole, NSAccessibilityValueIndicatorRole }, + { ImageRole, NSAccessibilityImageRole }, + { MenuBarRole, NSAccessibilityMenuBarRole }, + { MenuRole, NSAccessibilityMenuRole }, + { MenuItemRole, NSAccessibilityMenuItemRole }, + { ColumnRole, NSAccessibilityColumnRole }, + { RowRole, NSAccessibilityRowRole }, + { ToolbarRole, NSAccessibilityToolbarRole }, + { BusyIndicatorRole, NSAccessibilityBusyIndicatorRole }, + { ProgressIndicatorRole, NSAccessibilityProgressIndicatorRole }, + { WindowRole, NSAccessibilityWindowRole }, + { DrawerRole, NSAccessibilityDrawerRole }, + { SystemWideRole, NSAccessibilitySystemWideRole }, + { OutlineRole, NSAccessibilityOutlineRole }, + { IncrementorRole, NSAccessibilityIncrementorRole }, + { BrowserRole, NSAccessibilityBrowserRole }, + { ComboBoxRole, NSAccessibilityComboBoxRole }, + { SplitGroupRole, NSAccessibilitySplitGroupRole }, + { SplitterRole, NSAccessibilitySplitterRole }, + { ColorWellRole, NSAccessibilityColorWellRole }, + { GrowAreaRole, NSAccessibilityGrowAreaRole }, + { SheetRole, NSAccessibilitySheetRole }, + { HelpTagRole, NSAccessibilityHelpTagRole }, + { MatteRole, NSAccessibilityMatteRole }, + { RulerRole, NSAccessibilityRulerRole }, + { RulerMarkerRole, NSAccessibilityRulerMarkerRole }, + { LinkRole, NSAccessibilityLinkRole }, +#ifndef BUILDING_ON_TIGER + { DisclosureTriangleRole, NSAccessibilityDisclosureTriangleRole }, + { GridRole, NSAccessibilityGridRole }, +#endif + { WebCoreLinkRole, NSAccessibilityLinkRole }, + { ImageMapLinkRole, NSAccessibilityLinkRole }, + { ImageMapRole, @"AXImageMap" }, + { ListMarkerRole, @"AXListMarker" }, + { WebAreaRole, @"AXWebArea" }, + { HeadingRole, @"AXHeading" }, + { ListBoxRole, NSAccessibilityListRole }, + { ListBoxOptionRole, NSAccessibilityStaticTextRole }, + // cells don't exist on tiger or leopard +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + { CellRole, NSAccessibilityGroupRole }, +#else + { CellRole, NSAccessibilityCellRole }, +#endif + { TableHeaderContainerRole, NSAccessibilityGroupRole }, + { DefinitionListDefinitionRole, NSAccessibilityGroupRole }, + { DefinitionListTermRole, NSAccessibilityGroupRole } + + }; + AccessibilityRoleMap& roleMap = *new AccessibilityRoleMap; + + const unsigned numRoles = sizeof(roles) / sizeof(roles[0]); + for (unsigned i = 0; i < numRoles; ++i) + roleMap.set(roles[i].value, roles[i].string); + return roleMap; +} + +static NSString* roleValueToNSString(AccessibilityRole value) +{ + ASSERT(value); + static const AccessibilityRoleMap& roleMap = createAccessibilityRoleMap(); + return roleMap.get(value); +} + +- (NSString*)role +{ + if (m_object->isAttachment()) + return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute]; + NSString* string = roleValueToNSString(m_object->roleValue()); + if (string != nil) + return string; + return NSAccessibilityUnknownRole; +} + +- (NSString*)subrole +{ + if (m_object->isPasswordField()) + return NSAccessibilitySecureTextFieldSubrole; + + if (m_object->isAttachment()) { + NSView* attachView = [self attachmentView]; + if ([[attachView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) { + return [attachView accessibilityAttributeValue:NSAccessibilitySubroleAttribute]; + } + } + + if (m_object->isList()) { + AccessibilityList* listObject = static_cast<AccessibilityList*>(m_object); + if (listObject->isUnorderedList() || listObject->isOrderedList()) + return NSAccessibilityContentListSubrole; + if (listObject->isDefinitionList()) + return NSAccessibilityDefinitionListSubrole; + } + + return nil; +} + +- (NSString*)roleDescription +{ + if (!m_object) + return nil; + + // attachments have the AXImage role, but a different subrole + if (m_object->isAttachment()) + return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute]; + + // FIXME 3447564: It would be better to call some AppKit API to get these strings + // (which would be the best way to localize them) + + NSString* axRole = [self role]; + if ([axRole isEqualToString:NSAccessibilityButtonRole]) + return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityPopUpButtonRole]) + return NSAccessibilityRoleDescription(NSAccessibilityPopUpButtonRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityStaticTextRole]) + return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityImageRole]) + return NSAccessibilityRoleDescription(NSAccessibilityImageRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityGroupRole]) + return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityCheckBoxRole]) + return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityRadioButtonRole]) + return NSAccessibilityRoleDescription(NSAccessibilityRadioButtonRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityTextFieldRole]) + return NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityTextAreaRole]) + return NSAccessibilityRoleDescription(NSAccessibilityTextAreaRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityListRole]) + return NSAccessibilityRoleDescription(NSAccessibilityListRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityTableRole]) + return NSAccessibilityRoleDescription(NSAccessibilityTableRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityRowRole]) + return NSAccessibilityRoleDescription(NSAccessibilityRowRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityColumnRole]) + return NSAccessibilityRoleDescription(NSAccessibilityColumnRole, [self subrole]); + + if ([axRole isEqualToString:NSAccessibilityCellRole]) + return NSAccessibilityRoleDescription(NSAccessibilityCellRole, [self subrole]); + + if ([axRole isEqualToString:@"AXWebArea"]) + return AXWebAreaText(); + + if ([axRole isEqualToString:@"AXLink"]) + return AXLinkText(); + + if ([axRole isEqualToString:@"AXListMarker"]) + return AXListMarkerText(); + + if ([axRole isEqualToString:@"AXImageMap"]) + return AXImageMapText(); + + if ([axRole isEqualToString:@"AXHeading"]) + return AXHeadingText(); + + if ([axRole isEqualToString:(NSString*)kAXMenuBarItemRole] || + [axRole isEqualToString:NSAccessibilityMenuRole]) + return nil; + + if ([axRole isEqualToString:NSAccessibilityMenuButtonRole]) + return NSAccessibilityRoleDescription(NSAccessibilityMenuButtonRole, [self subrole]); + + return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil); +} + +// FIXME: split up this function in a better way. +// suggestions: Use a hash table that maps attribute names to function calls, +// or maybe pointers to member functions +- (id)accessibilityAttributeValue:(NSString*)attributeName +{ + if (!m_object) + return nil; + + if ([attributeName isEqualToString: NSAccessibilityRoleAttribute]) + return [self role]; + + if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute]) + return [self subrole]; + + if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute]) + return [self roleDescription]; + + if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) { + if (m_object->isAccessibilityRenderObject()) { + FrameView* fv = static_cast<AccessibilityRenderObject*>(m_object)->frameViewIfRenderView(); + if (fv) + return fv->platformWidget(); + } + + return m_object->parentObjectUnignored()->wrapper(); + } + + if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) { + if (m_object->children().isEmpty()) { + NSArray* children = [self renderWidgetChildren]; + if (children != nil) + return children; + } + return convertToNSArray(m_object->children()); + } + + if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) { + if (m_object->isListBox()) { + AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy; + m_object->selectedChildren(selectedChildrenCopy); + return convertToNSArray(selectedChildrenCopy); + } + return nil; + } + + if ([attributeName isEqualToString: NSAccessibilityVisibleChildrenAttribute]) { + if (m_object->isListBox()) { + AccessibilityObject::AccessibilityChildrenVector visibleChildrenCopy; + m_object->visibleChildren(visibleChildrenCopy); + return convertToNSArray(visibleChildrenCopy); + } + else if (m_object->isList()) + return [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; + + return nil; + } + + + if (m_object->isWebArea()) { + if ([attributeName isEqualToString: @"AXLinkUIElements"]) { + AccessibilityObject::AccessibilityChildrenVector links; + static_cast<AccessibilityRenderObject*>(m_object)->getDocumentLinks(links); + return convertToNSArray(links); + } + if ([attributeName isEqualToString: @"AXLoaded"]) + return [NSNumber numberWithBool: m_object->isLoaded()]; + if ([attributeName isEqualToString: @"AXLayoutCount"]) + return [NSNumber numberWithInt: m_object->layoutCount()]; + } + + if (m_object->isTextControl()) { + if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) { + int length = m_object->textLength(); + if (length < 0) + return nil; + return [NSNumber numberWithUnsignedInt:length]; + } + if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) { + String selectedText = m_object->selectedText(); + if (selectedText.isNull()) + return nil; + return (NSString*)selectedText; + } + if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) { + PlainTextRange textRange = m_object->selectedTextRange(); + if (textRange.isNull()) + return nil; + return [NSValue valueWithRange:NSMakeRange(textRange.start, textRange.length)]; + } + // TODO: Get actual visible range. <rdar://problem/4712101> + if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) + return m_object->isPasswordField() ? nil : [NSValue valueWithRange: NSMakeRange(0, m_object->textLength())]; + if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) { + // if selectionEnd > 0, then there is selected text and this question should not be answered + if (m_object->isPasswordField() || m_object->selectionEnd() > 0) + return nil; + int lineNumber = m_object->lineForPosition(m_object->visiblePositionForIndex(m_object->selectionStart(), true)); + if (lineNumber < 0) + return nil; + return [NSNumber numberWithInt:lineNumber]; + } + } + + if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) { + KURL url = m_object->url(); + if (url.isNull()) + return nil; + return (NSURL*)url; + } + + if ([attributeName isEqualToString: @"AXVisited"]) + return [NSNumber numberWithBool: m_object->isVisited()]; + + if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) { + if (m_object->isAttachment()) { + if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute]) + return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute]; + } + return m_object->title(); + } + + if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) { + if (m_object->isAttachment()) { + if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute]) + return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityDescriptionAttribute]; + } + return m_object->accessibilityDescription(); + } + + if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) { + if (m_object->isAttachment()) { + if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute]) + return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityValueAttribute]; + } + if (m_object->isProgressIndicator() || m_object->isSlider()) + return [NSNumber numberWithFloat:m_object->valueForRange()]; + if (m_object->hasIntValue()) + return [NSNumber numberWithInt:m_object->intValue()]; + return m_object->stringValue(); + } + + if ([attributeName isEqualToString: NSAccessibilityMinValueAttribute]) + return [NSNumber numberWithFloat:m_object->minValueForRange()]; + + if ([attributeName isEqualToString: NSAccessibilityMaxValueAttribute]) + return [NSNumber numberWithFloat:m_object->maxValueForRange()]; + + if ([attributeName isEqualToString: NSAccessibilityHelpAttribute]) + return m_object->helpText(); + + if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) + return [NSNumber numberWithBool: m_object->isFocused()]; + + if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute]) + return [NSNumber numberWithBool: m_object->isEnabled()]; + + if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) { + IntSize s = m_object->size(); + return [NSValue valueWithSize: NSMakeSize(s.width(), s.height())]; + } + + if ([attributeName isEqualToString: NSAccessibilityPositionAttribute]) + return [self position]; + + if ([attributeName isEqualToString: NSAccessibilityWindowAttribute] || + [attributeName isEqualToString: NSAccessibilityTopLevelUIElementAttribute]) { + FrameView* fv = m_object->documentFrameView(); + if (fv) + return [fv->platformWidget() window]; + return nil; + } + + if (m_object->isDataTable()) { + // TODO: distinguish between visible and non-visible rows + if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] || + [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) { + return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->rows()); + } + // TODO: distinguish between visible and non-visible columns + if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute] || + [attributeName isEqualToString:NSAccessibilityVisibleColumnsAttribute]) { + return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->columns()); + } + + // HTML tables don't support these + if ([attributeName isEqualToString:NSAccessibilitySelectedColumnsAttribute] || + [attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute] || + [attributeName isEqualToString:NSAccessibilitySelectedCellsAttribute]) + return nil; + + if ([attributeName isEqualToString:(NSString *)kAXColumnHeaderUIElementsAttribute]) { + AccessibilityObject::AccessibilityChildrenVector columnHeaders; + static_cast<AccessibilityTable*>(m_object)->columnHeaders(columnHeaders); + return convertToNSArray(columnHeaders); + } + + if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) { + AccessibilityObject* headerContainer = static_cast<AccessibilityTable*>(m_object)->headerContainer(); + if (headerContainer) + return headerContainer->wrapper(); + return nil; + } + + if ([attributeName isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) { + AccessibilityObject::AccessibilityChildrenVector rowHeaders; + static_cast<AccessibilityTable*>(m_object)->rowHeaders(rowHeaders); + return convertToNSArray(rowHeaders); + } + + if ([attributeName isEqualToString:NSAccessibilityVisibleCellsAttribute]) { + AccessibilityObject::AccessibilityChildrenVector cells; + static_cast<AccessibilityTable*>(m_object)->cells(cells); + return convertToNSArray(cells); + } + } + + if (m_object->isTableRow()) { + if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) + return [NSNumber numberWithInt:static_cast<AccessibilityTableRow*>(m_object)->rowIndex()]; + } + + if (m_object->isTableColumn()) { + if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) + return [NSNumber numberWithInt:static_cast<AccessibilityTableColumn*>(m_object)->columnIndex()]; + + // rows attribute for a column is the list of all the elements in that column at each row + if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] || + [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) { + return convertToNSArray(static_cast<AccessibilityTableColumn*>(m_object)->children()); + } + if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) { + AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_object)->headerObject(); + if (!header) + return nil; + return header->wrapper(); + } + } + + if (m_object->isTableCell()) { + if ([attributeName isEqualToString:NSAccessibilityRowIndexRangeAttribute]) { + pair<int, int> rowRange; + static_cast<AccessibilityTableCell*>(m_object)->rowIndexRange(rowRange); + return [NSValue valueWithRange:NSMakeRange(rowRange.first, rowRange.second)]; + } + if ([attributeName isEqualToString:NSAccessibilityColumnIndexRangeAttribute]) { + pair<int, int> columnRange; + static_cast<AccessibilityTableCell*>(m_object)->columnIndexRange(columnRange); + return [NSValue valueWithRange:NSMakeRange(columnRange.first, columnRange.second)]; + } + } + + if ((m_object->isListBox() ||m_object->isList()) && [attributeName isEqualToString:NSAccessibilityOrientationAttribute]) + return NSAccessibilityVerticalOrientationValue; + + if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) + return [self textMarkerRangeForSelection]; + + if (m_object->isAccessibilityRenderObject()) { + RenderObject* renderer = static_cast<AccessibilityRenderObject*>(m_object)->renderer(); + if (!renderer) + return nil; + + if ([attributeName isEqualToString: @"AXStartTextMarker"]) + return textMarkerForVisiblePosition(startOfDocument(renderer->document())); + if ([attributeName isEqualToString: @"AXEndTextMarker"]) + return textMarkerForVisiblePosition(endOfDocument(renderer->document())); + + if ([attributeName isEqualToString: @"AXBlockQuoteLevel"]) + return [NSNumber numberWithInt:blockquoteLevel(renderer)]; + } + + if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) { + AccessibilityObject::AccessibilityChildrenVector linkedUIElements; + m_object->linkedUIElements(linkedUIElements); + if (linkedUIElements.size() == 0) + return nil; + return convertToNSArray(linkedUIElements); + } + + if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) + return [NSNumber numberWithBool:m_object->isSelected()]; + + if ([attributeName isEqualToString: NSAccessibilityServesAsTitleForUIElementsAttribute] && m_object->isMenuButton()) { + AccessibilityObject* uiElement = static_cast<AccessibilityRenderObject*>(m_object)->menuForMenuButton(); + if (uiElement) + return [NSArray arrayWithObject:uiElement->wrapper()]; + } + + if ([attributeName isEqualToString:NSAccessibilityTitleUIElementAttribute]) { + AccessibilityObject* obj = m_object->titleUIElement(); + if (obj) + return obj->wrapper(); + return nil; + } + + return nil; +} + +- (id)accessibilityFocusedUIElement +{ + RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement(); + + if (!focusedObj) + return nil; + + return focusedObj->wrapper(); +} + +- (id)accessibilityHitTest:(NSPoint)point +{ + RefPtr<AccessibilityObject> axObject = m_object->doAccessibilityHitTest(IntPoint(point)); + if (axObject) + return NSAccessibilityUnignoredAncestor(axObject->wrapper()); + return NSAccessibilityUnignoredAncestor(self); +} + +- (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName +{ + if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) + return YES; + + if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) + return m_object->canSetFocusAttribute(); + + if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) + return m_object->canSetValueAttribute(); + + if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) + return m_object->canSetSelectedAttribute(); + + if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) + return m_object->canSetSelectedChildrenAttribute(); + + if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] || + [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] || + [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) + return m_object->canSetTextRangeAttributes(); + + return NO; +} + +// accessibilityShouldUseUniqueId is an AppKit method we override so that +// objects will be given a unique ID, and therefore allow AppKit to know when they +// become obsolete (e.g. when the user navigates to a new web page, making this one +// unrendered but not deallocated because it is in the back/forward cache). +// It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the +// appropriate place (e.g. dealloc) to remove these non-retained references from +// AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement. +// +// Registering an object is also required for observing notifications. Only registered objects can be observed. +- (BOOL)accessibilityIsIgnored +{ + if (m_object->isAttachment()) + return [[self attachmentView] accessibilityIsIgnored]; + return m_object->accessibilityIsIgnored(); +} + +- (NSArray* )accessibilityParameterizedAttributeNames +{ + if (m_object->isAttachment()) + return nil; + + static NSArray* paramAttrs = nil; + static NSArray* textParamAttrs = nil; + static NSArray* tableParamAttrs = nil; + if (paramAttrs == nil) { + paramAttrs = [[NSArray alloc] initWithObjects: + @"AXUIElementForTextMarker", + @"AXTextMarkerRangeForUIElement", + @"AXLineForTextMarker", + @"AXTextMarkerRangeForLine", + @"AXStringForTextMarkerRange", + @"AXTextMarkerForPosition", + @"AXBoundsForTextMarkerRange", + @"AXAttributedStringForTextMarkerRange", + @"AXTextMarkerRangeForUnorderedTextMarkers", + @"AXNextTextMarkerForTextMarker", + @"AXPreviousTextMarkerForTextMarker", + @"AXLeftWordTextMarkerRangeForTextMarker", + @"AXRightWordTextMarkerRangeForTextMarker", + @"AXLeftLineTextMarkerRangeForTextMarker", + @"AXRightLineTextMarkerRangeForTextMarker", + @"AXSentenceTextMarkerRangeForTextMarker", + @"AXParagraphTextMarkerRangeForTextMarker", + @"AXNextWordEndTextMarkerForTextMarker", + @"AXPreviousWordStartTextMarkerForTextMarker", + @"AXNextLineEndTextMarkerForTextMarker", + @"AXPreviousLineStartTextMarkerForTextMarker", + @"AXNextSentenceEndTextMarkerForTextMarker", + @"AXPreviousSentenceStartTextMarkerForTextMarker", + @"AXNextParagraphEndTextMarkerForTextMarker", + @"AXPreviousParagraphStartTextMarkerForTextMarker", + @"AXStyleTextMarkerRangeForTextMarker", + @"AXLengthForTextMarkerRange", + NSAccessibilityBoundsForRangeParameterizedAttribute, + nil]; + } + + if (textParamAttrs == nil) { + NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs]; + [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute]; + [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute]; + textParamAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + if (tableParamAttrs == nil) { + NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs]; + [tempArray addObject:NSAccessibilityCellForColumnAndRowParameterizedAttribute]; + tableParamAttrs = [[NSArray alloc] initWithArray:tempArray]; + [tempArray release]; + } + + if (m_object->isPasswordField()) + return [NSArray array]; + + if (!m_object->isAccessibilityRenderObject()) + return paramAttrs; + + if (m_object->isTextControl()) + return textParamAttrs; + + if (m_object->isDataTable()) + return tableParamAttrs; + + if (m_object->isMenuRelated()) + return nil; + + return paramAttrs; +} + +- (void)accessibilityPerformPressAction +{ + if (m_object->isAttachment()) + [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction]; + + m_object->press(); +} + +- (void)accessibilityPerformShowMenuAction +{ + // This needs to be performed in an iteration of the run loop that did not start from an AX call. + // If it's the same run loop iteration, the menu open notification won't be sent + [self performSelector:@selector(accessibilityShowContextMenu) withObject:nil afterDelay:0.0]; +} + +- (void)accessibilityShowContextMenu +{ + FrameView* frameView = m_object->documentFrameView(); + if (!frameView) + return; + + // simulate a click in the middle of the object + IntPoint clickPoint = m_object->clickPoint(); + NSPoint nsClickPoint = NSMakePoint(clickPoint.x(), clickPoint.y()); + + NSView* view = nil; + if (m_object->isAttachment()) + view = [self attachmentView]; + else + view = frameView->documentView(); + + if (!view) + return; + + NSPoint nsScreenPoint = [view convertPoint:nsClickPoint toView:nil]; + + // Show the contextual menu for this event. + NSEvent* event = [NSEvent mouseEventWithType:NSRightMouseDown location:nsScreenPoint modifierFlags:0 timestamp:0 windowNumber:[[view window] windowNumber] context:0 eventNumber:0 clickCount:1 pressure:1]; + NSMenu* menu = [view menuForEvent:event]; + + if (menu) + [NSMenu popUpContextMenu:menu withEvent:event forView:view]; +} + +- (void)accessibilityPerformAction:(NSString*)action +{ + if ([action isEqualToString:NSAccessibilityPressAction]) + [self accessibilityPerformPressAction]; + + else if ([action isEqualToString:NSAccessibilityShowMenuAction]) + [self accessibilityPerformShowMenuAction]; +} + +- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName +{ + WebCoreTextMarkerRange* textMarkerRange = nil; + NSNumber* number = nil; + NSString* string = nil; + NSRange range = {0, 0}; + NSArray* array = nil; + + // decode the parameter + if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:value]) + textMarkerRange = (WebCoreTextMarkerRange*) value; + + else if ([value isKindOfClass:[NSNumber self]]) + number = value; + + else if ([value isKindOfClass:[NSString self]]) + string = value; + + else if ([value isKindOfClass:[NSValue self]]) + range = [value rangeValue]; + + else if ([value isKindOfClass:[NSArray self]]) + array = value; + + // handle the command + if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) { + ASSERT(textMarkerRange); + m_object->setSelectedVisiblePositionRange([self visiblePositionRangeForTextMarkerRange:textMarkerRange]); + } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) { + ASSERT(number); + m_object->setFocused([number intValue] != 0); + } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) { + if (!string) + return; + m_object->setValue(string); + } else if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) { + if (!number) + return; + m_object->setSelected([number boolValue]); + } else if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) { + if (!array || m_object->roleValue() != ListBoxRole) + return; + AccessibilityObject::AccessibilityChildrenVector selectedChildren; + convertToVector(array, selectedChildren); + static_cast<AccessibilityListBox*>(m_object)->setSelectedChildren(selectedChildren); + } else if (m_object->isTextControl()) { + if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) { + m_object->setSelectedText(string); + } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) { + m_object->setSelectedTextRange(PlainTextRange(range.location, range.length)); + } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) { + m_object->makeRangeVisible(PlainTextRange(range.location, range.length)); + } + } +} + +static RenderObject* rendererForView(NSView* view) +{ + if (![view conformsToProtocol:@protocol(WebCoreFrameView)]) + return 0; + + NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view; + Frame* frame = [frameView _web_frame]; + if (!frame) + return 0; + + Node* node = frame->document()->ownerElement(); + if (!node) + return 0; + + return node->renderer(); +} + +- (id)_accessibilityParentForSubview:(NSView*)subview +{ + RenderObject* renderer = rendererForView(subview); + if (!renderer) + return nil; + + AccessibilityObject* obj = renderer->document()->axObjectCache()->get(renderer); + if (obj) + return obj->parentObjectUnignored()->wrapper(); + return nil; +} + +- (NSString*)accessibilityActionDescription:(NSString*)action +{ + // we have no custom actions + return NSAccessibilityActionDescription(action); +} + +// The CFAttributedStringType representation of the text associated with this accessibility +// object that is specified by the given range. +- (NSAttributedString*)doAXAttributedStringForRange:(NSRange)range +{ + PlainTextRange textRange = PlainTextRange(range.location, range.length); + VisiblePositionRange visiblePosRange = m_object->visiblePositionRangeForRange(textRange); + return [self doAXAttributedStringForTextMarkerRange:textMarkerRangeFromVisiblePositions(visiblePosRange.start, visiblePosRange.end)]; +} + +// The RTF representation of the text associated with this accessibility object that is +// specified by the given range. +- (NSData*)doAXRTFForRange:(NSRange)range +{ + NSAttributedString* attrString = [self doAXAttributedStringForRange:range]; + return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil]; +} + +- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter +{ + WebCoreTextMarker* textMarker = nil; + WebCoreTextMarkerRange* textMarkerRange = nil; + NSNumber* number = nil; + NSArray* array = nil; + RefPtr<AccessibilityObject> uiElement = 0; + NSPoint point = NSZeroPoint; + bool pointSet = false; + NSRange range = {0, 0}; + bool rangeSet = false; + + // basic parameter validation + if (!m_object || !attribute || !parameter) + return nil; + + // common parameter type check/casting. Nil checks in handlers catch wrong type case. + // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from + // a parameter of the wrong type. + if ([[WebCoreViewFactory sharedFactory] objectIsTextMarker:parameter]) + textMarker = (WebCoreTextMarker*) parameter; + + else if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:parameter]) + textMarkerRange = (WebCoreTextMarkerRange*) parameter; + + else if ([parameter isKindOfClass:[AccessibilityObjectWrapper self]]) + uiElement = [(AccessibilityObjectWrapper*)parameter accessibilityObject]; + + else if ([parameter isKindOfClass:[NSNumber self]]) + number = parameter; + + else if ([parameter isKindOfClass:[NSArray self]]) + array = parameter; + + else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) { + pointSet = true; + point = [(NSValue*)parameter pointValue]; + + } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) { + rangeSet = true; + range = [(NSValue*)parameter rangeValue]; + + } else { + // got a parameter of a type we never use + // NOTE: No ASSERT_NOT_REACHED because this can happen accidentally + // while using accesstool (e.g.), forcing you to start over + return nil; + } + + // Convert values to WebCore types + // FIXME: prepping all of these values as WebCore types is unnecessary in many + // cases. Re-organization of this function or performing the conversion on a + // need basis are possible improvements. + VisiblePosition visiblePos; + if (textMarker) + visiblePos = visiblePositionForTextMarker(textMarker); + int intNumber = [number intValue]; + VisiblePositionRange visiblePosRange; + if (textMarkerRange) + visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange]; + IntPoint webCorePoint = IntPoint(point); + PlainTextRange plainTextRange = PlainTextRange(range.location, range.length); + + // dispatch + if ([attribute isEqualToString: @"AXUIElementForTextMarker"]) + return m_object->accessibilityObjectForPosition(visiblePos)->wrapper(); + + if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"]) { + VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange(); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXLineForTextMarker"]) + return [NSNumber numberWithUnsignedInt:m_object->lineForPosition(visiblePos)]; + + if ([attribute isEqualToString: @"AXTextMarkerRangeForLine"]) { + VisiblePositionRange vpRange = m_object->visiblePositionRangeForLine(intNumber); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXStringForTextMarkerRange"]) + return m_object->stringForVisiblePositionRange(visiblePosRange); + + if ([attribute isEqualToString: @"AXTextMarkerForPosition"]) + return pointSet ? textMarkerForVisiblePosition(m_object->visiblePositionForPoint(webCorePoint)) : nil; + + if ([attribute isEqualToString: @"AXBoundsForTextMarkerRange"]) { + NSRect rect = m_object->boundsForVisiblePositionRange(visiblePosRange); + return [NSValue valueWithRect:rect]; + } + + if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) { + VisiblePosition start = m_object->visiblePositionForIndex(range.location); + VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length); + if (start.isNull() || end.isNull()) + return nil; + NSRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange(start, end)); + return [NSValue valueWithRect:rect]; + } + + if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"]) + return [self doAXAttributedStringForTextMarkerRange:textMarkerRange]; + + if ([attribute isEqualToString: @"AXTextMarkerRangeForUnorderedTextMarkers"]) { + if ([array count] < 2) + return nil; + + WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [array objectAtIndex:0]; + WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [array objectAtIndex:1]; + if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1] + || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2]) + return nil; + + VisiblePosition visiblePos1 = visiblePositionForTextMarker(textMarker1); + VisiblePosition visiblePos2 = visiblePositionForTextMarker(textMarker2); + VisiblePositionRange vpRange = m_object->visiblePositionRangeForUnorderedPositions(visiblePos1, visiblePos2); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXNextTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->nextVisiblePosition(visiblePos)); + + if ([attribute isEqualToString: @"AXPreviousTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->previousVisiblePosition(visiblePos)); + + if ([attribute isEqualToString: @"AXLeftWordTextMarkerRangeForTextMarker"]) { + VisiblePositionRange vpRange = m_object->positionOfLeftWord(visiblePos); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXRightWordTextMarkerRangeForTextMarker"]) { + VisiblePositionRange vpRange = m_object->positionOfRightWord(visiblePos); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXLeftLineTextMarkerRangeForTextMarker"]) { + VisiblePositionRange vpRange = m_object->leftLineVisiblePositionRange(visiblePos); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXRightLineTextMarkerRangeForTextMarker"]) { + VisiblePositionRange vpRange = m_object->rightLineVisiblePositionRange(visiblePos); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXSentenceTextMarkerRangeForTextMarker"]) { + VisiblePositionRange vpRange = m_object->sentenceForPosition(visiblePos); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXParagraphTextMarkerRangeForTextMarker"]) { + VisiblePositionRange vpRange = m_object->paragraphForPosition(visiblePos); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXNextWordEndTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->nextWordEnd(visiblePos)); + + if ([attribute isEqualToString: @"AXPreviousWordStartTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->previousWordStart(visiblePos)); + + if ([attribute isEqualToString: @"AXNextLineEndTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->nextLineEndPosition(visiblePos)); + + if ([attribute isEqualToString: @"AXPreviousLineStartTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->previousLineStartPosition(visiblePos)); + + if ([attribute isEqualToString: @"AXNextSentenceEndTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->nextSentenceEndPosition(visiblePos)); + + if ([attribute isEqualToString: @"AXPreviousSentenceStartTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->previousSentenceStartPosition(visiblePos)); + + if ([attribute isEqualToString: @"AXNextParagraphEndTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->nextParagraphEndPosition(visiblePos)); + + if ([attribute isEqualToString: @"AXPreviousParagraphStartTextMarkerForTextMarker"]) + return textMarkerForVisiblePosition(m_object->previousParagraphStartPosition(visiblePos)); + + if ([attribute isEqualToString: @"AXStyleTextMarkerRangeForTextMarker"]) { + VisiblePositionRange vpRange = m_object->styleRangeForPosition(visiblePos); + return (id)textMarkerRangeFromVisiblePositions(vpRange.start, vpRange.end); + } + + if ([attribute isEqualToString: @"AXLengthForTextMarkerRange"]) { + int length = m_object->lengthForVisiblePositionRange(visiblePosRange); + if (length < 0) + return nil; + return [NSNumber numberWithInt:length]; + } + + if (m_object->isDataTable()) { + if ([attribute isEqualToString:NSAccessibilityCellForColumnAndRowParameterizedAttribute]) { + if (array == nil || [array count] != 2) + return nil; + AccessibilityTableCell* cell = static_cast<AccessibilityTable*>(m_object)->cellForColumnAndRow([[array objectAtIndex:0] unsignedIntValue], [[array objectAtIndex:1] unsignedIntValue]); + if (!cell) + return nil; + + return cell->wrapper(); + } + } + + if (m_object->isTextControl()) { + if ([attribute isEqualToString: (NSString *)kAXLineForIndexParameterizedAttribute]) { + int lineNumber = m_object->doAXLineForIndex(intNumber); + if (lineNumber < 0) + return nil; + return [NSNumber numberWithUnsignedInt:lineNumber]; + } + + if ([attribute isEqualToString: (NSString *)kAXRangeForLineParameterizedAttribute]) { + PlainTextRange textRange = m_object->doAXRangeForLine(intNumber); + return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; + } + + if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute]) + return rangeSet ? (id)(m_object->doAXStringForRange(plainTextRange)) : nil; + + if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) { + if (!pointSet) + return nil; + PlainTextRange textRange = m_object->doAXRangeForPosition(webCorePoint); + return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; + } + + if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) { + PlainTextRange textRange = m_object->doAXRangeForIndex(intNumber); + return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; + } + + if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) { + if (!rangeSet) + return nil; + NSRect rect = m_object->doAXBoundsForRange(plainTextRange); + return [NSValue valueWithRect:rect]; + } + + if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute]) + return rangeSet ? [self doAXRTFForRange:range] : nil; + + if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute]) + return rangeSet ? [self doAXAttributedStringForRange:range] : nil; + + if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) { + PlainTextRange textRange = m_object->doAXStyleRangeForIndex(intNumber); + return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)]; + } + } + + return nil; +} + +- (BOOL)accessibilityShouldUseUniqueId +{ + return m_object->accessibilityShouldUseUniqueId(); +} + +// API that AppKit uses for faster access +- (NSUInteger)accessibilityIndexOfChild:(id)child +{ + const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); + + if (children.isEmpty()) + return [[self renderWidgetChildren] indexOfObject:child]; + + unsigned count = children.size(); + for (unsigned k = 0; k < count; ++k) { + if (children[k]->wrapper() == child) + return k; + } + + return NSNotFound; +} + +- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute +{ + if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); + if (children.isEmpty()) + return [[self renderWidgetChildren] count]; + + return children.size(); + } + + return [super accessibilityArrayAttributeCount:attribute]; +} + +- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount +{ + if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + if (m_object->children().isEmpty()) { + NSArray *children = [self renderWidgetChildren]; + if (!children) + return nil; + + NSUInteger childCount = [children count]; + if (index >= childCount) + return nil; + + NSUInteger arrayLength = min(childCount - index, maxCount); + return [children subarrayWithRange:NSMakeRange(index, arrayLength)]; + } + + const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children(); + unsigned childCount = children.size(); + if (index >= childCount) + return nil; + + unsigned available = min(childCount - index, maxCount); + + NSMutableArray *subarray = [NSMutableArray arrayWithCapacity:available]; + for (unsigned added = 0; added < available; ++index, ++added) + [subarray addObject:children[index]->wrapper()]; + + return subarray; + } + + return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount]; +} + +@end diff --git a/WebCore/page/mac/ChromeMac.mm b/WebCore/page/mac/ChromeMac.mm index 9fc8107..aba3449 100644 --- a/WebCore/page/mac/ChromeMac.mm +++ b/WebCore/page/mac/ChromeMac.mm @@ -1,6 +1,5 @@ -// -*- mode: c++; c-basic-offset: 4 -*- /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008 Apple 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 @@ -22,8 +21,7 @@ #import "Chrome.h" #import "BlockExceptions.h" -#import "Frame.h" -#import "Page.h" +#import "ChromeClient.h" namespace WebCore { @@ -31,25 +29,23 @@ void Chrome::focusNSView(NSView* view) { BEGIN_BLOCK_OBJC_EXCEPTIONS; - WebCoreFrameBridge *bridge = m_page->mainFrame()->bridge(); - NSResponder *firstResponder = [bridge firstResponder]; + NSResponder *firstResponder = client()->firstResponder(); if (firstResponder == view) return; if (![view window] || ![view superview] || ![view acceptsFirstResponder]) return; - [bridge makeFirstResponder:view]; + client()->makeFirstResponder(view); // Setting focus can actually cause a style change which might // remove the view from its superview while it's being made // first responder. This confuses AppKit so we must restore // the old first responder. if (![view superview]) - [bridge makeFirstResponder:firstResponder]; + client()->makeFirstResponder(firstResponder); END_BLOCK_OBJC_EXCEPTIONS; } } // namespace WebCore - diff --git a/WebCore/page/mac/EventHandlerMac.mm b/WebCore/page/mac/EventHandlerMac.mm index ca2f94c..562c1dd 100644 --- a/WebCore/page/mac/EventHandlerMac.mm +++ b/WebCore/page/mac/EventHandlerMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,35 +27,27 @@ #include "EventHandler.h" #include "BlockExceptions.h" +#include "ChromeClient.h" #include "ClipboardMac.h" -#include "Cursor.h" -#include "Document.h" -#include "DragController.h" #include "EventNames.h" -#include "FloatPoint.h" #include "FocusController.h" -#include "FoundationExtras.h" #include "FrameLoader.h" #include "Frame.h" -#include "FrameTree.h" #include "FrameView.h" -#include "HTMLFrameOwnerElement.h" -#include "HTMLFrameSetElement.h" -#include "HitTestRequest.h" -#include "HitTestResult.h" #include "KeyboardEvent.h" #include "MouseEventWithHitTestResults.h" #include "Page.h" #include "PlatformKeyboardEvent.h" -#include "PlatformScrollBar.h" #include "PlatformWheelEvent.h" #include "RenderWidget.h" +#include "Scrollbar.h" #include "Settings.h" -#include "WebCoreFrameBridge.h" namespace WebCore { -using namespace EventNames; +unsigned EventHandler::s_accessKeyModifiers = PlatformKeyboardEvent::CtrlKey | PlatformKeyboardEvent::AltKey; + +const double EventHandler::TextDragDelay = 0.15; static RetainPtr<NSEvent>& currentEvent() { @@ -91,10 +83,10 @@ PassRefPtr<KeyboardEvent> EventHandler::currentKeyboardEvent() const case NSKeyDown: { PlatformKeyboardEvent platformEvent(event); platformEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown); - return new KeyboardEvent(platformEvent, m_frame->document() ? m_frame->document()->defaultView() : 0); + return KeyboardEvent::create(platformEvent, m_frame->document() ? m_frame->document()->defaultView() : 0); } case NSKeyUp: - return new KeyboardEvent(event, m_frame->document() ? m_frame->document()->defaultView() : 0); + return KeyboardEvent::create(event, m_frame->document() ? m_frame->document()->defaultView() : 0); default: return 0; } @@ -103,9 +95,9 @@ PassRefPtr<KeyboardEvent> EventHandler::currentKeyboardEvent() const static inline bool isKeyboardOptionTab(KeyboardEvent* event) { return event - && (event->type() == keydownEvent || event->type() == keypressEvent) - && event->altKey() - && event->keyIdentifier() == "U+0009"; + && (event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent) + && event->altKey() + && event->keyIdentifier() == "U+0009"; } bool EventHandler::invertSenseOfTabsToLinks(KeyboardEvent* event) const @@ -115,7 +107,11 @@ bool EventHandler::invertSenseOfTabsToLinks(KeyboardEvent* event) const bool EventHandler::tabsToAllControls(KeyboardEvent* event) const { - KeyboardUIMode keyboardUIMode = [m_frame->bridge() keyboardUIMode]; + Page* page = m_frame->page(); + if (!page) + return false; + + KeyboardUIMode keyboardUIMode = page->chrome()->client()->keyboardUIMode(); bool handlingOptionTab = isKeyboardOptionTab(event); // If tab-to-links is off, option-tab always highlights all controls @@ -148,12 +144,21 @@ bool EventHandler::needsKeyboardEventDisambiguationQuirks() const return false; // RSS view needs arrow key keypress events. - if (isSafari && document->url().startsWith("feed:", false) || document->url().startsWith("feeds:", false)) + if (isSafari && document->url().protocolIs("feed") || document->url().protocolIs("feeds")) return true; Settings* settings = m_frame->settings(); if (!settings) return false; - return settings->usesDashboardBackwardCompatibilityMode() || settings->needsKeyboardEventDisambiguationQuirks(); + +#if ENABLE(DASHBOARD_SUPPORT) + if (settings->usesDashboardBackwardCompatibilityMode()) + return true; +#endif + + if (settings->needsKeyboardEventDisambiguationQuirks()) + return true; + + return false; } bool EventHandler::keyEvent(NSEvent *event) @@ -184,10 +189,11 @@ void EventHandler::focusDocumentView() if (!page) return; - if (FrameView* frameView = m_frame->view()) - if (NSView *documentView = frameView->getDocumentView()) + if (FrameView* frameView = m_frame->view()) { + if (NSView *documentView = frameView->documentView()) page->chrome()->focusNSView(documentView); - + } + page->focusController()->setFocusedFrame(m_frame); } @@ -239,7 +245,7 @@ bool EventHandler::passMouseDownEventToWidget(Widget* widget) BEGIN_BLOCK_OBJC_EXCEPTIONS; - NSView *nodeView = widget->getView(); + NSView *nodeView = widget->platformWidget(); ASSERT(nodeView); ASSERT([nodeView superview]); NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentEvent().get() locationInWindow] fromView:nil]]; @@ -248,11 +254,15 @@ bool EventHandler::passMouseDownEventToWidget(Widget* widget) return true; } - if ([m_frame->bridge() firstResponder] != view) { + Page* page = m_frame->page(); + if (!page) + return true; + + if (page->chrome()->client()->firstResponder() != view) { // Normally [NSWindow sendEvent:] handles setting the first responder. // But in our case, the event was sent to the view representing the entire web page. if ([currentEvent().get() clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) - [m_frame->bridge() makeFirstResponder:view]; + page->chrome()->client()->makeFirstResponder(view); } // We need to "defer loading" while tracking the mouse, because tearing down the @@ -262,9 +272,9 @@ bool EventHandler::passMouseDownEventToWidget(Widget* widget) // mouse. We should confirm that, and then remove the deferrsLoading // hack entirely. - bool wasDeferringLoading = m_frame->page()->defersLoading(); + bool wasDeferringLoading = page->defersLoading(); if (!wasDeferringLoading) - m_frame->page()->setDefersLoading(true); + page->setDefersLoading(true); ASSERT(!m_sendingEventToSubview); m_sendingEventToSubview = true; @@ -272,7 +282,7 @@ bool EventHandler::passMouseDownEventToWidget(Widget* widget) m_sendingEventToSubview = false; if (!wasDeferringLoading) - m_frame->page()->setDefersLoading(false); + page->setDefersLoading(false); // Remember which view we sent the event to, so we can direct the release event properly. m_mouseDownView = view; @@ -319,7 +329,7 @@ NSView *EventHandler::mouseDownViewIfStillGood() return nil; } FrameView* topFrameView = m_frame->view(); - NSView *topView = topFrameView ? topFrameView->getView() : nil; + NSView *topView = topFrameView ? topFrameView->platformWidget() : nil; if (!topView || !findViewInSubviews(topView, mouseDownView)) { m_mouseDownView = nil; return nil; @@ -350,13 +360,13 @@ bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResult return true; } -Clipboard* EventHandler::createDraggingClipboard() const +PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const { NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; // Must be done before ondragstart adds types and data to the pboard, // also done for security, as it erases data from the last drag [pasteboard declareTypes:[NSArray array] owner:nil]; - return new ClipboardMac(true, pasteboard, ClipboardWritable, m_frame); + return ClipboardMac::create(true, pasteboard, ClipboardWritable, m_frame); } bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&) @@ -442,7 +452,7 @@ bool EventHandler::passWheelEventToWidget(PlatformWheelEvent&, Widget* widget) if ([currentEvent().get() type] != NSScrollWheel || m_sendingEventToSubview || !widget) return false; - NSView *nodeView = widget->getView(); + NSView* nodeView = widget->platformWidget(); ASSERT(nodeView); ASSERT([nodeView superview]); NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentEvent().get() locationInWindow] fromView:nil]]; @@ -545,6 +555,10 @@ void EventHandler::mouseUp(NSEvent *event) */ void EventHandler::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent) { + FrameView* view = m_frame->view(); + if (!view) + return; + BEGIN_BLOCK_OBJC_EXCEPTIONS; m_sendingEventToSubview = false; @@ -553,34 +567,34 @@ void EventHandler::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent) NSEvent *fakeEvent = nil; if (eventType == NSLeftMouseDown) { fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp - location:[initiatingEvent locationInWindow] - modifierFlags:[initiatingEvent modifierFlags] - timestamp:[initiatingEvent timestamp] - windowNumber:[initiatingEvent windowNumber] - context:[initiatingEvent context] - eventNumber:[initiatingEvent eventNumber] - clickCount:[initiatingEvent clickCount] - pressure:[initiatingEvent pressure]]; + location:[initiatingEvent locationInWindow] + modifierFlags:[initiatingEvent modifierFlags] + timestamp:[initiatingEvent timestamp] + windowNumber:[initiatingEvent windowNumber] + context:[initiatingEvent context] + eventNumber:[initiatingEvent eventNumber] + clickCount:[initiatingEvent clickCount] + pressure:[initiatingEvent pressure]]; [NSApp postEvent:fakeEvent atStart:YES]; } else { // eventType == NSKeyDown fakeEvent = [NSEvent keyEventWithType:NSKeyUp - location:[initiatingEvent locationInWindow] - modifierFlags:[initiatingEvent modifierFlags] - timestamp:[initiatingEvent timestamp] - windowNumber:[initiatingEvent windowNumber] - context:[initiatingEvent context] - characters:[initiatingEvent characters] - charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] - isARepeat:[initiatingEvent isARepeat] - keyCode:[initiatingEvent keyCode]]; + location:[initiatingEvent locationInWindow] + modifierFlags:[initiatingEvent modifierFlags] + timestamp:[initiatingEvent timestamp] + windowNumber:[initiatingEvent windowNumber] + context:[initiatingEvent context] + characters:[initiatingEvent characters] + charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] + isARepeat:[initiatingEvent isARepeat] + keyCode:[initiatingEvent keyCode]]; [NSApp postEvent:fakeEvent atStart:YES]; } - // FIXME: We should really get the current modifierFlags here, but there's no way to poll + // FIXME: We should really get the current modifierFlags here, but there's no way to poll // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have // no up-to-date cache of them anywhere. fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved - location:[[m_frame->bridge() window] convertScreenToBase:[NSEvent mouseLocation]] + location:[[view->platformWidget() window] convertScreenToBase:[NSEvent mouseLocation]] modifierFlags:[initiatingEvent modifierFlags] timestamp:[initiatingEvent timestamp] windowNumber:[initiatingEvent windowNumber] @@ -629,9 +643,4 @@ bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& return passSubframeEventToSubframe(mev, subframe); } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults&, PlatformScrollbar* scrollbar) -{ - return passMouseDownEventToWidget(scrollbar); -} - } diff --git a/WebCore/page/mac/FrameMac.mm b/WebCore/page/mac/FrameMac.mm index 8a75797..66e2d04 100644 --- a/WebCore/page/mac/FrameMac.mm +++ b/WebCore/page/mac/FrameMac.mm @@ -1,7 +1,7 @@ /* - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) - * Copyright (C) 2007 Trolltech ASA + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,35 +28,17 @@ #import "config.h" #import "Frame.h" -#import "AXObjectCache.h" -#import "BeforeUnloadEvent.h" #import "BlockExceptions.h" -#import "CSSHelper.h" -#import "Cache.h" -#import "Chrome.h" -#import "ClipboardEvent.h" -#import "ClipboardMac.h" #import "ColorMac.h" #import "Cursor.h" #import "DOMInternal.h" #import "DocumentLoader.h" -#import "EditCommand.h" #import "EditorClient.h" #import "Event.h" -#import "EventNames.h" -#import "FloatRect.h" -#import "FoundationExtras.h" -#import "FrameLoadRequest.h" -#import "FrameLoader.h" #import "FrameLoaderClient.h" -#import "FrameLoaderTypes.h" #import "FramePrivate.h" #import "FrameView.h" #import "GraphicsContext.h" -#import "HTMLDocument.h" -#import "HTMLFormElement.h" -#import "HTMLGenericFormElement.h" -#import "HTMLInputElement.h" #import "HTMLNames.h" #import "HTMLTableCellElement.h" #import "HitTestRequest.h" @@ -66,76 +48,34 @@ #import "MouseEventWithHitTestResults.h" #import "Page.h" #import "PlatformKeyboardEvent.h" -#import "PlatformScrollBar.h" #import "PlatformWheelEvent.h" -#import "Plugin.h" #import "RegularExpression.h" -#import "RenderImage.h" -#import "RenderListItem.h" -#import "RenderPart.h" #import "RenderTableCell.h" -#import "RenderTheme.h" -#import "RenderView.h" -#import "ResourceHandle.h" -#import "Settings.h" +#import "Scrollbar.h" #import "SimpleFontData.h" -#import "SystemTime.h" -#import "TextResourceDecoder.h" #import "UserStyleSheetLoader.h" -#import "WebCoreFrameBridge.h" -#import "WebCoreSystemInterface.h" #import "WebCoreViewFactory.h" -#import "WebDashboardRegion.h" -#import "WebScriptObjectPrivate.h" -#import "kjs_proxy.h" -#import "kjs_window.h" #import "visible_units.h" -#import <Carbon/Carbon.h> -#import <JavaScriptCore/NP_jsobject.h> -#import <JavaScriptCore/npruntime_impl.h> -#undef _webcore_TIMING +#import <Carbon/Carbon.h> +#import <runtime/JSLock.h> -@interface NSObject (WebPlugIn) -- (id)objectForWebScript; -- (NPObject *)createPluginScriptableObject; -@end +#if ENABLE(DASHBOARD_SUPPORT) +#import "WebDashboardRegion.h" +#endif @interface NSView (WebCoreHTMLDocumentView) - (void)drawSingleRect:(NSRect)rect; @end using namespace std; -using namespace KJS::Bindings; -using KJS::JSLock; +using JSC::JSLock; namespace WebCore { -using namespace EventNames; using namespace HTMLNames; -void Frame::setBridge(WebCoreFrameBridge* bridge) -{ - if (d->m_bridge == bridge) - return; - - if (!bridge) { - [d->m_bridge clearFrame]; - HardRelease(d->m_bridge); - d->m_bridge = nil; - return; - } - HardRetain(bridge); - HardRelease(d->m_bridge); - d->m_bridge = bridge; -} - -WebCoreFrameBridge* Frame::bridge() const -{ - return d->m_bridge; -} - // Either get cached regexp or build one that matches any of the labels. // The regexp we build is of the form: (STR1|STR2|STRN) RegularExpression* regExpForLabels(NSArray* labels) @@ -158,17 +98,17 @@ RegularExpression* regExpForLabels(NSArray* labels) if (cacheHit != NSNotFound) result = regExps.at(cacheHit); else { - DeprecatedString pattern("("); + String pattern("("); unsigned int numLabels = [labels count]; unsigned int i; for (i = 0; i < numLabels; i++) { - DeprecatedString label = DeprecatedString::fromNSString((NSString*)[labels objectAtIndex:i]); + String label = [labels objectAtIndex:i]; bool startsWithWordChar = false; bool endsWithWordChar = false; if (label.length() != 0) { - startsWithWordChar = wordRegExp.search(label.at(0)) >= 0; - endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0; + startsWithWordChar = wordRegExp.search(label.substring(0, 1)) >= 0; + endsWithWordChar = wordRegExp.search(label.substring(label.length() - 1, 1)) >= 0; } if (i != 0) @@ -176,13 +116,11 @@ RegularExpression* regExpForLabels(NSArray* labels) // Search for word boundaries only if label starts/ends with "word characters". // If we always searched for word boundaries, this wouldn't work for languages // such as Japanese. - if (startsWithWordChar) { + if (startsWithWordChar) pattern.append("\\b"); - } pattern.append(label); - if (endsWithWordChar) { + if (endsWithWordChar) pattern.append("\\b"); - } } pattern.append(")"); result = new RegularExpression(pattern, false); @@ -225,10 +163,10 @@ NSString* Frame::searchForNSLabelsAboveCell(RegularExpression* regExp, HTMLTable for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) { if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { // For each text chunk, run the regexp - DeprecatedString nodeString = n->nodeValue().deprecatedString(); + String nodeString = n->nodeValue(); int pos = regExp->searchRev(nodeString); if (pos >= 0) - return nodeString.mid(pos, regExp->matchedLength()).getNSString(); + return nodeString.substring(pos, regExp->matchedLength()); } } } @@ -272,13 +210,13 @@ NSString* Frame::searchForLabelsBeforeElement(NSArray* labels, Element* element) searchedCellAbove = true; } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) { // For each text chunk, run the regexp - DeprecatedString nodeString = n->nodeValue().deprecatedString(); + String nodeString = n->nodeValue(); // add 100 for slop, to make it more likely that we'll search whole nodes if (lengthSearched + nodeString.length() > maxCharsSearched) nodeString = nodeString.right(charsSearchedThreshold - lengthSearched); int pos = regExp->searchRev(nodeString); if (pos >= 0) - return nodeString.mid(pos, regExp->matchedLength()).getNSString(); + return nodeString.substring(pos, regExp->matchedLength()); lengthSearched += nodeString.length(); } @@ -297,11 +235,14 @@ NSString* Frame::searchForLabelsBeforeElement(NSArray* labels, Element* element) NSString* Frame::matchLabelsAgainstElement(NSArray* labels, Element* element) { - DeprecatedString name = element->getAttribute(nameAttr).deprecatedString(); + String name = element->getAttribute(nameAttr); + if (name.isEmpty()) + return nil; + // Make numbers and _'s in field names behave like word boundaries, e.g., "address2" - name.replace(RegularExpression("\\d"), " "); + replace(name, RegularExpression("\\d"), " "); name.replace('_', ' '); - + RegularExpression* regExp = regExpForLabels(labels); // Use the largest match we can find in the whole name string int pos; @@ -317,18 +258,18 @@ NSString* Frame::matchLabelsAgainstElement(NSArray* labels, Element* element) bestPos = pos; bestLength = length; } - start = pos+1; + start = pos + 1; } } while (pos != -1); if (bestPos != -1) - return name.mid(bestPos, bestLength).getNSString(); + return name.substring(bestPos, bestLength); return nil; } NSImage* Frame::imageFromRect(NSRect rect) const { - NSView* view = d->m_view->getDocumentView(); + NSView* view = d->m_view->documentView(); if (!view) return nil; if (![view respondsToSelector:@selector(drawSingleRect:)]) @@ -373,10 +314,10 @@ NSImage* Frame::imageFromRect(NSRect rect) const NSImage* Frame::selectionImage(bool forceBlackText) const { - d->m_paintRestriction = forceBlackText ? PaintRestrictionSelectionOnlyBlackText : PaintRestrictionSelectionOnly; + d->m_view->setPaintRestriction(forceBlackText ? PaintRestrictionSelectionOnlyBlackText : PaintRestrictionSelectionOnly); d->m_doc->updateLayout(); NSImage* result = imageFromRect(selectionRect()); - d->m_paintRestriction = PaintRestrictionNone; + d->m_view->setPaintRestriction(PaintRestrictionNone); return result; } @@ -392,11 +333,11 @@ NSImage* Frame::snapshotDragImage(Node* node, NSRect* imageRect, NSRect* element IntRect topLevelRect; NSRect paintingRect = renderer->paintingRootRect(topLevelRect); - d->m_elementToDraw = node; // invoke special sub-tree drawing mode + d->m_view->setNodeToDraw(node); // invoke special sub-tree drawing mode NSImage* result = imageFromRect(paintingRect); renderer->updateDragState(false); d->m_doc->updateLayout(); - d->m_elementToDraw = 0; + d->m_view->setNodeToDraw(0); if (elementRect) *elementRect = topLevelRect; @@ -405,6 +346,24 @@ NSImage* Frame::snapshotDragImage(Node* node, NSRect* imageRect, NSRect* element return result; } +NSImage* Frame::nodeImage(Node* node) const +{ + RenderObject* renderer = node->renderer(); + if (!renderer) + return nil; + + d->m_doc->updateLayout(); // forces style recalc + + IntRect topLevelRect; + NSRect paintingRect = renderer->paintingRootRect(topLevelRect); + + d->m_view->setNodeToDraw(node); // invoke special sub-tree drawing mode + NSImage* result = imageFromRect(paintingRect); + d->m_view->setNodeToDraw(0); + + return result; +} + NSDictionary* Frame::fontAttributesForSelectionStart() const { Node* nodeToRemove; @@ -473,11 +432,22 @@ NSWritingDirection Frame::baseWritingDirectionForSelectionStart() const { NSWritingDirection result = NSWritingDirectionLeftToRight; - Position pos = selectionController()->selection().visibleStart().deepEquivalent(); + Position pos = selection()->selection().visibleStart().deepEquivalent(); Node* node = pos.node(); - if (!node || !node->renderer() || !node->renderer()->containingBlock()) + if (!node) return result; - RenderStyle* style = node->renderer()->containingBlock()->style(); + + RenderObject* renderer = node->renderer(); + if (!renderer) + return result; + + if (!renderer->isBlockFlow()) { + renderer = renderer->containingBlock(); + if (!renderer) + return result; + } + + RenderStyle* style = renderer->style(); if (!style) return result; @@ -493,11 +463,6 @@ NSWritingDirection Frame::baseWritingDirectionForSelectionStart() const return result; } -void Frame::issuePasteCommand() -{ - [d->m_bridge issuePasteCommand]; -} - const short enableRomanKeyboardsOnly = -23; void Frame::setUseSecureKeyboardEntry(bool enable) { @@ -522,6 +487,7 @@ void Frame::setUseSecureKeyboardEntry(bool enable) } } +#if ENABLE(DASHBOARD_SUPPORT) NSMutableDictionary* Frame::dashboardRegionsDictionary() { Document* doc = document(); @@ -559,106 +525,15 @@ NSMutableDictionary* Frame::dashboardRegionsDictionary() return webRegions; } - -void Frame::dashboardRegionsChanged() -{ - NSMutableDictionary *webRegions = dashboardRegionsDictionary(); - [d->m_bridge dashboardRegionsChanged:webRegions]; -} - -void Frame::willPopupMenu(NSMenu * menu) -{ - [d->m_bridge willPopupMenu:menu]; -} - -FloatRect Frame::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect, Node* node) -{ - return [d->m_bridge customHighlightRect:type forLine:lineRect representedNode:node]; -} - -void Frame::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line, Node* node) -{ - [d->m_bridge paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line representedNode:node]; -} +#endif DragImageRef Frame::dragImageForSelection() { - if (!selectionController()->isRange()) + if (!selection()->isRange()) return nil; return selectionImage(); } - -KJS::Bindings::Instance* Frame::createScriptInstanceForWidget(WebCore::Widget* widget) -{ - NSView* aView = widget->getView(); - if (!aView) - return 0; - - void* nativeHandle = aView; - CreateRootObjectFunction createRootObject = RootObject::createRootObject(); - RefPtr<RootObject> rootObject = createRootObject(nativeHandle); - - if ([aView respondsToSelector:@selector(objectForWebScript)]) { - id objectForWebScript = [aView objectForWebScript]; - if (objectForWebScript) - return Instance::createBindingForLanguageInstance(Instance::ObjectiveCLanguage, objectForWebScript, rootObject.release()); - return 0; - } else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) { -#if USE(NPOBJECT) - NPObject* npObject = [aView createPluginScriptableObject]; - if (npObject) { - Instance* instance = Instance::createBindingForLanguageInstance(Instance::CLanguage, npObject, rootObject.release()); - - // -createPluginScriptableObject returns a retained NPObject. The caller is expected to release it. - _NPN_ReleaseObject(npObject); - return instance; - } -#endif - return 0; - } - - jobject applet; - - // Get a pointer to the actual Java applet instance. - if ([d->m_bridge respondsToSelector:@selector(getAppletInView:)]) - applet = [d->m_bridge getAppletInView:aView]; - else - applet = [d->m_bridge pollForAppletInView:aView]; - - if (applet) { - // Wrap the Java instance in a language neutral binding and hand - // off ownership to the APPLET element. - Instance* instance = Instance::createBindingForLanguageInstance(Instance::JavaLanguage, applet, rootObject.release()); - return instance; - } - - return 0; -} - -WebScriptObject* Frame::windowScriptObject() -{ - if (!scriptProxy()->isEnabled()) - return 0; - - if (!d->m_windowScriptObject) { - KJS::JSLock lock; - KJS::JSObject* win = KJS::Window::retrieveWindow(this); - KJS::Bindings::RootObject *root = bindingRootObject(); - d->m_windowScriptObject = [WebScriptObject scriptObjectForJSObject:toRef(win) originRootObject:root rootObject:root]; - } - - return d->m_windowScriptObject.get(); -} - -void Frame::clearPlatformScriptObjects() -{ - if (d->m_windowScriptObject) { - KJS::Bindings::RootObject* root = bindingRootObject(); - [d->m_windowScriptObject.get() _setOriginRootObject:root andRootObject:root]; - } -} - void Frame::setUserStyleSheetLocation(const KURL& url) { delete d->m_userStyleSheetLoader; diff --git a/WebCore/page/mac/PageMac.cpp b/WebCore/page/mac/PageMac.cpp new file mode 100644 index 0000000..7386eea --- /dev/null +++ b/WebCore/page/mac/PageMac.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Apple 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "Page.h" + +namespace WebCore { + +void Page::addSchedulePair(PassRefPtr<SchedulePair> prpPair) +{ + RefPtr<SchedulePair> pair = prpPair; + + if (!m_scheduledRunLoopPairs) + m_scheduledRunLoopPairs.set(new SchedulePairHashSet); + m_scheduledRunLoopPairs->add(pair); + +#ifndef BUILDING_ON_TIGER + for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) { + if (DocumentLoader* documentLoader = frame->loader()->documentLoader()) + documentLoader->schedule(pair.get()); + if (DocumentLoader* documentLoader = frame->loader()->provisionalDocumentLoader()) + documentLoader->schedule(pair.get()); + } +#endif + + // FIXME: make SharedTimerMac use these SchedulePairs. +} + +void Page::removeSchedulePair(PassRefPtr<SchedulePair> prpPair) +{ + ASSERT(m_scheduledRunLoopPairs); + if (!m_scheduledRunLoopPairs) + return; + + RefPtr<SchedulePair> pair = prpPair; + m_scheduledRunLoopPairs->remove(pair); + +#ifndef BUILDING_ON_TIGER + for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) { + if (DocumentLoader* documentLoader = frame->loader()->documentLoader()) + documentLoader->unschedule(pair.get()); + if (DocumentLoader* documentLoader = frame->loader()->provisionalDocumentLoader()) + documentLoader->unschedule(pair.get()); + } +#endif +} + +} // namespace diff --git a/WebCore/page/mac/WebCoreFrameBridge.h b/WebCore/page/mac/WebCoreFrameBridge.h deleted file mode 100644 index a67f1b3..0000000 --- a/WebCore/page/mac/WebCoreFrameBridge.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006, 2007 Apple 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. - */ - -#import <Cocoa/Cocoa.h> -#import <JavaVM/jni.h> -#import <WebCore/WebCoreKeyboardUIMode.h> -#import <WebCore/EditAction.h> -#import <WebCore/FrameLoaderTypes.h> -#import <WebCore/SelectionController.h> -#import <WebCore/TextAffinity.h> -#import <WebCore/TextGranularity.h> - -#if USE(NPOBJECT) -#import <JavaScriptCore/npruntime.h> -#endif - -namespace WebCore { - class Frame; - class HTMLFrameOwnerElement; - class Page; - class String; -} - -@class DOMCSSStyleDeclaration; -@class DOMDocument; -@class DOMDocumentFragment; -@class DOMElement; -@class DOMHTMLInputElement; -@class DOMHTMLTextAreaElement; -@class DOMNode; -@class DOMRange; -@class NSMenu; - -@protocol WebCoreRenderTreeCopier; - -enum WebCoreDeviceType { - WebCoreDeviceScreen, - WebCoreDevicePrinter -}; - -enum WebScrollDirection { - WebScrollUp, - WebScrollDown, - WebScrollLeft, - WebScrollRight -}; - -enum WebScrollGranularity { - WebScrollLine, - WebScrollPage, - WebScrollDocument, - WebScrollWheel -}; - -@protocol WebCoreOpenPanelResultListener <NSObject> -- (void)chooseFilename:(NSString *)fileName; -- (void)cancel; -@end - -// WebCoreFrameBridge objects are used by WebCore to abstract away operations that need -// to be implemented by library clients, for example WebKit. The objects are also -// used in the opposite direction, for simple access to WebCore functions without dealing -// directly with the KHTML C++ classes. - -// A WebCoreFrameBridge creates and holds a reference to a Frame. - -// The WebCoreFrameBridge interface contains methods for use by the non-WebCore side of the bridge. - -@interface WebCoreFrameBridge : NSObject -{ -@public - WebCore::Frame* m_frame; - BOOL _shouldCreateRenderers; - BOOL _closed; -} - -- (WebCore::Frame*)_frame; // underscore to prevent conflict with -[NSView frame] - -+ (WebCoreFrameBridge *)bridgeForDOMDocument:(DOMDocument *)document; - -- (id)init; -- (void)close; - -- (void)clearFrame; - -- (NSURL *)baseURL; - -- (void)installInFrame:(NSView *)view; - -- (BOOL)scrollOverflowInDirection:(WebScrollDirection)direction granularity:(WebScrollGranularity)granularity; - -- (void)createFrameViewWithNSView:(NSView *)view marginWidth:(int)mw marginHeight:(int)mh; - -- (void)reapplyStylesForDeviceType:(WebCoreDeviceType)deviceType; -- (void)forceLayoutAdjustingViewSize:(BOOL)adjustSizeFlag; -- (void)forceLayoutWithMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustSizeFlag; -- (void)sendScrollEvent; -- (BOOL)needsLayout; -- (void)drawRect:(NSRect)rect; -- (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit; -- (NSArray*)computePageRectsWithPrintWidthScaleFactor:(float)printWidthScaleFactor printHeight:(float)printHeight; - -- (NSObject *)copyRenderTree:(id <WebCoreRenderTreeCopier>)copier; -- (NSString *)renderTreeAsExternalRepresentation; - -- (NSURL *)URLWithAttributeString:(NSString *)string; - -- (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form; -- (BOOL)elementDoesAutoComplete:(DOMElement *)element; -- (BOOL)elementIsPassword:(DOMElement *)element; -- (DOMElement *)formForElement:(DOMElement *)element; -- (DOMElement *)currentForm; -- (NSArray *)controlsInForm:(DOMElement *)form; -- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element; -- (NSString *)matchLabels:(NSArray *)labels againstElement:(DOMElement *)element; - -- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection; -- (unsigned)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(unsigned)limit; -- (BOOL)markedTextMatchesAreHighlighted; -- (void)setMarkedTextMatchesAreHighlighted:(BOOL)doHighlight; -- (void)unmarkAllTextMatches; -- (NSArray *)rectsForTextMatches; - -- (void)setTextSizeMultiplier:(float)multiplier; - -- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string; -- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture; -- (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)string; - -- (NSString *)selectedString; - -- (NSString *)stringForRange:(DOMRange *)range; - -- (NSString *)markupStringFromNode:(DOMNode *)node nodes:(NSArray **)nodes; -- (NSString *)markupStringFromRange:(DOMRange *)range nodes:(NSArray **)nodes; - -- (NSRect)caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity; -- (NSRect)firstRectForDOMRange:(DOMRange *)range; -- (void)scrollDOMRangeToVisible:(DOMRange *)range; - -- (NSFont *)fontForSelection:(BOOL *)hasMultipleFonts; - -- (NSString *)stringWithData:(NSData *)data; // using the encoding of the frame's main resource -+ (NSString *)stringWithData:(NSData *)data textEncodingName:(NSString *)textEncodingName; // nil for textEncodingName means Latin-1 - -- (void)setShouldCreateRenderers:(BOOL)shouldCreateRenderers; - -- (void)setBaseBackgroundColor:(NSColor *)backgroundColor; -- (void)setDrawsBackground:(BOOL)drawsBackround; - -- (id)accessibilityTree; - -- (DOMRange *)rangeByAlteringCurrentSelection:(WebCore::SelectionController::EAlteration)alteration direction:(WebCore::SelectionController::EDirection)direction granularity:(WebCore::TextGranularity)granularity; -- (WebCore::TextGranularity)selectionGranularity; -- (void)smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)charRangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString; -- (void)selectNSRange:(NSRange)range; -- (NSRange)selectedNSRange; -- (NSRange)markedTextNSRange; -- (DOMRange *)convertNSRangeToDOMRange:(NSRange)range; -- (NSRange)convertDOMRangeToNSRange:(DOMRange *)range; - -- (DOMDocumentFragment *)documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString; -- (DOMDocumentFragment *)documentFragmentWithText:(NSString *)text inContext:(DOMRange *)context; -- (DOMDocumentFragment *)documentFragmentWithNodesAsParagraphs:(NSArray *)nodes; - -- (void)replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle; -- (void)replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle; -- (void)replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace; -- (void)replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace; - -- (void)insertParagraphSeparatorInQuotedContent; - -- (DOMRange *)characterRangeAtPoint:(NSPoint)point; - -- (DOMCSSStyleDeclaration *)typingStyle; -- (void)setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(WebCore::EditAction)undoAction; - -- (void)dragSourceMovedTo:(NSPoint)windowLoc; -- (void)dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation; - -- (BOOL)getData:(NSData **)data andResponse:(NSURLResponse **)response forURL:(NSString *)URL; -- (void)getAllResourceDatas:(NSArray **)datas andResponses:(NSArray **)responses; - -- (BOOL)canProvideDocumentSource; -- (BOOL)canSaveAsWebArchive; - -- (void)receivedData:(NSData *)data textEncodingName:(NSString *)textEncodingName; - -@end - -// The WebCoreFrameBridge protocol contains methods for use by the WebCore side of the bridge. - -@protocol WebCoreFrameBridge - -- (NSWindow *)window; - -- (NSResponder *)firstResponder; -- (void)makeFirstResponder:(NSResponder *)responder; - -- (void)runOpenPanelForFileButtonWithResultListener:(id <WebCoreOpenPanelResultListener>)resultListener; - -- (jobject)getAppletInView:(NSView *)view; - -// Deprecated, use getAppletInView: instead. -- (jobject)pollForAppletInView:(NSView *)view; - -- (void)issuePasteCommand; - -- (void)setIsSelected:(BOOL)isSelected forView:(NSView *)view; - -- (void)dashboardRegionsChanged:(NSMutableDictionary *)regions; -- (void)willPopupMenu:(NSMenu *)menu; - -- (NSRect)customHighlightRect:(NSString*)type forLine:(NSRect)lineRect representedNode:(WebCore::Node *)node; -- (void)paintCustomHighlight:(NSString*)type forBox:(NSRect)boxRect onLine:(NSRect)lineRect behindText:(BOOL)text entireLine:(BOOL)line representedNode:(WebCore::Node *)node; - -- (WebCore::KeyboardUIMode)keyboardUIMode; - -- (NSString*)imageTitleForFilename:(NSString*)filename size:(NSSize)size; - -@end - -// This interface definition allows those who hold a WebCoreFrameBridge * to call all the methods -// in the WebCoreFrameBridge protocol without requiring the base implementation to supply the methods. -// This idiom is appropriate because WebCoreFrameBridge is an abstract class. - -@interface WebCoreFrameBridge (SubclassResponsibility) <WebCoreFrameBridge> -@end - -// Protocols that make up part of the interfaces above. - -@protocol WebCoreRenderTreeCopier <NSObject> -- (NSObject *)nodeWithName:(NSString *)name position:(NSPoint)p rect:(NSRect)rect view:(NSView *)view children:(NSArray *)children; -@end diff --git a/WebCore/page/mac/WebCoreFrameBridge.mm b/WebCore/page/mac/WebCoreFrameBridge.mm deleted file mode 100644 index d280bcb..0000000 --- a/WebCore/page/mac/WebCoreFrameBridge.mm +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2005, 2006 Alexey Proskuryakov (ap@nypop.com) - * Copyright (C) 2006 David Smith (catfish.man@gmail.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. - */ - -#import "config.h" -#import "WebCoreFrameBridge.h" - -#import "AXObjectCache.h" -#import "CSSHelper.h" -#import "Cache.h" -#import "ClipboardMac.h" -#import "ColorMac.h" -#import "DOMImplementation.h" -#import "DOMInternal.h" -#import "DOMWindow.h" -#import "DeleteSelectionCommand.h" -#import "DocLoader.h" -#import "DocumentFragment.h" -#import "DocumentLoader.h" -#import "DocumentType.h" -#import "Editor.h" -#import "EditorClient.h" -#import "EventHandler.h" -#import "FloatRect.h" -#import "FormDataStreamMac.h" -#import "Frame.h" -#import "FrameLoader.h" -#import "FrameLoaderClient.h" -#import "FrameTree.h" -#import "FrameView.h" -#import "GraphicsContext.h" -#import "HTMLDocument.h" -#import "HTMLFormElement.h" -#import "HTMLInputElement.h" -#import "HTMLNames.h" -#import "HitTestResult.h" -#import "Image.h" -#import "LoaderNSURLExtras.h" -#import "MoveSelectionCommand.h" -#import "Page.h" -#import "PlatformMouseEvent.h" -#import "PlatformScreen.h" -#import "PluginInfoStore.h" -#import "RenderImage.h" -#import "RenderPart.h" -#import "RenderTreeAsText.h" -#import "RenderView.h" -#import "RenderWidget.h" -#import "ReplaceSelectionCommand.h" -#import "ResourceRequest.h" -#import "SelectionController.h" -#import "SimpleFontData.h" -#import "SmartReplace.h" -#import "SubresourceLoader.h" -#import "SystemTime.h" -#import "Text.h" -#import "TextEncoding.h" -#import "TextIterator.h" -#import "TextResourceDecoder.h" -#import "TypingCommand.h" -#import "WebCoreSystemInterface.h" -#import "WebCoreViewFactory.h" -#import "XMLTokenizer.h" -#import "htmlediting.h" -#import "kjs_proxy.h" -#import "kjs_window.h" -#import "markup.h" -#import "visible_units.h" -#import <OpenScripting/ASRegistry.h> -#import <JavaScriptCore/array_instance.h> -#import <JavaScriptCore/date_object.h> -#import <JavaScriptCore/runtime_root.h> -#import <wtf/RetainPtr.h> - -@class NSView; - -using namespace std; -using namespace WebCore; -using namespace HTMLNames; - -using KJS::ArrayInstance; -using KJS::BooleanType; -using KJS::DateInstance; -using KJS::ExecState; -using KJS::GetterSetterType; -using KJS::JSImmediate; -using KJS::JSLock; -using KJS::JSObject; -using KJS::JSValue; -using KJS::NullType; -using KJS::NumberType; -using KJS::ObjectType; -using KJS::SavedBuiltins; -using KJS::SavedProperties; -using KJS::StringType; -using KJS::UndefinedType; -using KJS::UnspecifiedType; -using KJS::Window; - -using KJS::Bindings::RootObject; - -static PassRefPtr<RootObject> createRootObject(void* nativeHandle) -{ - NSView *view = (NSView *)nativeHandle; - WebCoreFrameBridge *bridge = [[WebCoreViewFactory sharedFactory] bridgeForView:view]; - if (!bridge) - return 0; - - Frame* frame = [bridge _frame]; - return frame->createRootObject(nativeHandle, frame->scriptProxy()->globalObject()); -} - -static pthread_t mainThread = 0; - -static void updateRenderingForBindings(ExecState* exec, JSObject* rootObject) -{ - if (pthread_self() != mainThread) - return; - - if (!rootObject) - return; - - Window* window = static_cast<Window*>(rootObject); - if (!window) - return; - - if (Frame* frame = window->impl()->frame()) - if (Document* doc = frame->document()) - doc->updateRendering(); -} - -static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue* jsValue) -{ - NSAppleEventDescriptor* aeDesc = 0; - switch (jsValue->type()) { - case BooleanType: - aeDesc = [NSAppleEventDescriptor descriptorWithBoolean:jsValue->getBoolean()]; - break; - case StringType: - aeDesc = [NSAppleEventDescriptor descriptorWithString:String(jsValue->getString())]; - break; - case NumberType: { - double value = jsValue->getNumber(); - int intValue = (int)value; - if (value == intValue) - aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt32 bytes:&intValue length:sizeof(intValue)]; - else - aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint bytes:&value length:sizeof(value)]; - break; - } - case ObjectType: { - JSObject* object = jsValue->getObject(); - if (object->inherits(&DateInstance::info)) { - DateInstance* date = static_cast<DateInstance*>(object); - double ms = 0; - int tzOffset = 0; - if (date->getTime(ms, tzOffset)) { - CFAbsoluteTime utcSeconds = ms / 1000 - kCFAbsoluteTimeIntervalSince1970; - LongDateTime ldt; - if (noErr == UCConvertCFAbsoluteTimeToLongDateTime(utcSeconds, &ldt)) - aeDesc = [NSAppleEventDescriptor descriptorWithDescriptorType:typeLongDateTime bytes:&ldt length:sizeof(ldt)]; - } - } - else if (object->inherits(&ArrayInstance::info)) { - static HashSet<JSObject*> visitedElems; - if (!visitedElems.contains(object)) { - visitedElems.add(object); - - ArrayInstance* array = static_cast<ArrayInstance*>(object); - aeDesc = [NSAppleEventDescriptor listDescriptor]; - unsigned numItems = array->getLength(); - for (unsigned i = 0; i < numItems; ++i) - [aeDesc insertDescriptor:aeDescFromJSValue(exec, array->getItem(i)) atIndex:0]; - - visitedElems.remove(object); - } - } - if (!aeDesc) { - JSValue* primitive = object->toPrimitive(exec); - if (exec->hadException()) { - exec->clearException(); - return [NSAppleEventDescriptor nullDescriptor]; - } - return aeDescFromJSValue(exec, primitive); - } - break; - } - case UndefinedType: - aeDesc = [NSAppleEventDescriptor descriptorWithTypeCode:cMissingValue]; - break; - default: - LOG_ERROR("Unknown JavaScript type: %d", jsValue->type()); - // no break; - case UnspecifiedType: - case NullType: - case GetterSetterType: - aeDesc = [NSAppleEventDescriptor nullDescriptor]; - break; - } - - return aeDesc; -} - -@implementation WebCoreFrameBridge - -static inline WebCoreFrameBridge *bridge(Frame *frame) -{ - if (!frame) - return nil; - return frame->bridge(); -} - -- (NSString *)domain -{ - Document *doc = m_frame->document(); - if (doc) - return doc->domain(); - return nil; -} - -+ (WebCoreFrameBridge *)bridgeForDOMDocument:(DOMDocument *)document -{ - return bridge([document _document]->frame()); -} - -- (id)init -{ - static bool initializedKJS; - if (!initializedKJS) { - initializedKJS = true; - - mainThread = pthread_self(); - RootObject::setCreateRootObject(createRootObject); - KJS::Bindings::Instance::setDidExecuteFunction(updateRenderingForBindings); - } - - if (!(self = [super init])) - return nil; - - _shouldCreateRenderers = YES; - return self; -} - -- (void)dealloc -{ - ASSERT(_closed); - [super dealloc]; -} - -- (void)finalize -{ - ASSERT(_closed); - [super finalize]; -} - -- (void)close -{ - [self clearFrame]; - _closed = YES; -} - -- (void)addData:(NSData *)data -{ - Document *doc = m_frame->document(); - - // Document may be nil if the part is about to redirect - // as a result of JS executing during load, i.e. one frame - // changing another's location before the frame's document - // has been created. - if (doc) { - doc->setShouldCreateRenderers(_shouldCreateRenderers); - m_frame->loader()->addData((const char *)[data bytes], [data length]); - } -} - -- (BOOL)scrollOverflowInDirection:(WebScrollDirection)direction granularity:(WebScrollGranularity)granularity -{ - if (!m_frame) - return NO; - return m_frame->eventHandler()->scrollOverflow((ScrollDirection)direction, (ScrollGranularity)granularity); -} - -- (void)clearFrame -{ - m_frame = 0; -} - -- (void)createFrameViewWithNSView:(NSView *)view marginWidth:(int)mw marginHeight:(int)mh -{ - // If we own the view, delete the old one - otherwise the render m_frame will take care of deleting the view. - if (m_frame) - m_frame->setView(0); - - FrameView* frameView = new FrameView(m_frame); - m_frame->setView(frameView); - frameView->deref(); - - frameView->setView(view); - if (mw >= 0) - frameView->setMarginWidth(mw); - if (mh >= 0) - frameView->setMarginHeight(mh); -} - -- (NSString *)_stringWithDocumentTypeStringAndMarkupString:(NSString *)markupString -{ - return m_frame->documentTypeString() + markupString; -} - -- (NSArray *)nodesFromList:(Vector<Node*> *)nodesVector -{ - size_t size = nodesVector->size(); - NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:size]; - for (size_t i = 0; i < size; ++i) - [nodes addObject:[DOMNode _wrapNode:(*nodesVector)[i]]]; - return nodes; -} - -- (NSString *)markupStringFromNode:(DOMNode *)node nodes:(NSArray **)nodes -{ - // FIXME: This is never "for interchange". Is that right? See the next method. - Vector<Node*> nodeList; - NSString *markupString = createMarkup([node _node], IncludeNode, nodes ? &nodeList : 0); - if (nodes) - *nodes = [self nodesFromList:&nodeList]; - - return [self _stringWithDocumentTypeStringAndMarkupString:markupString]; -} - -- (NSString *)markupStringFromRange:(DOMRange *)range nodes:(NSArray **)nodes -{ - // FIXME: This is always "for interchange". Is that right? See the previous method. - Vector<Node*> nodeList; - NSString *markupString = createMarkup([range _range], nodes ? &nodeList : 0, AnnotateForInterchange); - if (nodes) - *nodes = [self nodesFromList:&nodeList]; - - return [self _stringWithDocumentTypeStringAndMarkupString:markupString]; -} - -- (NSString *)selectedString -{ - String text = m_frame->selectedText(); - text.replace('\\', m_frame->backslashAsCurrencySymbol()); - return text; -} - -- (NSString *)stringForRange:(DOMRange *)range -{ - // This will give a system malloc'd buffer that can be turned directly into an NSString - unsigned length; - UChar* buf = plainTextToMallocAllocatedBuffer([range _range], length); - - if (!buf) - return [NSString string]; - - UChar backslashAsCurrencySymbol = m_frame->backslashAsCurrencySymbol(); - if (backslashAsCurrencySymbol != '\\') - for (unsigned n = 0; n < length; n++) - if (buf[n] == '\\') - buf[n] = backslashAsCurrencySymbol; - - // Transfer buffer ownership to NSString - return [[[NSString alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES] autorelease]; -} - -- (void)reapplyStylesForDeviceType:(WebCoreDeviceType)deviceType -{ - if (m_frame->view()) - m_frame->view()->setMediaType(deviceType == WebCoreDeviceScreen ? "screen" : "print"); - Document *doc = m_frame->document(); - if (doc) - doc->setPrinting(deviceType == WebCoreDevicePrinter); - m_frame->reapplyStyles(); -} - -- (void)forceLayoutAdjustingViewSize:(BOOL)flag -{ - m_frame->forceLayout(!flag); - if (flag) - m_frame->view()->adjustViewSize(); -} - -- (void)forceLayoutWithMinimumPageWidth:(float)minPageWidth maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)flag -{ - m_frame->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, flag); -} - -- (void)sendScrollEvent -{ - m_frame->sendScrollEvent(); -} - -- (void)drawRect:(NSRect)rect -{ - PlatformGraphicsContext* platformContext = static_cast<PlatformGraphicsContext*>([[NSGraphicsContext currentContext] graphicsPort]); - ASSERT([[NSGraphicsContext currentContext] isFlipped]); - GraphicsContext context(platformContext); - - m_frame->paint(&context, enclosingIntRect(rect)); -} - -// Used by pagination code called from AppKit when a standalone web page is printed. -- (NSArray*)computePageRectsWithPrintWidthScaleFactor:(float)printWidthScaleFactor printHeight:(float)printHeight -{ - NSMutableArray* pages = [NSMutableArray arrayWithCapacity:5]; - if (printWidthScaleFactor <= 0) { - LOG_ERROR("printWidthScaleFactor has bad value %.2f", printWidthScaleFactor); - return pages; - } - - if (printHeight <= 0) { - LOG_ERROR("printHeight has bad value %.2f", printHeight); - return pages; - } - - if (!m_frame || !m_frame->document() || !m_frame->view()) return pages; - RenderView* root = static_cast<RenderView *>(m_frame->document()->renderer()); - if (!root) return pages; - - FrameView* view = m_frame->view(); - if (!view) - return pages; - - NSView* documentView = view->getDocumentView(); - if (!documentView) - return pages; - - float currPageHeight = printHeight; - float docHeight = root->layer()->height(); - float docWidth = root->layer()->width(); - float printWidth = docWidth/printWidthScaleFactor; - - // We need to give the part the opportunity to adjust the page height at each step. - for (float i = 0; i < docHeight; i += currPageHeight) { - float proposedBottom = min(docHeight, i + printHeight); - m_frame->adjustPageHeight(&proposedBottom, i, proposedBottom, i); - currPageHeight = max(1.0f, proposedBottom - i); - for (float j = 0; j < docWidth; j += printWidth) { - NSValue* val = [NSValue valueWithRect: NSMakeRect(j, i, printWidth, currPageHeight)]; - [pages addObject: val]; - } - } - - return pages; -} - -// This is to support the case where a webview is embedded in the view that's being printed -- (void)adjustPageHeightNew:(float *)newBottom top:(float)oldTop bottom:(float)oldBottom limit:(float)bottomLimit -{ - m_frame->adjustPageHeight(newBottom, oldTop, oldBottom, bottomLimit); -} - -- (NSObject *)copyRenderNode:(RenderObject *)node copier:(id <WebCoreRenderTreeCopier>)copier -{ - NSMutableArray *children = [[NSMutableArray alloc] init]; - for (RenderObject *child = node->firstChild(); child; child = child->nextSibling()) { - [children addObject:[self copyRenderNode:child copier:copier]]; - } - - NSString *name = [[NSString alloc] initWithUTF8String:node->renderName()]; - - RenderWidget* renderWidget = node->isWidget() ? static_cast<RenderWidget*>(node) : 0; - Widget* widget = renderWidget ? renderWidget->widget() : 0; - NSView *view = widget ? widget->getView() : nil; - - int nx, ny; - node->absolutePosition(nx, ny); - NSObject *copiedNode = [copier nodeWithName:name - position:NSMakePoint(nx,ny) - rect:NSMakeRect(node->xPos(), node->yPos(), node->width(), node->height()) - view:view - children:children]; - - [name release]; - [children release]; - - return copiedNode; -} - -- (NSObject *)copyRenderTree:(id <WebCoreRenderTreeCopier>)copier -{ - RenderObject *renderer = m_frame->renderer(); - if (!renderer) { - return nil; - } - return [self copyRenderNode:renderer copier:copier]; -} - -- (void)installInFrame:(NSView *)view -{ - // If this isn't the main frame, it must have a render m_frame set, or it - // won't ever get installed in the view hierarchy. - ASSERT(m_frame == m_frame->page()->mainFrame() || m_frame->ownerElement()); - - m_frame->view()->setView(view); - // FIXME: frame tries to do this too, is it needed? - if (m_frame->ownerRenderer()) { - m_frame->ownerRenderer()->setWidget(m_frame->view()); - // Now the render part owns the view, so we don't any more. - } - - m_frame->view()->initScrollbars(); -} - -static HTMLInputElement* inputElementFromDOMElement(DOMElement* element) -{ - Node* node = [element _node]; - if (node->hasTagName(inputTag)) - return static_cast<HTMLInputElement*>(node); - return nil; -} - -static HTMLFormElement *formElementFromDOMElement(DOMElement *element) -{ - Node *node = [element _node]; - // This should not be necessary, but an XSL file on - // maps.google.com crashes otherwise because it is an xslt file - // that contains <form> elements that aren't in any namespace, so - // they come out as generic CML elements - if (node && node->hasTagName(formTag)) { - return static_cast<HTMLFormElement *>(node); - } - return nil; -} - -- (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form -{ - HTMLFormElement *formElement = formElementFromDOMElement(form); - if (formElement) { - Vector<HTMLGenericFormElement*>& elements = formElement->formElements; - AtomicString targetName = name; - for (unsigned int i = 0; i < elements.size(); i++) { - HTMLGenericFormElement *elt = elements[i]; - // Skip option elements, other duds - if (elt->name() == targetName) - return [DOMElement _wrapElement:elt]; - } - } - return nil; -} - -- (BOOL)elementDoesAutoComplete:(DOMElement *)element -{ - HTMLInputElement *inputElement = inputElementFromDOMElement(element); - return inputElement != nil - && inputElement->inputType() == HTMLInputElement::TEXT - && inputElement->autoComplete(); -} - -- (BOOL)elementIsPassword:(DOMElement *)element -{ - HTMLInputElement *inputElement = inputElementFromDOMElement(element); - return inputElement != nil - && inputElement->inputType() == HTMLInputElement::PASSWORD; -} - -- (DOMElement *)formForElement:(DOMElement *)element; -{ - HTMLInputElement *inputElement = inputElementFromDOMElement(element); - if (inputElement) { - HTMLFormElement *formElement = inputElement->form(); - if (formElement) { - return [DOMElement _wrapElement:formElement]; - } - } - return nil; -} - -- (DOMElement *)currentForm -{ - return [DOMElement _wrapElement:m_frame->currentForm()]; -} - -- (NSArray *)controlsInForm:(DOMElement *)form -{ - NSMutableArray *results = nil; - HTMLFormElement *formElement = formElementFromDOMElement(form); - if (formElement) { - Vector<HTMLGenericFormElement*>& elements = formElement->formElements; - for (unsigned int i = 0; i < elements.size(); i++) { - if (elements.at(i)->isEnumeratable()) { // Skip option elements, other duds - DOMElement *de = [DOMElement _wrapElement:elements.at(i)]; - if (!results) { - results = [NSMutableArray arrayWithObject:de]; - } else { - [results addObject:de]; - } - } - } - } - return results; -} - -- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element -{ - return m_frame->searchForLabelsBeforeElement(labels, [element _element]); -} - -- (NSString *)matchLabels:(NSArray *)labels againstElement:(DOMElement *)element -{ - return m_frame->matchLabelsAgainstElement(labels, [element _element]); -} - -- (NSURL *)URLWithAttributeString:(NSString *)string -{ - Document *doc = m_frame->document(); - if (!doc) - return nil; - // FIXME: is parseURL appropriate here? - DeprecatedString rel = parseURL(string).deprecatedString(); - return KURL(doc->completeURL(rel)).getNSURL(); -} - -- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection -{ - return m_frame->findString(string, forward, caseFlag, wrapFlag, startInSelection); -} - -- (unsigned)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag limit:(unsigned)limit -{ - return m_frame->markAllMatchesForText(string, caseFlag, limit); -} - -- (BOOL)markedTextMatchesAreHighlighted -{ - return m_frame->markedTextMatchesAreHighlighted(); -} - -- (void)setMarkedTextMatchesAreHighlighted:(BOOL)doHighlight -{ - m_frame->setMarkedTextMatchesAreHighlighted(doHighlight); -} - -- (void)unmarkAllTextMatches -{ - Document *doc = m_frame->document(); - if (!doc) { - return; - } - doc->removeMarkers(DocumentMarker::TextMatch); -} - -- (NSArray *)rectsForTextMatches -{ - Document *doc = m_frame->document(); - if (!doc) - return [NSArray array]; - - NSMutableArray *result = [NSMutableArray array]; - Vector<IntRect> rects = doc->renderedRectsForMarkers(DocumentMarker::TextMatch); - unsigned count = rects.size(); - for (unsigned index = 0; index < count; ++index) - [result addObject:[NSValue valueWithRect:rects[index]]]; - - return result; -} - -- (void)setTextSizeMultiplier:(float)multiplier -{ - int newZoomFactor = (int)rint(multiplier * 100); - if (m_frame->zoomFactor() == newZoomFactor) { - return; - } - m_frame->setZoomFactor(newZoomFactor); -} - -- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string -{ - return [self stringByEvaluatingJavaScriptFromString:string forceUserGesture:true]; -} - -- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture -{ - ASSERT(m_frame->document()); - - JSValue* result = m_frame->loader()->executeScript(string, forceUserGesture); - - if (!m_frame) // In case the script removed our frame from the page. - return @""; - - // This bizarre set of rules matches behavior from WebKit for Safari 2.0. - // If you don't like it, use -[WebScriptObject evaluateWebScript:] or - // JSEvaluateScript instead, since they have less surprising semantics. - if (!result || !result->isBoolean() && !result->isString() && !result->isNumber()) - return @""; - - JSLock lock; - return String(result->toString(m_frame->scriptProxy()->globalObject()->globalExec())); -} - -- (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)string -{ - ASSERT(m_frame->document()); - ASSERT(m_frame == m_frame->page()->mainFrame()); - JSValue* result = m_frame->loader()->executeScript(string, true); - if (!result) // FIXME: pass errors - return 0; - JSLock lock; - return aeDescFromJSValue(m_frame->scriptProxy()->globalObject()->globalExec(), result); -} - -- (NSRect)caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity -{ - return [node _node]->renderer()->caretRect(offset, static_cast<EAffinity>(affinity)); -} - -- (NSRect)firstRectForDOMRange:(DOMRange *)range -{ - return m_frame->firstRectForRange([range _range]); -} - -- (void)scrollDOMRangeToVisible:(DOMRange *)range -{ - NSRect rangeRect = [self firstRectForDOMRange:range]; - Node *startNode = [[range startContainer] _node]; - - if (startNode && startNode->renderer()) { - RenderLayer *layer = startNode->renderer()->enclosingLayer(); - if (layer) - layer->scrollRectToVisible(enclosingIntRect(rangeRect), RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignToEdgeIfNeeded); - } -} - -- (NSURL *)baseURL -{ - return m_frame->loader()->completeURL(m_frame->document()->baseURL()).getNSURL(); -} - -- (NSString *)stringWithData:(NSData *)data -{ - Document* doc = m_frame->document(); - if (!doc) - return nil; - TextResourceDecoder* decoder = doc->decoder(); - if (!decoder) - return nil; - return decoder->encoding().decode(reinterpret_cast<const char*>([data bytes]), [data length]); -} - -+ (NSString *)stringWithData:(NSData *)data textEncodingName:(NSString *)textEncodingName -{ - WebCore::TextEncoding encoding(textEncodingName); - if (!encoding.isValid()) - encoding = WindowsLatin1Encoding(); - return encoding.decode(reinterpret_cast<const char*>([data bytes]), [data length]); -} - -- (BOOL)needsLayout -{ - return m_frame->view() ? m_frame->view()->needsLayout() : false; -} - -- (NSString *)renderTreeAsExternalRepresentation -{ - return externalRepresentation(m_frame->renderer()).getNSString(); -} - -- (void)setShouldCreateRenderers:(BOOL)f -{ - _shouldCreateRenderers = f; -} - -- (id)accessibilityTree -{ - AXObjectCache::enableAccessibility(); - if (!m_frame || !m_frame->document()) - return nil; - RenderView* root = static_cast<RenderView *>(m_frame->document()->renderer()); - if (!root) - return nil; - return m_frame->document()->axObjectCache()->get(root); -} - -- (void)setBaseBackgroundColor:(NSColor *)backgroundColor -{ - if (m_frame && m_frame->view()) { - Color color = colorFromNSColor([backgroundColor colorUsingColorSpaceName:NSDeviceRGBColorSpace]); - m_frame->view()->setBaseBackgroundColor(color); - } -} - -- (void)setDrawsBackground:(BOOL)drawsBackground -{ - if (m_frame && m_frame->view()) - m_frame->view()->setTransparent(!drawsBackground); -} - -- (DOMRange *)rangeByAlteringCurrentSelection:(SelectionController::EAlteration)alteration direction:(SelectionController::EDirection)direction granularity:(TextGranularity)granularity -{ - if (m_frame->selectionController()->isNone()) - return nil; - - SelectionController selectionController; - selectionController.setSelection(m_frame->selectionController()->selection()); - selectionController.modify(alteration, direction, granularity); - return [DOMRange _wrapRange:selectionController.toRange().get()]; -} - -- (TextGranularity)selectionGranularity -{ - return m_frame->selectionGranularity(); -} - -- (NSRange)convertToNSRange:(Range *)range -{ - int exception = 0; - - if (!range || range->isDetached()) - return NSMakeRange(NSNotFound, 0); - - Element* selectionRoot = m_frame->selectionController()->rootEditableElement(); - Element* scope = selectionRoot ? selectionRoot : m_frame->document()->documentElement(); - - // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view - // that is not inside the current editable region. These checks ensure we don't produce - // potentially invalid data when responding to such requests. - if (range->startContainer(exception) != scope && !range->startContainer(exception)->isDescendantOf(scope)) - return NSMakeRange(NSNotFound, 0); - if(range->endContainer(exception) != scope && !range->endContainer(exception)->isDescendantOf(scope)) - return NSMakeRange(NSNotFound, 0); - - RefPtr<Range> testRange = new Range(scope->document(), scope, 0, range->startContainer(exception), range->startOffset(exception)); - ASSERT(testRange->startContainer(exception) == scope); - int startPosition = TextIterator::rangeLength(testRange.get()); - - testRange->setEnd(range->endContainer(exception), range->endOffset(exception), exception); - ASSERT(testRange->startContainer(exception) == scope); - int endPosition = TextIterator::rangeLength(testRange.get()); - - return NSMakeRange(startPosition, endPosition - startPosition); -} - -- (PassRefPtr<Range>)convertToDOMRange:(NSRange)nsrange -{ - if (nsrange.location > INT_MAX) - return 0; - if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX) - nsrange.length = INT_MAX - nsrange.location; - - // our critical assumption is that we are only called by input methods that - // concentrate on a given area containing the selection - // We have to do this because of text fields and textareas. The DOM for those is not - // directly in the document DOM, so serialization is problematic. Our solution is - // to use the root editable element of the selection start as the positional base. - // That fits with AppKit's idea of an input context. - Element* selectionRoot = m_frame->selectionController()->rootEditableElement(); - Element* scope = selectionRoot ? selectionRoot : m_frame->document()->documentElement(); - return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length); -} - -- (DOMRange *)convertNSRangeToDOMRange:(NSRange)nsrange -{ - return [DOMRange _wrapRange:[self convertToDOMRange:nsrange].get()]; -} - -- (NSRange)convertDOMRangeToNSRange:(DOMRange *)range -{ - return [self convertToNSRange:[range _range]]; -} - -- (void)selectNSRange:(NSRange)range -{ - RefPtr<Range> domRange = [self convertToDOMRange:range]; - if (domRange) - m_frame->selectionController()->setSelection(Selection(domRange.get(), SEL_DEFAULT_AFFINITY)); -} - -- (NSRange)selectedNSRange -{ - return [self convertToNSRange:m_frame->selectionController()->toRange().get()]; -} - -- (DOMRange *)markDOMRange -{ - return [DOMRange _wrapRange:m_frame->mark().toRange().get()]; -} - -- (NSRange)markedTextNSRange -{ - return [self convertToNSRange:m_frame->editor()->compositionRange().get()]; -} - -// Given proposedRange, returns an extended range that includes adjacent whitespace that should -// be deleted along with the proposed range in order to preserve proper spacing and punctuation of -// the text surrounding the deletion. -- (DOMRange *)smartDeleteRangeForProposedRange:(DOMRange *)proposedRange -{ - Node *startContainer = [[proposedRange startContainer] _node]; - Node *endContainer = [[proposedRange endContainer] _node]; - if (startContainer == nil || endContainer == nil) - return nil; - - ASSERT(startContainer->document() == endContainer->document()); - - m_frame->document()->updateLayoutIgnorePendingStylesheets(); - - Position start(startContainer, [proposedRange startOffset]); - Position end(endContainer, [proposedRange endOffset]); - Position newStart = start.upstream().leadingWhitespacePosition(DOWNSTREAM, true); - if (newStart.isNull()) - newStart = start; - Position newEnd = end.downstream().trailingWhitespacePosition(DOWNSTREAM, true); - if (newEnd.isNull()) - newEnd = end; - - newStart = rangeCompliantEquivalent(newStart); - newEnd = rangeCompliantEquivalent(newEnd); - - RefPtr<Range> range = m_frame->document()->createRange(); - int exception = 0; - range->setStart(newStart.node(), newStart.offset(), exception); - range->setEnd(newStart.node(), newStart.offset(), exception); - return [DOMRange _wrapRange:range.get()]; -} - -// Determines whether whitespace needs to be added around aString to preserve proper spacing and -// punctuation when it’s inserted into the receiver’s text over charRange. Returns by reference -// in beforeString and afterString any whitespace that should be added, unless either or both are -// nil. Both are returned as nil if aString is nil or if smart insertion and deletion are disabled. -- (void)smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString -{ - // give back nil pointers in case of early returns - if (beforeString) - *beforeString = nil; - if (afterString) - *afterString = nil; - - // inspect destination - Node *startContainer = [[rangeToReplace startContainer] _node]; - Node *endContainer = [[rangeToReplace endContainer] _node]; - - Position startPos(startContainer, [rangeToReplace startOffset]); - Position endPos(endContainer, [rangeToReplace endOffset]); - - VisiblePosition startVisiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY); - VisiblePosition endVisiblePos = VisiblePosition(endPos, VP_DEFAULT_AFFINITY); - - // this check also ensures startContainer, startPos, endContainer, and endPos are non-null - if (startVisiblePos.isNull() || endVisiblePos.isNull()) - return; - - bool addLeadingSpace = startPos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isStartOfParagraph(startVisiblePos); - if (addLeadingSpace) - if (UChar previousChar = startVisiblePos.previous().characterAfter()) - addLeadingSpace = !isCharacterSmartReplaceExempt(previousChar, true); - - bool addTrailingSpace = endPos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isEndOfParagraph(endVisiblePos); - if (addTrailingSpace) - if (UChar thisChar = endVisiblePos.characterAfter()) - addTrailingSpace = !isCharacterSmartReplaceExempt(thisChar, false); - - // inspect source - bool hasWhitespaceAtStart = false; - bool hasWhitespaceAtEnd = false; - unsigned pasteLength = [pasteString length]; - if (pasteLength > 0) { - NSCharacterSet *whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; - - if ([whiteSet characterIsMember:[pasteString characterAtIndex:0]]) { - hasWhitespaceAtStart = YES; - } - if ([whiteSet characterIsMember:[pasteString characterAtIndex:(pasteLength - 1)]]) { - hasWhitespaceAtEnd = YES; - } - } - - // issue the verdict - if (beforeString && addLeadingSpace && !hasWhitespaceAtStart) - *beforeString = @" "; - if (afterString && addTrailingSpace && !hasWhitespaceAtEnd) - *afterString = @" "; -} - -- (DOMDocumentFragment *)documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString -{ - if (!m_frame || !m_frame->document()) - return 0; - - return [DOMDocumentFragment _wrapDocumentFragment:createFragmentFromMarkup(m_frame->document(), markupString, baseURLString).get()]; -} - -- (DOMDocumentFragment *)documentFragmentWithText:(NSString *)text inContext:(DOMRange *)context -{ - return [DOMDocumentFragment _wrapDocumentFragment:createFragmentFromText([context _range], text).get()]; -} - -- (DOMDocumentFragment *)documentFragmentWithNodesAsParagraphs:(NSArray *)nodes -{ - if (!m_frame || !m_frame->document()) - return 0; - - NSEnumerator *nodeEnum = [nodes objectEnumerator]; - Vector<Node*> nodesVector; - DOMNode *node; - while ((node = [nodeEnum nextObject])) - nodesVector.append([node _node]); - - return [DOMDocumentFragment _wrapDocumentFragment:createFragmentFromNodes(m_frame->document(), nodesVector).get()]; -} - -- (void)replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle -{ - if (m_frame->selectionController()->isNone() || !fragment) - return; - - applyCommand(new ReplaceSelectionCommand(m_frame->document(), [fragment _documentFragment], selectReplacement, smartReplace, matchStyle)); - m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); -} - -- (void)replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle -{ - DOMDocumentFragment *fragment = [DOMDocumentFragment _wrapDocumentFragment:m_frame->document()->createDocumentFragment().get()]; - [fragment appendChild:node]; - [self replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:matchStyle]; -} - -- (void)replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace -{ - DOMDocumentFragment *fragment = [self documentFragmentWithMarkupString:markupString baseURLString:baseURLString]; - [self replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:NO]; -} - -- (void)replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace -{ - [self replaceSelectionWithFragment:[self documentFragmentWithText:text - inContext:[DOMRange _wrapRange:m_frame->selectionController()->toRange().get()]] - selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES]; -} - -- (void)insertParagraphSeparatorInQuotedContent -{ - if (m_frame->selectionController()->isNone()) - return; - - TypingCommand::insertParagraphSeparatorInQuotedContent(m_frame->document()); - m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); -} - -- (VisiblePosition)_visiblePositionForPoint:(NSPoint)point -{ - IntPoint outerPoint(point); - HitTestResult result = m_frame->eventHandler()->hitTestResultAtPoint(outerPoint, true); - Node* node = result.innerNode(); - if (!node) - return VisiblePosition(); - RenderObject* renderer = node->renderer(); - if (!renderer) - return VisiblePosition(); - VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y()); - if (visiblePos.isNull()) - visiblePos = VisiblePosition(Position(node, 0)); - return visiblePos; -} - -- (DOMRange *)characterRangeAtPoint:(NSPoint)point -{ - VisiblePosition position = [self _visiblePositionForPoint:point]; - if (position.isNull()) - return nil; - - VisiblePosition previous = position.previous(); - if (previous.isNotNull()) { - DOMRange *previousCharacterRange = [DOMRange _wrapRange:makeRange(previous, position).get()]; - NSRect rect = [self firstRectForDOMRange:previousCharacterRange]; - if (NSPointInRect(point, rect)) - return previousCharacterRange; - } - - VisiblePosition next = position.next(); - if (next.isNotNull()) { - DOMRange *nextCharacterRange = [DOMRange _wrapRange:makeRange(position, next).get()]; - NSRect rect = [self firstRectForDOMRange:nextCharacterRange]; - if (NSPointInRect(point, rect)) - return nextCharacterRange; - } - - return nil; -} - -- (DOMCSSStyleDeclaration *)typingStyle -{ - if (!m_frame || !m_frame->typingStyle()) - return nil; - return [DOMCSSStyleDeclaration _wrapCSSStyleDeclaration:m_frame->typingStyle()->copy().get()]; -} - -- (void)setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction -{ - if (!m_frame) - return; - m_frame->computeAndSetTypingStyle([style _CSSStyleDeclaration], undoAction); -} - -- (NSFont *)fontForSelection:(BOOL *)hasMultipleFonts -{ - bool multipleFonts = false; - NSFont *font = nil; - if (m_frame) { - const SimpleFontData* fd = m_frame->editor()->fontForSelection(multipleFonts); - if (fd) - font = fd->getNSFont(); - } - - if (hasMultipleFonts) - *hasMultipleFonts = multipleFonts; - return font; -} - -- (void)dragSourceMovedTo:(NSPoint)windowLoc -{ - if (m_frame) { - // FIXME: Fake modifier keys here. - PlatformMouseEvent event(IntPoint(windowLoc), globalPoint(windowLoc, [self window]), - LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime()); - m_frame->eventHandler()->dragSourceMovedTo(event); - } -} - -- (void)dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation -{ - if (m_frame) { - // FIXME: Fake modifier keys here. - PlatformMouseEvent event(IntPoint(windowLoc), globalPoint(windowLoc, [self window]), - LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime()); - m_frame->eventHandler()->dragSourceEndedAt(event, (DragOperation)operation); - } -} - -- (BOOL)getData:(NSData **)data andResponse:(NSURLResponse **)response forURL:(NSString *)url -{ - Document* doc = m_frame->document(); - if (!doc) - return NO; - - CachedResource* resource = doc->docLoader()->cachedResource(url); - if (!resource) - return NO; - - SharedBuffer* buffer = resource->data(); - if (buffer) - *data = [buffer->createNSData() autorelease]; - else - *data = nil; - - *response = resource->response().nsURLResponse(); - return YES; -} - -- (void)getAllResourceDatas:(NSArray **)datas andResponses:(NSArray **)responses -{ - Document* doc = m_frame->document(); - if (!doc) { - NSArray* emptyArray = [NSArray array]; - *datas = emptyArray; - *responses = emptyArray; - return; - } - - const HashMap<String, CachedResource*>& allResources = doc->docLoader()->allCachedResources(); - - NSMutableArray *d = [[NSMutableArray alloc] initWithCapacity:allResources.size()]; - NSMutableArray *r = [[NSMutableArray alloc] initWithCapacity:allResources.size()]; - - HashMap<String, CachedResource*>::const_iterator end = allResources.end(); - for (HashMap<String, CachedResource*>::const_iterator it = allResources.begin(); it != end; ++it) { - SharedBuffer* buffer = it->second->data(); - NSData *data; - if (buffer) - data = buffer->createNSData(); - else - data = [[NSData alloc] init]; - [d addObject:data]; - [data release]; - [r addObject:it->second->response().nsURLResponse()]; - } - - *datas = [d autorelease]; - *responses = [r autorelease]; -} - -- (BOOL)canProvideDocumentSource -{ - String mimeType = m_frame->loader()->responseMIMEType(); - - if (WebCore::DOMImplementation::isTextMIMEType(mimeType) || - Image::supportsType(mimeType) || - PluginInfoStore::supportsMIMEType(mimeType)) - return NO; - - return YES; -} - -- (BOOL)canSaveAsWebArchive -{ - // Currently, all documents that we can view source for - // (HTML and XML documents) can also be saved as web archives - return [self canProvideDocumentSource]; -} - -- (void)receivedData:(NSData *)data textEncodingName:(NSString *)textEncodingName -{ - // Set the encoding. This only needs to be done once, but it's harmless to do it again later. - String encoding; - if (m_frame) - encoding = m_frame->loader()->documentLoader()->overrideEncoding(); - bool userChosen = !encoding.isNull(); - if (encoding.isNull()) - encoding = textEncodingName; - m_frame->loader()->setEncoding(encoding, userChosen); - [self addData:data]; -} - -// ------------------- - -- (Frame*)_frame -{ - return m_frame; -} - -@end diff --git a/WebCore/page/mac/WebCoreFrameView.h b/WebCore/page/mac/WebCoreFrameView.h index a478dca..977b1a7 100644 --- a/WebCore/page/mac/WebCoreFrameView.h +++ b/WebCore/page/mac/WebCoreFrameView.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,32 +23,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -@class WebCoreFrameBridge; +#include "ScrollTypes.h" -// This protocol is a way for an NSScrollView to detect -// that the view it's embedded in is one that should be resized when the -// scroll view is resized. - -typedef enum { - WebCoreScrollbarAuto, - WebCoreScrollbarAlwaysOff, - WebCoreScrollbarAlwaysOn -} WebCoreScrollbarMode; - -@protocol WebCoreFrameView -- (void)setHorizontalScrollingMode:(WebCoreScrollbarMode)mode; -- (void)setVerticalScrollingMode:(WebCoreScrollbarMode)mode; -- (void)setScrollingMode:(WebCoreScrollbarMode)mode; - -- (WebCoreScrollbarMode)horizontalScrollingMode; -- (WebCoreScrollbarMode)verticalScrollingMode; +namespace WebCore { + class Frame; +} +@protocol WebCoreFrameScrollView +- (void)setScrollingModes:(WebCore::ScrollbarMode)hMode vertical:(WebCore::ScrollbarMode)vMode andLock:(BOOL)lock; +- (void)scrollingModes:(WebCore::ScrollbarMode*)hMode vertical:(WebCore::ScrollbarMode*)vMode; - (void)setScrollBarsSuppressed:(BOOL)suppressed repaintOnUnsuppress:(BOOL)repaint; - @end -// This protocol is a way for WebCore to gain access to its information -// about WebKit subclasses of NSView -@protocol WebCoreBridgeHolder -- (WebCoreFrameBridge *) webCoreBridge; +@protocol WebCoreFrameView +- (WebCore::Frame*)_web_frame; @end diff --git a/WebCore/page/mac/WebCoreViewFactory.h b/WebCore/page/mac/WebCoreViewFactory.h index 55514ed..4caef54 100644 --- a/WebCore/page/mac/WebCoreViewFactory.h +++ b/WebCore/page/mac/WebCoreViewFactory.h @@ -23,21 +23,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -@class NSArray; -@class NSDictionary; -@class NSMenu; -@class NSString; -@class NSView; -@class WebCoreFrameBridge; @class WebCoreTextMarker; @class WebCoreTextMarkerRange; @protocol WebCoreViewFactory - (NSArray *)pluginsInfo; // array of id <WebCorePluginInfo> -- (void)refreshPlugins:(BOOL)reloadPages; -- (NSString *)pluginNameForMIMEType:(NSString *)MIMEType; -- (BOOL)pluginSupportsMIMEType:(NSString *)MIMEType; +- (void)refreshPlugins; - (NSString *)inputElementAltText; - (NSString *)resetButtonDefaultLabel; @@ -98,6 +90,8 @@ - (NSString *)defaultLanguageCode; +- (NSString *)imageTitleForFilename:(NSString *)filename width:(int)width height:(int)height; + - (BOOL)objectIsTextMarker:(id)object; - (BOOL)objectIsTextMarkerRange:(id)object; @@ -113,25 +107,29 @@ - (AXUIElementRef)AXUIElementForElement:(id)element; - (void)unregisterUniqueIdForUIElement:(id)element; -- (WebCoreFrameBridge *)bridgeForView:(NSView *)aView; - - (NSString *)AXWebAreaText; - (NSString *)AXLinkText; - (NSString *)AXListMarkerText; - (NSString *)AXImageMapText; - (NSString *)AXHeadingText; +- (NSString *)AXDefinitionListTermText; +- (NSString *)AXDefinitionListDefinitionText; + +- (NSString *)AXButtonActionVerb; +- (NSString *)AXRadioButtonActionVerb; +- (NSString *)AXTextFieldActionVerb; +- (NSString *)AXCheckedCheckBoxActionVerb; +- (NSString *)AXUncheckedCheckBoxActionVerb; +- (NSString *)AXLinkActionVerb; +- (NSString *)multipleFileUploadTextForNumberOfFiles:(unsigned)numberOfFiles; // FTP Directory Related - (NSString *)unknownFileSizeText; @end @interface WebCoreViewFactory : NSObject -{ -} - + (WebCoreViewFactory *)sharedFactory; - @end @interface WebCoreViewFactory (SubclassResponsibility) <WebCoreViewFactory> diff --git a/WebCore/page/mac/WebDashboardRegion.h b/WebCore/page/mac/WebDashboardRegion.h index 81113e9..4963d04 100644 --- a/WebCore/page/mac/WebDashboardRegion.h +++ b/WebCore/page/mac/WebDashboardRegion.h @@ -23,6 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif + +#if ENABLE_DASHBOARD_SUPPORT + typedef enum { WebDashboardRegionTypeNone, WebDashboardRegionTypeCircle, @@ -41,3 +47,5 @@ typedef enum { - (NSRect)dashboardRegionRect; - (WebDashboardRegionType)dashboardRegionType; @end + +#endif diff --git a/WebCore/page/mac/WebDashboardRegion.m b/WebCore/page/mac/WebDashboardRegion.m index 958e599..d2eb07f 100644 --- a/WebCore/page/mac/WebDashboardRegion.m +++ b/WebCore/page/mac/WebDashboardRegion.m @@ -25,6 +25,7 @@ #include "config.h" #import "WebDashboardRegion.h" +#if ENABLE(DASHBOARD_SUPPORT) @implementation WebDashboardRegion - initWithRect:(NSRect)r clip:(NSRect)c type:(WebDashboardRegionType)t { @@ -73,3 +74,4 @@ } @end +#endif diff --git a/WebCore/page/qt/AccessibilityObjectQt.cpp b/WebCore/page/qt/AccessibilityObjectQt.cpp new file mode 100644 index 0000000..b755645 --- /dev/null +++ b/WebCore/page/qt/AccessibilityObjectQt.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Apple Ltd. + * + * 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 "AccessibilityObject.h" + +namespace WebCore { + +bool AccessibilityObject::accessibilityIgnoreAttachment() const +{ + return false; +} + +} // namespace WebCore diff --git a/WebCore/page/qt/EventHandlerQt.cpp b/WebCore/page/qt/EventHandlerQt.cpp index ce16f5b..421caaf 100644 --- a/WebCore/page/qt/EventHandlerQt.cpp +++ b/WebCore/page/qt/EventHandlerQt.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Zack Rusin <zack@kde.org> - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2007 Trolltech ASA + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -45,19 +45,25 @@ #include "MouseEventWithHitTestResults.h" #include "Page.h" #include "PlatformKeyboardEvent.h" -#include "PlatformScrollBar.h" #include "PlatformWheelEvent.h" #include "RenderWidget.h" +#include "Scrollbar.h" #include "NotImplemented.h" +QT_BEGIN_NAMESPACE +extern Q_GUI_EXPORT bool qt_tab_all_widgets; // from qapplication.cpp +QT_END_NAMESPACE + namespace WebCore { -using namespace EventNames; +unsigned EventHandler::s_accessKeyModifiers = PlatformKeyboardEvent::CtrlKey; + +const double EventHandler::TextDragDelay = 0.0; static bool isKeyboardOptionTab(KeyboardEvent* event) { return event - && (event->type() == keydownEvent || event->type() == keypressEvent) + && (event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent) && event->altKey() && event->keyIdentifier() == "U+0009"; } @@ -69,9 +75,7 @@ bool EventHandler::invertSenseOfTabsToLinks(KeyboardEvent* event) const bool EventHandler::tabsToAllControls(KeyboardEvent* event) const { - bool handlingOptionTab = isKeyboardOptionTab(event); - - return handlingOptionTab; + return (isKeyboardOptionTab(event) ? !qt_tab_all_widgets : qt_tab_all_widgets); } void EventHandler::focusDocumentView() @@ -105,9 +109,9 @@ bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& event, Widget* wid return static_cast<FrameView*>(widget)->frame()->eventHandler()->handleWheelEvent(event); } -Clipboard* EventHandler::createDraggingClipboard() const +PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const { - return new ClipboardQt(ClipboardWritable, true); + return ClipboardQt::create(ClipboardWritable, true); } bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe) @@ -128,11 +132,4 @@ bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& return true; } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, PlatformScrollbar* scrollbar) -{ - if (!scrollbar || !scrollbar->isEnabled()) - return false; - return scrollbar->handleMousePressEvent(mev.event()); -} - } diff --git a/WebCore/page/qt/FrameQt.cpp b/WebCore/page/qt/FrameQt.cpp index 7ae1dd9..1bbbff5 100644 --- a/WebCore/page/qt/FrameQt.cpp +++ b/WebCore/page/qt/FrameQt.cpp @@ -1,14 +1,4 @@ /* - * Copyright (C) 2006 Dirk Mueller <mueller@kde.org> - * Copyright (C) 2006 Zack Rusin <zack@kde.org> - * Copyright (C) 2006 George Staikos <staikos@kde.org> - * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org> - * Copyright (C) 2006 Rob Buis <buis@kde.org> - * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> - * Copyright (C) 2007 Trolltech ASA - * - * All rights reserved. - * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -33,91 +23,30 @@ #include "config.h" #include "Frame.h" - -#include "Element.h" -#include "RenderObject.h" -#include "RenderWidget.h" -#include "RenderLayer.h" -#include "Page.h" -#include "Document.h" -#include "HTMLElement.h" -#include "DOMWindow.h" -#include "FrameLoadRequest.h" -#include "FrameLoaderClientQt.h" -#include "DOMImplementation.h" -#include "ResourceHandleInternal.h" -#include "Document.h" -#include "Settings.h" -#include "Plugin.h" -#include "FrameView.h" #include "FramePrivate.h" -#include "GraphicsContext.h" -#include "HTMLDocument.h" -#include "ResourceHandle.h" -#include "FrameLoader.h" -#include "PlatformMouseEvent.h" -#include "PlatformKeyboardEvent.h" -#include "PlatformWheelEvent.h" -#include "MouseEventWithHitTestResults.h" -#include "SelectionController.h" -#include "kjs_proxy.h" -#include "TypingCommand.h" -#include "JSLock.h" -#include "kjs_window.h" -#include "runtime_root.h" -#include "runtime.h" -#include <QScrollArea> -#include "NotImplemented.h" +#include "UserStyleSheetLoader.h" namespace WebCore { -// FIXME: Turned this off to fix buildbot. This function be either deleted or used. -#if 0 -static void doScroll(const RenderObject* r, bool isHorizontal, int multiplier) -{ - // FIXME: The scrolling done here should be done in the default handlers - // of the elements rather than here in the part. - if (!r) - return; - - //broken since it calls scroll on scrollbars - //and we have none now - //r->scroll(direction, KWQScrollWheel, multiplier); - if (!r->layer()) - return; - - int x = r->layer()->scrollXOffset(); - int y = r->layer()->scrollYOffset(); - if (isHorizontal) - x += multiplier; - else - y += multiplier; - - r->layer()->scrollToOffset(x, y, true, true); -} -#endif - -KJS::Bindings::Instance* Frame::createScriptInstanceForWidget(WebCore::Widget* widget) +DragImageRef Frame::dragImageForSelection() { - QWidget* nativeWidget = widget->nativeWidget(); - if (!nativeWidget) - return 0; - return KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::QtLanguage, - nativeWidget, - bindingRootObject()); + return 0; } -void Frame::clearPlatformScriptObjects() +void Frame::setUserStyleSheetLocation(const KURL& url) { + delete d->m_userStyleSheetLoader; + d->m_userStyleSheetLoader = 0; + if (d->m_doc && d->m_doc->docLoader()) + d->m_userStyleSheetLoader = new UserStyleSheetLoader(d->m_doc, url.string()); } -DragImageRef Frame::dragImageForSelection() -{ - return 0; -} - -void Frame::dashboardRegionsChanged() +void Frame::setUserStyleSheet(const String& styleSheet) { + delete d->m_userStyleSheetLoader; + d->m_userStyleSheetLoader = 0; + if (d->m_doc) + d->m_doc->setUserStyleSheet(styleSheet); } } diff --git a/WebCore/page/win/AXObjectCacheWin.cpp b/WebCore/page/win/AXObjectCacheWin.cpp new file mode 100644 index 0000000..da30ac5 --- /dev/null +++ b/WebCore/page/win/AXObjectCacheWin.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "AXObjectCache.h" + +#include "AccessibilityObject.h" + +namespace WebCore { + +void AXObjectCache::detachWrapper(AccessibilityObject* obj) +{ + // On Windows, AccessibilityObjects are created when get_accChildCount is + // called, but they are not wrapped until get_accChild is called, so this + // object may not have a wrapper. + if (AccessibilityObjectWrapper* wrapper = obj->wrapper()) + wrapper->detach(); +} + +void AXObjectCache::attachWrapper(AccessibilityObject*) +{ + // On Windows, AccessibilityObjects are wrapped when the accessibility + // software requests them via get_accChild. +} + +void AXObjectCache::postNotification(RenderObject*, const String&) +{ +} + +void AXObjectCache::postNotificationToElement(RenderObject*, const String&) +{ +} + +void AXObjectCache::handleFocusedUIElementChanged() +{ +} + +} // namespace WebCore diff --git a/WebCore/page/win/AccessibilityObjectWin.cpp b/WebCore/page/win/AccessibilityObjectWin.cpp new file mode 100644 index 0000000..e309ac8 --- /dev/null +++ b/WebCore/page/win/AccessibilityObjectWin.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "AccessibilityObject.h" + +namespace WebCore { + +bool AccessibilityObject::accessibilityIgnoreAttachment() const +{ + return false; +} + +} // namespace WebCore diff --git a/WebCore/page/win/AccessibilityObjectWrapperWin.h b/WebCore/page/win/AccessibilityObjectWrapperWin.h new file mode 100644 index 0000000..779443c --- /dev/null +++ b/WebCore/page/win/AccessibilityObjectWrapperWin.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 AccessibilityObjectWrapperWin_h +#define AccessibilityObjectWrapperWin_h + +namespace WebCore { + + class AccessibilityObject; + + class AccessibilityObjectWrapper : public IUnknown { + public: + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) = 0; + virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; + virtual ULONG STDMETHODCALLTYPE Release(void) = 0; + + virtual void detach() = 0; + bool attached() const { return m_object; } + AccessibilityObject* accessibilityObject() const { return m_object; } + + protected: + AccessibilityObjectWrapper(AccessibilityObject* obj) : m_object(obj) { } + AccessibilityObjectWrapper() : m_object(0) { } + + AccessibilityObject* m_object; + }; + +} // namespace WebCore + +#endif // AccessibilityObjectWrapperWin_h diff --git a/WebCore/page/win/EventHandlerWin.cpp b/WebCore/page/win/EventHandlerWin.cpp index 5e349e3..bfd2b02 100644 --- a/WebCore/page/win/EventHandlerWin.cpp +++ b/WebCore/page/win/EventHandlerWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,14 +36,19 @@ #include "HitTestResult.h" #include "MouseEventWithHitTestResults.h" #include "Page.h" -#include "PlatformScrollbar.h" +#include "PlatformKeyboardEvent.h" #include "PlatformWheelEvent.h" +#include "Scrollbar.h" #include "SelectionController.h" #include "WCDataObject.h" #include "NotImplemented.h" namespace WebCore { +unsigned EventHandler::s_accessKeyModifiers = PlatformKeyboardEvent::AltKey; + +const double EventHandler::TextDragDelay = 0.0; + bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe) { subframe->eventHandler()->handleMousePressEvent(mev.event()); @@ -72,13 +77,6 @@ bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& wheelEvent, Widget return static_cast<FrameView*>(widget)->frame()->eventHandler()->handleWheelEvent(wheelEvent); } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, PlatformScrollbar* scrollbar) -{ - if (!scrollbar || !scrollbar->isEnabled()) - return false; - return scrollbar->handleMousePressEvent(mev.event()); -} - bool EventHandler::tabsToAllControls(KeyboardEvent*) const { return true; @@ -89,11 +87,11 @@ bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const return event.activatedWebView(); } -Clipboard* EventHandler::createDraggingClipboard() const +PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const { COMPtr<WCDataObject> dataObject; WCDataObject::createInstance(&dataObject); - return new ClipboardWin(true, dataObject.get(), ClipboardWritable); + return ClipboardWin::create(true, dataObject.get(), ClipboardWritable); } void EventHandler::focusDocumentView() diff --git a/WebCore/page/win/FrameCGWin.cpp b/WebCore/page/win/FrameCGWin.cpp new file mode 100644 index 0000000..ad22967 --- /dev/null +++ b/WebCore/page/win/FrameCGWin.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "FrameWin.h" + +#include <windows.h> + +#include "FrameView.h" +#include "GraphicsContext.h" +#include "Settings.h" + +#include <CoreGraphics/CoreGraphics.h> + +using std::min; + +namespace WebCore { + +static void drawRectIntoContext(IntRect rect, FrameView* view, GraphicsContext* gc) +{ + IntSize offset = view->scrollOffset(); + rect.move(-offset.width(), -offset.height()); + rect = view->convertToContainingWindow(rect); + + gc->concatCTM(AffineTransform().translate(-rect.x(), -rect.y())); + + view->paint(gc, rect); +} + +HBITMAP imageFromSelection(Frame* frame, bool forceBlackText) +{ + frame->view()->setPaintRestriction(forceBlackText ? PaintRestrictionSelectionOnlyBlackText : PaintRestrictionSelectionOnly); + FloatRect fr = frame->selectionRect(); + IntRect ir(static_cast<int>(fr.x()), static_cast<int>(fr.y()), + static_cast<int>(fr.width()), static_cast<int>(fr.height())); + + void* bits; + HDC hdc = CreateCompatibleDC(0); + int w = ir.width(); + int h = ir.height(); + BITMAPINFO bmp = { { sizeof(BITMAPINFOHEADER), w, h, 1, 32 } }; + + HBITMAP hbmp = CreateDIBSection(0, &bmp, DIB_RGB_COLORS, static_cast<void**>(&bits), 0, 0); + HBITMAP hbmpOld = static_cast<HBITMAP>(SelectObject(hdc, hbmp)); + CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = CGBitmapContextCreate(static_cast<void*>(bits), w, h, + 8, w * sizeof(RGBQUAD), deviceRGB, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); + CGColorSpaceRelease(deviceRGB); + CGContextSaveGState(context); + + GraphicsContext gc(context); + + frame->document()->updateLayout(); + drawRectIntoContext(ir, frame->view(), &gc); + + CGContextRelease(context); + SelectObject(hdc, hbmpOld); + DeleteDC(hdc); + + frame->view()->setPaintRestriction(PaintRestrictionNone); + + return hbmp; +} + +} // namespace WebCore diff --git a/WebCore/page/InspectorClient.h b/WebCore/page/win/FrameCairoWin.cpp index ec1ee92..a645a10 100644 --- a/WebCore/page/InspectorClient.h +++ b/WebCore/page/win/FrameCairoWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,37 +23,20 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef InspectorClient_h -#define InspectorClient_h +#include "config.h" +#include "FrameWin.h" -namespace WebCore { - -class Node; -class Page; -class String; - -class InspectorClient { -public: - virtual ~InspectorClient() { } - - virtual void inspectorDestroyed() = 0; - - virtual Page* createPage() = 0; +#include "EditorClient.h" +#include "NotImplemented.h" - virtual String localizedStringsURL() = 0; +using std::min; - virtual void showWindow() = 0; - virtual void closeWindow() = 0; - - virtual void attachWindow() = 0; - virtual void detachWindow() = 0; - - virtual void highlight(Node*) = 0; - virtual void hideHighlight() = 0; +namespace WebCore { - virtual void inspectedURLChanged(const String& newURL) = 0; -}; +HBITMAP imageFromSelection(Frame* frame, bool forceBlackText) +{ + notImplemented(); + return 0; +} } // namespace WebCore - -#endif // !defined(InspectorClient_h) diff --git a/WebCore/page/win/FrameWin.cpp b/WebCore/page/win/FrameWin.cpp new file mode 100644 index 0000000..536f5d8 --- /dev/null +++ b/WebCore/page/win/FrameWin.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "runtime.h" +#include "FrameWin.h" + +#include "AffineTransform.h" +#include "FloatRect.h" +#include "Document.h" +#include "FramePrivate.h" +#include "RenderView.h" +#include "Settings.h" + +using std::min; + +namespace WebCore { + +void computePageRectsForFrame(Frame* frame, const IntRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, Vector<IntRect>& pages, int& outPageHeight) +{ + ASSERT(frame); + + pages.clear(); + outPageHeight = 0; + + if (!frame->document() || !frame->view() || !frame->document()->renderer()) + return; + + RenderView* root = static_cast<RenderView*>(frame->document()->renderer()); + + if (!root) { + LOG_ERROR("document to be printed has no renderer"); + return; + } + + if (userScaleFactor <= 0) { + LOG_ERROR("userScaleFactor has bad value %.2f", userScaleFactor); + return; + } + + float ratio = static_cast<float>(printRect.height()) / static_cast<float>(printRect.width()); + + float pageWidth = static_cast<float>(root->docWidth()); + float pageHeight = pageWidth * ratio; + outPageHeight = static_cast<int>(pageHeight); // this is the height of the page adjusted by margins + pageHeight -= (headerHeight + footerHeight); + + if (pageHeight <= 0) { + LOG_ERROR("pageHeight has bad value %.2f", pageHeight); + return; + } + + float currPageHeight = pageHeight / userScaleFactor; + float docHeight = root->layer()->height(); + float docWidth = root->layer()->width(); + float currPageWidth = pageWidth / userScaleFactor; + + + // always return at least one page, since empty files should print a blank page + float printedPagesHeight = 0.0f; + do { + float proposedBottom = min(docHeight, printedPagesHeight + pageHeight); + frame->adjustPageHeight(&proposedBottom, printedPagesHeight, proposedBottom, printedPagesHeight); + currPageHeight = max(1.0f, proposedBottom - printedPagesHeight); + + pages.append(IntRect(0, printedPagesHeight, currPageWidth, currPageHeight)); + printedPagesHeight += currPageHeight; + } while (printedPagesHeight < docHeight); +} + +DragImageRef Frame::dragImageForSelection() +{ + if (selection()->isRange()) + return imageFromSelection(this, false); + + return 0; +} + +} // namespace WebCore diff --git a/WebCore/page/win/FrameWin.h b/WebCore/page/win/FrameWin.h new file mode 100644 index 0000000..405c7b2 --- /dev/null +++ b/WebCore/page/win/FrameWin.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2006, 2007 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 FrameWin_H +#define FrameWin_H + +#include "Frame.h" + +// Forward declared so we don't need wingdi.h. +typedef struct HBITMAP__* HBITMAP; + +namespace WebCore { + + HBITMAP imageFromSelection(Frame* frame, bool forceWhiteText); + void computePageRectsForFrame(Frame*, const IntRect& printRect, float headerHeight, float footerHeight, float userScaleFactor,Vector<IntRect>& pages, int& pageHeight); + +} + +#endif diff --git a/WebCore/page/win/PageWin.cpp b/WebCore/page/win/PageWin.cpp new file mode 100644 index 0000000..f4c744a --- /dev/null +++ b/WebCore/page/win/PageWin.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2006, 2007 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "Page.h" + +#include "Frame.h" +#include "FrameView.h" +#include "FloatRect.h" +#include <windows.h> + +namespace WebCore { + +HINSTANCE Page::s_instanceHandle = 0; + +} // namespace WebCore diff --git a/WebCore/page/wx/AccessibilityObjectWx.cpp b/WebCore/page/wx/AccessibilityObjectWx.cpp new file mode 100644 index 0000000..b755645 --- /dev/null +++ b/WebCore/page/wx/AccessibilityObjectWx.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 Apple Ltd. + * + * 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 "AccessibilityObject.h" + +namespace WebCore { + +bool AccessibilityObject::accessibilityIgnoreAttachment() const +{ + return false; +} + +} // namespace WebCore diff --git a/WebCore/page/wx/EventHandlerWx.cpp b/WebCore/page/wx/EventHandlerWx.cpp index 6670c18..ce4473f 100644 --- a/WebCore/page/wx/EventHandlerWx.cpp +++ b/WebCore/page/wx/EventHandlerWx.cpp @@ -24,20 +24,25 @@ */ #include "config.h" +#include "EventHandler.h" #include "ClipboardWx.h" -#include "EventHandler.h" #include "FocusController.h" #include "Frame.h" #include "FrameView.h" #include "KeyboardEvent.h" #include "MouseEventWithHitTestResults.h" #include "Page.h" -#include "PlatformScrollBar.h" +#include "PlatformKeyboardEvent.h" #include "RenderWidget.h" +#include "Scrollbar.h" namespace WebCore { +unsigned EventHandler::s_accessKeyModifiers = PlatformKeyboardEvent::AltKey; + +const double EventHandler::TextDragDelay = 0.0; + bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe) { return passSubframeEventToSubframe(mev, subframe); @@ -53,11 +58,6 @@ bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& return passSubframeEventToSubframe(mev, subframe); } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mouseEvent, PlatformScrollbar* scrollbar) -{ - return passMouseDownEventToWidget(scrollbar); -} - bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event) { // Figure out which view to send the event to. @@ -86,9 +86,9 @@ bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const return false; } -Clipboard* EventHandler::createDraggingClipboard() const +PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const { - return new ClipboardWx(ClipboardWritable, true); + return ClipboardWx::create(ClipboardWritable, true); } } |