summaryrefslogtreecommitdiffstats
path: root/WebCore/page
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/page')
-rw-r--r--WebCore/page/AXObjectCache.h102
-rw-r--r--WebCore/page/AbstractView.idl34
-rw-r--r--WebCore/page/AnimationController.cpp617
-rw-r--r--WebCore/page/AnimationController.h57
-rw-r--r--WebCore/page/BarInfo.cpp72
-rw-r--r--WebCore/page/BarInfo.h57
-rw-r--r--WebCore/page/BarInfo.idl35
-rw-r--r--WebCore/page/Chrome.cpp398
-rw-r--r--WebCore/page/Chrome.h139
-rw-r--r--WebCore/page/ChromeClient.h113
-rw-r--r--WebCore/page/Console.cpp98
-rw-r--r--WebCore/page/Console.h58
-rw-r--r--WebCore/page/Console.idl38
-rw-r--r--WebCore/page/ContextMenuClient.h59
-rw-r--r--WebCore/page/ContextMenuController.cpp298
-rw-r--r--WebCore/page/ContextMenuController.h61
-rw-r--r--WebCore/page/DOMSelection.cpp427
-rw-r--r--WebCore/page/DOMSelection.h102
-rw-r--r--WebCore/page/DOMSelection.idl70
-rw-r--r--WebCore/page/DOMWindow.cpp740
-rw-r--r--WebCore/page/DOMWindow.h177
-rw-r--r--WebCore/page/DOMWindow.idl330
-rw-r--r--WebCore/page/DragActions.h66
-rw-r--r--WebCore/page/DragClient.h81
-rw-r--r--WebCore/page/DragController.cpp769
-rw-r--r--WebCore/page/DragController.h131
-rw-r--r--WebCore/page/EditorClient.h145
-rw-r--r--WebCore/page/EventHandler.cpp1866
-rw-r--r--WebCore/page/EventHandler.h325
-rw-r--r--WebCore/page/FocusController.cpp310
-rw-r--r--WebCore/page/FocusController.h64
-rw-r--r--WebCore/page/FocusDirection.h36
-rw-r--r--WebCore/page/Frame.cpp1886
-rw-r--r--WebCore/page/Frame.h391
-rw-r--r--WebCore/page/FrameLoadRequest.h74
-rw-r--r--WebCore/page/FramePrivate.h134
-rw-r--r--WebCore/page/FrameTree.cpp296
-rw-r--r--WebCore/page/FrameTree.h87
-rw-r--r--WebCore/page/FrameView.cpp1092
-rw-r--r--WebCore/page/FrameView.h173
-rw-r--r--WebCore/page/GlobalHistory.h37
-rw-r--r--WebCore/page/History.cpp77
-rw-r--r--WebCore/page/History.h56
-rw-r--r--WebCore/page/History.idl41
-rw-r--r--WebCore/page/InspectorClient.h59
-rw-r--r--WebCore/page/InspectorController.cpp1653
-rw-r--r--WebCore/page/InspectorController.h177
-rw-r--r--WebCore/page/MouseEventWithHitTestResults.cpp74
-rw-r--r--WebCore/page/MouseEventWithHitTestResults.h50
-rw-r--r--WebCore/page/Page.cpp363
-rw-r--r--WebCore/page/Page.h173
-rw-r--r--WebCore/page/Plugin.h42
-rw-r--r--WebCore/page/Screen.cpp107
-rw-r--r--WebCore/page/Screen.h62
-rw-r--r--WebCore/page/Screen.idl43
-rw-r--r--WebCore/page/Settings.cpp326
-rw-r--r--WebCore/page/Settings.h193
-rw-r--r--WebCore/page/WindowFeatures.cpp188
-rw-r--r--WebCore/page/WindowFeatures.h83
-rw-r--r--WebCore/page/gtk/DragControllerGtk.cpp66
-rw-r--r--WebCore/page/gtk/EventHandlerGtk.cpp127
-rw-r--r--WebCore/page/gtk/FrameGtk.cpp58
-rw-r--r--WebCore/page/inspector/ConsolePanel.js459
-rw-r--r--WebCore/page/inspector/Database.js129
-rw-r--r--WebCore/page/inspector/DatabasePanel.js462
-rw-r--r--WebCore/page/inspector/DocumentPanel.js836
-rw-r--r--WebCore/page/inspector/FontPanel.js103
-rw-r--r--WebCore/page/inspector/ImagePanel.js74
-rw-r--r--WebCore/page/inspector/Images/alternateTableRows.pngbin0 -> 3445 bytes
-rw-r--r--WebCore/page/inspector/Images/attachedShadow.pngbin0 -> 3458 bytes
-rw-r--r--WebCore/page/inspector/Images/backNormal.pngbin0 -> 758 bytes
-rw-r--r--WebCore/page/inspector/Images/bottomShadow.pngbin0 -> 3450 bytes
-rw-r--r--WebCore/page/inspector/Images/breadcrumbBackground.pngbin0 -> 3460 bytes
-rw-r--r--WebCore/page/inspector/Images/checker.pngbin0 -> 3471 bytes
-rw-r--r--WebCore/page/inspector/Images/console.pngbin0 -> 1953 bytes
-rw-r--r--WebCore/page/inspector/Images/darkShadow.pngbin0 -> 3456 bytes
-rw-r--r--WebCore/page/inspector/Images/database.pngbin0 -> 4434 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseBrowserViewNormal.pngbin0 -> 574 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseBrowserViewNormalSelected.pngbin0 -> 575 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseBrowserViewSmall.pngbin0 -> 569 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseBrowserViewSmallSelected.pngbin0 -> 571 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseQueryViewNormal.pngbin0 -> 630 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseQueryViewNormalSelected.pngbin0 -> 626 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseQueryViewSmall.pngbin0 -> 614 bytes
-rw-r--r--WebCore/page/inspector/Images/databaseQueryViewSmallSelected.pngbin0 -> 609 bytes
-rw-r--r--WebCore/page/inspector/Images/disclosureDownPressed.pngbin0 -> 212 bytes
-rw-r--r--WebCore/page/inspector/Images/disclosureRightDown.pngbin0 -> 261 bytes
-rw-r--r--WebCore/page/inspector/Images/disclosureRightPressed.pngbin0 -> 233 bytes
-rw-r--r--WebCore/page/inspector/Images/document.pngbin0 -> 799 bytes
-rw-r--r--WebCore/page/inspector/Images/domViewNormal.pngbin0 -> 604 bytes
-rw-r--r--WebCore/page/inspector/Images/domViewNormalSelected.pngbin0 -> 599 bytes
-rw-r--r--WebCore/page/inspector/Images/domViewSmall.pngbin0 -> 595 bytes
-rw-r--r--WebCore/page/inspector/Images/domViewSmallSelected.pngbin0 -> 599 bytes
-rw-r--r--WebCore/page/inspector/Images/downTriangle.pngbin0 -> 281 bytes
-rw-r--r--WebCore/page/inspector/Images/errorIcon.pngbin0 -> 3811 bytes
-rw-r--r--WebCore/page/inspector/Images/errorMediumIcon.pngbin0 -> 4059 bytes
-rw-r--r--WebCore/page/inspector/Images/folder.pngbin0 -> 4153 bytes
-rw-r--r--WebCore/page/inspector/Images/forwardNormal.pngbin0 -> 759 bytes
-rw-r--r--WebCore/page/inspector/Images/glossyHeader.pngbin0 -> 3563 bytes
-rw-r--r--WebCore/page/inspector/Images/glossyHeaderPressed.pngbin0 -> 189 bytes
-rw-r--r--WebCore/page/inspector/Images/goArrow.pngbin0 -> 3591 bytes
-rw-r--r--WebCore/page/inspector/Images/gradient.pngbin0 -> 3447 bytes
-rw-r--r--WebCore/page/inspector/Images/gradientHighlight.pngbin0 -> 3454 bytes
-rw-r--r--WebCore/page/inspector/Images/gradientHighlightBottom.pngbin0 -> 3641 bytes
-rw-r--r--WebCore/page/inspector/Images/hideStatusWidget.pngbin0 -> 3734 bytes
-rw-r--r--WebCore/page/inspector/Images/hideStatusWidgetPressed.pngbin0 -> 3729 bytes
-rw-r--r--WebCore/page/inspector/Images/network.pngbin0 -> 2740 bytes
-rw-r--r--WebCore/page/inspector/Images/paneBottomGrow.pngbin0 -> 3457 bytes
-rw-r--r--WebCore/page/inspector/Images/paneBottomGrowActive.pngbin0 -> 3457 bytes
-rw-r--r--WebCore/page/inspector/Images/paneGrowHandleLine.pngbin0 -> 3443 bytes
-rw-r--r--WebCore/page/inspector/Images/paneHeader.pngbin0 -> 3476 bytes
-rw-r--r--WebCore/page/inspector/Images/paneHeaderActive.pngbin0 -> 3477 bytes
-rw-r--r--WebCore/page/inspector/Images/plainDocument.pngbin0 -> 460 bytes
-rw-r--r--WebCore/page/inspector/Images/popupArrows.pngbin0 -> 700 bytes
-rw-r--r--WebCore/page/inspector/Images/popupArrowsBlack.pngbin0 -> 117 bytes
-rw-r--r--WebCore/page/inspector/Images/reload.pngbin0 -> 971 bytes
-rw-r--r--WebCore/page/inspector/Images/rightTriangle.pngbin0 -> 308 bytes
-rw-r--r--WebCore/page/inspector/Images/segment.pngbin0 -> 3848 bytes
-rw-r--r--WebCore/page/inspector/Images/segmentEnd.pngbin0 -> 3482 bytes
-rw-r--r--WebCore/page/inspector/Images/segmentHover.pngbin0 -> 3858 bytes
-rw-r--r--WebCore/page/inspector/Images/segmentHoverEnd.pngbin0 -> 3480 bytes
-rw-r--r--WebCore/page/inspector/Images/segmentSelected.pngbin0 -> 3836 bytes
-rw-r--r--WebCore/page/inspector/Images/segmentSelectedEnd.pngbin0 -> 3486 bytes
-rw-r--r--WebCore/page/inspector/Images/showStatusWidget.pngbin0 -> 3733 bytes
-rw-r--r--WebCore/page/inspector/Images/showStatusWidgetPressed.pngbin0 -> 3726 bytes
-rw-r--r--WebCore/page/inspector/Images/sidbarItemBackground.pngbin0 -> 180 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarActionWidget.pngbin0 -> 3169 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarActionWidgetPressed.pngbin0 -> 3924 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarAttachWidget.pngbin0 -> 784 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarAttachWidgetPressed.pngbin0 -> 776 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarDetachWidget.pngbin0 -> 852 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarDetachWidgetPressed.pngbin0 -> 847 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarResizeWidget.pngbin0 -> 3646 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarSelection.pngbin0 -> 3481 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarSelectionBlurred.pngbin0 -> 3475 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarSelectionBlurredTall.pngbin0 -> 3476 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarSelectionGray.pngbin0 -> 3469 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarSelectionGrayTall.pngbin0 -> 3455 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarSelectionTall.pngbin0 -> 3464 bytes
-rw-r--r--WebCore/page/inspector/Images/sidebarStatusAreaBackground.pngbin0 -> 2835 bytes
-rw-r--r--WebCore/page/inspector/Images/sourceViewNormal.pngbin0 -> 566 bytes
-rw-r--r--WebCore/page/inspector/Images/sourceViewNormalSelected.pngbin0 -> 566 bytes
-rw-r--r--WebCore/page/inspector/Images/sourceViewSmall.pngbin0 -> 561 bytes
-rw-r--r--WebCore/page/inspector/Images/sourceViewSmallSelected.pngbin0 -> 560 bytes
-rw-r--r--WebCore/page/inspector/Images/splitviewDimple.pngbin0 -> 216 bytes
-rw-r--r--WebCore/page/inspector/Images/splitviewDividerBackground.pngbin0 -> 149 bytes
-rw-r--r--WebCore/page/inspector/Images/tab.pngbin0 -> 3952 bytes
-rw-r--r--WebCore/page/inspector/Images/tabSelected.pngbin0 -> 4012 bytes
-rw-r--r--WebCore/page/inspector/Images/timelinePillBlue.pngbin0 -> 3346 bytes
-rw-r--r--WebCore/page/inspector/Images/timelinePillGray.pngbin0 -> 3297 bytes
-rw-r--r--WebCore/page/inspector/Images/timelinePillGreen.pngbin0 -> 3350 bytes
-rw-r--r--WebCore/page/inspector/Images/timelinePillOrange.pngbin0 -> 3352 bytes
-rw-r--r--WebCore/page/inspector/Images/timelinePillPurple.pngbin0 -> 3353 bytes
-rw-r--r--WebCore/page/inspector/Images/timelinePillRed.pngbin0 -> 3343 bytes
-rw-r--r--WebCore/page/inspector/Images/timelinePillYellow.pngbin0 -> 3336 bytes
-rw-r--r--WebCore/page/inspector/Images/tipBalloon.pngbin0 -> 3689 bytes
-rw-r--r--WebCore/page/inspector/Images/tipBalloonBottom.pngbin0 -> 3139 bytes
-rw-r--r--WebCore/page/inspector/Images/tipIcon.pngbin0 -> 1212 bytes
-rw-r--r--WebCore/page/inspector/Images/tipIconPressed.pngbin0 -> 1224 bytes
-rw-r--r--WebCore/page/inspector/Images/toggleDown.pngbin0 -> 3768 bytes
-rw-r--r--WebCore/page/inspector/Images/toggleUp.pngbin0 -> 3769 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarBackground.pngbin0 -> 3513 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarBackgroundInactive.pngbin0 -> 3534 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonNormal.pngbin0 -> 956 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonNormalInactive.pngbin0 -> 974 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonNormalPressed.pngbin0 -> 1203 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonNormalSelected.pngbin0 -> 1093 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonNormalSelectedInactive.pngbin0 -> 1120 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonSmall.pngbin0 -> 899 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonSmallInactive.pngbin0 -> 890 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonSmallPressed.pngbin0 -> 1058 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonSmallSelected.pngbin0 -> 1007 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarButtonSmallSelectedInactive.pngbin0 -> 1037 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarPopupButtonNormal.pngbin0 -> 1119 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarPopupButtonNormalInactive.pngbin0 -> 1142 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarPopupButtonNormalPressed.pngbin0 -> 1320 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarPopupButtonSmall.pngbin0 -> 1043 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarPopupButtonSmallInactive.pngbin0 -> 1038 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarPopupButtonSmallPressed.pngbin0 -> 1167 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarSplitButtonDividerNormal.pngbin0 -> 582 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarSplitButtonDividerNormalInactive.pngbin0 -> 584 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarSplitButtonDividerSmall.pngbin0 -> 583 bytes
-rw-r--r--WebCore/page/inspector/Images/toolbarSplitButtonDividerSmallInactive.pngbin0 -> 584 bytes
-rw-r--r--WebCore/page/inspector/Images/treeDownTriangleBlack.pngbin0 -> 3570 bytes
-rw-r--r--WebCore/page/inspector/Images/treeDownTriangleWhite.pngbin0 -> 3531 bytes
-rw-r--r--WebCore/page/inspector/Images/treeLeftTriangleBlack.pngbin0 -> 3551 bytes
-rw-r--r--WebCore/page/inspector/Images/treeRightTriangleBlack.pngbin0 -> 3561 bytes
-rw-r--r--WebCore/page/inspector/Images/treeRightTriangleWhite.pngbin0 -> 3535 bytes
-rw-r--r--WebCore/page/inspector/Images/warningIcon.pngbin0 -> 3726 bytes
-rw-r--r--WebCore/page/inspector/Images/warningMediumIcon.pngbin0 -> 3833 bytes
-rw-r--r--WebCore/page/inspector/Images/warningsErrors.pngbin0 -> 5192 bytes
-rw-r--r--WebCore/page/inspector/MetricsSidebarPane.js140
-rw-r--r--WebCore/page/inspector/NetworkPanel.js1036
-rw-r--r--WebCore/page/inspector/Panel.js180
-rw-r--r--WebCore/page/inspector/PropertiesSection.js139
-rw-r--r--WebCore/page/inspector/PropertiesSidebarPane.js139
-rw-r--r--WebCore/page/inspector/Resource.js689
-rw-r--r--WebCore/page/inspector/ResourceCategory.js105
-rw-r--r--WebCore/page/inspector/ResourcePanel.js50
-rw-r--r--WebCore/page/inspector/SidebarPane.js123
-rw-r--r--WebCore/page/inspector/SourcePanel.js144
-rw-r--r--WebCore/page/inspector/StylesSidebarPane.js682
-rw-r--r--WebCore/page/inspector/WebKit.qrc137
-rw-r--r--WebCore/page/inspector/inspector.css2169
-rw-r--r--WebCore/page/inspector/inspector.html75
-rw-r--r--WebCore/page/inspector/inspector.js1186
-rw-r--r--WebCore/page/inspector/treeoutline.js728
-rw-r--r--WebCore/page/inspector/utilities.js793
-rw-r--r--WebCore/page/mac/AXObjectCacheMac.mm193
-rw-r--r--WebCore/page/mac/ChromeMac.mm55
-rw-r--r--WebCore/page/mac/DragControllerMac.mm69
-rw-r--r--WebCore/page/mac/EventHandlerMac.mm627
-rw-r--r--WebCore/page/mac/FrameMac.mm678
-rw-r--r--WebCore/page/mac/GlobalHistoryMac.mm39
-rw-r--r--WebCore/page/mac/WebCoreAXObject.h65
-rw-r--r--WebCore/page/mac/WebCoreAXObject.mm2781
-rw-r--r--WebCore/page/mac/WebCoreFrameBridge.h254
-rw-r--r--WebCore/page/mac/WebCoreFrameBridge.mm1242
-rw-r--r--WebCore/page/mac/WebCoreFrameView.h54
-rw-r--r--WebCore/page/mac/WebCoreKeyboardUIMode.h40
-rw-r--r--WebCore/page/mac/WebCoreScriptDebugger.h94
-rw-r--r--WebCore/page/mac/WebCoreScriptDebugger.mm368
-rw-r--r--WebCore/page/mac/WebCoreViewFactory.h150
-rw-r--r--WebCore/page/mac/WebCoreViewFactory.m48
-rw-r--r--WebCore/page/mac/WebDashboardRegion.h43
-rw-r--r--WebCore/page/mac/WebDashboardRegion.m75
-rw-r--r--WebCore/page/qt/DragControllerQt.cpp68
-rw-r--r--WebCore/page/qt/EventHandlerQt.cpp138
-rw-r--r--WebCore/page/qt/FrameQt.cpp124
-rw-r--r--WebCore/page/win/DragControllerWin.cpp64
-rw-r--r--WebCore/page/win/EventHandlerWin.cpp113
-rw-r--r--WebCore/page/win/FrameCGWin.cpp87
-rw-r--r--WebCore/page/win/FrameCairoWin.cpp42
-rw-r--r--WebCore/page/win/FrameWin.cpp147
-rw-r--r--WebCore/page/win/FrameWin.h41
-rw-r--r--WebCore/page/win/GlobalHistoryWin.cpp39
-rw-r--r--WebCore/page/win/PageWin.cpp38
-rw-r--r--WebCore/page/wx/DragControllerWx.cpp68
-rw-r--r--WebCore/page/wx/EventHandlerWx.cpp94
239 files changed, 34579 insertions, 0 deletions
diff --git a/WebCore/page/AXObjectCache.h b/WebCore/page/AXObjectCache.h
new file mode 100644
index 0000000..d73a5a3
--- /dev/null
+++ b/WebCore/page/AXObjectCache.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2003, 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 AXObjectCache_h
+#define AXObjectCache_h
+
+#include <limits.h>
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+#ifdef __OBJC__
+@class WebCoreAXObject;
+@class WebCoreTextMarker;
+#else
+class WebCoreAXObject;
+class WebCoreTextMarker;
+#endif
+
+namespace WebCore {
+
+ class RenderObject;
+ class String;
+ class VisiblePosition;
+
+ typedef unsigned AXID;
+
+ struct AXIDHashTraits : WTF::GenericHashTraits<unsigned> {
+ static TraitType deletedValue() { return UINT_MAX; }
+ };
+
+ class AXObjectCache {
+ public:
+ ~AXObjectCache();
+
+ WebCoreAXObject* get(RenderObject*);
+ void remove(RenderObject*);
+
+ void removeAXID(WebCoreAXObject*);
+
+ WebCoreTextMarker* textMarkerForVisiblePosition(const VisiblePosition&);
+ VisiblePosition visiblePositionForTextMarker(WebCoreTextMarker*);
+
+ void childrenChanged(RenderObject*);
+ void postNotification(RenderObject*, const String& message);
+ void postNotificationToElement(RenderObject*, const String& message);
+ void handleFocusedUIElementChanged();
+
+#if PLATFORM(MAC)
+ static void enableAccessibility() { gAccessibilityEnabled = true; }
+ static bool accessibilityEnabled() { return gAccessibilityEnabled; }
+#else
+ static bool accessibilityEnabled() { return false; }
+#endif
+
+ private:
+#if PLATFORM(MAC)
+ static bool gAccessibilityEnabled;
+#endif
+
+ AXID getAXID(WebCoreAXObject*);
+
+ HashMap<RenderObject*, WebCoreAXObject*> m_objects;
+ HashSet<AXID, IntHash<AXID>, AXIDHashTraits> m_idsInUse;
+ };
+
+#if !PLATFORM(MAC)
+ inline AXObjectCache::~AXObjectCache() { }
+ inline WebCoreAXObject* AXObjectCache::get(RenderObject*) { return 0; }
+ inline void AXObjectCache::remove(RenderObject*) { }
+ inline void AXObjectCache::removeAXID(WebCoreAXObject*) { }
+ inline void AXObjectCache::childrenChanged(RenderObject*) { }
+ inline void AXObjectCache::postNotification(RenderObject*, const String&) { }
+ inline void AXObjectCache::postNotificationToElement(RenderObject*, const String&) { }
+ inline void AXObjectCache::handleFocusedUIElementChanged() { }
+#endif
+
+}
+
+#endif
diff --git a/WebCore/page/AbstractView.idl b/WebCore/page/AbstractView.idl
new file mode 100644
index 0000000..ca02c82
--- /dev/null
+++ b/WebCore/page/AbstractView.idl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Samuel Weinig <sam.weinig@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.
+ */
+
+module views {
+
+ // Introduced in DOM Level 2:
+ interface AbstractView {
+ readonly attribute Document document;
+ };
+
+}
diff --git a/WebCore/page/AnimationController.cpp b/WebCore/page/AnimationController.cpp
new file mode 100644
index 0000000..9d7a1f7
--- /dev/null
+++ b/WebCore/page/AnimationController.cpp
@@ -0,0 +1,617 @@
+/*
+ * 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)
+{
+ const Transition* currentTransitions = currentStyle->transitions();
+ const Transition* targetTransitions = targetStyle->transitions();
+ if (currentTransitions != targetTransitions && !(currentTransitions && targetTransitions && *currentTransitions == *targetTransitions)) {
+ reset(renderer);
+ deleteAllValues(m_animations);
+ m_animations.clear();
+ }
+
+ // 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 = currentTransitions; 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 && renderer->style()->transitions()) {
+ 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);
+ if (!animation)
+ return newStyle;
+
+ 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/AnimationController.h b/WebCore/page/AnimationController.h
new file mode 100644
index 0000000..fda1143
--- /dev/null
+++ b/WebCore/page/AnimationController.h
@@ -0,0 +1,57 @@
+/*
+ * 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
+
+namespace WebCore {
+
+class AnimationControllerPrivate;
+class Frame;
+class RenderObject;
+class RenderStyle;
+
+class AnimationController
+{
+public:
+ AnimationController(Frame*);
+ ~AnimationController();
+
+ void cancelImplicitAnimations(RenderObject*);
+ RenderStyle* updateImplicitAnimations(RenderObject*, RenderStyle* newStyle);
+
+ void suspendAnimations();
+ void resumeAnimations();
+
+private:
+ AnimationControllerPrivate* m_data;
+};
+
+}
+
+#endif
diff --git a/WebCore/page/BarInfo.cpp b/WebCore/page/BarInfo.cpp
new file mode 100644
index 0000000..153aee0
--- /dev/null
+++ b/WebCore/page/BarInfo.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "BarInfo.h"
+
+#include "Chrome.h"
+#include "Frame.h"
+#include "Page.h"
+
+namespace WebCore {
+
+BarInfo::BarInfo(Frame* frame, Type type)
+ : m_frame(frame)
+ , m_type(type)
+{
+}
+
+void BarInfo::disconnectFrame()
+{
+ m_frame = 0;
+}
+
+bool BarInfo::visible() const
+{
+ if (!m_frame)
+ return false;
+
+ switch (m_type) {
+ case Locationbar:
+ return m_frame->page()->chrome()->toolbarsVisible();
+ case Toolbar:
+ return m_frame->page()->chrome()->toolbarsVisible();
+ case Personalbar:
+ return m_frame->page()->chrome()->toolbarsVisible();
+ case Menubar:
+ return m_frame->page()->chrome()->menubarVisible();
+ case Scrollbars:
+ return m_frame->page()->chrome()->scrollbarsVisible();
+ case Statusbar:
+ return m_frame->page()->chrome()->statusbarVisible();
+ default:
+ return false;
+ }
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/BarInfo.h b/WebCore/page/BarInfo.h
new file mode 100644
index 0000000..4cbbcfc
--- /dev/null
+++ b/WebCore/page/BarInfo.h
@@ -0,0 +1,57 @@
+/*
+ * 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 BarInfo_h
+#define BarInfo_h
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class Frame;
+
+ class BarInfo : public RefCounted<BarInfo> {
+ public:
+ enum Type { Locationbar, Menubar, Personalbar, Scrollbars, Statusbar, Toolbar };
+
+ 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;
+ };
+
+} // namespace WebCore
+
+#endif // BarInfo_h
diff --git a/WebCore/page/BarInfo.idl b/WebCore/page/BarInfo.idl
new file mode 100644
index 0000000..42041c51
--- /dev/null
+++ b/WebCore/page/BarInfo.idl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+module window {
+
+ interface BarInfo {
+ readonly attribute boolean visible;
+ };
+
+}
diff --git a/WebCore/page/Chrome.cpp b/WebCore/page/Chrome.cpp
new file mode 100644
index 0000000..fecbe79
--- /dev/null
+++ b/WebCore/page/Chrome.cpp
@@ -0,0 +1,398 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Trolltech ASA
+ *
+ * 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 "Chrome.h"
+
+#include "ChromeClient.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "HTMLFormElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HitTestResult.h"
+#include "InspectorController.h"
+#include "Page.h"
+#include "ResourceHandle.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>
+
+namespace WebCore {
+
+using namespace HTMLNames;
+using namespace std;
+
+class PageGroupLoadDeferrer : Noncopyable {
+public:
+ PageGroupLoadDeferrer(Page*, bool deferSelf);
+ ~PageGroupLoadDeferrer();
+private:
+ Vector<RefPtr<Frame>, 16> m_deferredFrames;
+#if !PLATFORM(MAC)
+ Vector<pair<RefPtr<Frame>, PausedTimeouts*>, 16> m_pausedTimeouts;
+#endif
+};
+
+Chrome::Chrome(Page* page, ChromeClient* client)
+ : m_page(page)
+ , m_client(client)
+{
+ ASSERT(m_client);
+}
+
+Chrome::~Chrome()
+{
+ m_client->chromeDestroyed();
+}
+
+void Chrome::setWindowRect(const FloatRect& rect) const
+{
+ m_client->setWindowRect(rect);
+}
+
+FloatRect Chrome::windowRect() const
+{
+ return m_client->windowRect();
+}
+
+FloatRect Chrome::pageRect() const
+{
+ return m_client->pageRect();
+}
+
+float Chrome::scaleFactor()
+{
+ return m_client->scaleFactor();
+}
+
+void Chrome::focus() const
+{
+ m_client->focus();
+}
+
+void Chrome::unfocus() const
+{
+ m_client->unfocus();
+}
+
+bool Chrome::canTakeFocus(FocusDirection direction) const
+{
+ return m_client->canTakeFocus(direction);
+}
+
+void Chrome::takeFocus(FocusDirection direction) const
+{
+ m_client->takeFocus(direction);
+}
+
+Page* Chrome::createWindow(Frame* frame, const FrameLoadRequest& request, const WindowFeatures& features) const
+{
+ return m_client->createWindow(frame, request, features);
+}
+
+void Chrome::show() const
+{
+ m_client->show();
+}
+
+bool Chrome::canRunModal() const
+{
+ return m_client->canRunModal();
+}
+
+bool Chrome::canRunModalNow() const
+{
+ // If loads are blocked, we can't run modal because the contents
+ // of the modal dialog will never show up!
+ return canRunModal() && !ResourceHandle::loadsBlocked();
+}
+
+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);
+
+ TimerBase::fireTimersInNestedEventLoop();
+ m_client->runModal();
+}
+
+void Chrome::setToolbarsVisible(bool b) const
+{
+ m_client->setToolbarsVisible(b);
+}
+
+bool Chrome::toolbarsVisible() const
+{
+ return m_client->toolbarsVisible();
+}
+
+void Chrome::setStatusbarVisible(bool b) const
+{
+ m_client->setStatusbarVisible(b);
+}
+
+bool Chrome::statusbarVisible() const
+{
+ return m_client->statusbarVisible();
+}
+
+void Chrome::setScrollbarsVisible(bool b) const
+{
+ m_client->setScrollbarsVisible(b);
+}
+
+bool Chrome::scrollbarsVisible() const
+{
+ return m_client->scrollbarsVisible();
+}
+
+void Chrome::setMenubarVisible(bool b) const
+{
+ m_client->setMenubarVisible(b);
+}
+
+bool Chrome::menubarVisible() const
+{
+ return m_client->menubarVisible();
+}
+
+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();
+}
+
+bool Chrome::runBeforeUnloadConfirmPanel(const String& message, Frame* frame)
+{
+ // Defer loads in case the client method runs a new event loop that would
+ // otherwise cause the load to continue while we're in the middle of executing JavaScript.
+ PageGroupLoadDeferrer deferrer(m_page, true);
+
+ return m_client->runBeforeUnloadConfirmPanel(message, frame);
+}
+
+void Chrome::closeWindowSoon()
+{
+ m_client->closeWindowSoon();
+}
+
+void Chrome::runJavaScriptAlert(Frame* frame, const String& message)
+{
+ // Defer loads in case the client method runs a new event loop that would
+ // otherwise cause the load to continue while we're in the middle of executing JavaScript.
+ PageGroupLoadDeferrer deferrer(m_page, true);
+
+ ASSERT(frame);
+ String text = message;
+ text.replace('\\', frame->backslashAsCurrencySymbol());
+
+ m_client->runJavaScriptAlert(frame, text);
+}
+
+bool Chrome::runJavaScriptConfirm(Frame* frame, const String& message)
+{
+ // Defer loads in case the client method runs a new event loop that would
+ // otherwise cause the load to continue while we're in the middle of executing JavaScript.
+ PageGroupLoadDeferrer deferrer(m_page, true);
+
+ ASSERT(frame);
+ String text = message;
+ text.replace('\\', frame->backslashAsCurrencySymbol());
+
+ return m_client->runJavaScriptConfirm(frame, text);
+}
+
+bool Chrome::runJavaScriptPrompt(Frame* frame, const String& prompt, const String& defaultValue, String& result)
+{
+ // Defer loads in case the client method runs a new event loop that would
+ // otherwise cause the load to continue while we're in the middle of executing JavaScript.
+ PageGroupLoadDeferrer deferrer(m_page, true);
+
+ ASSERT(frame);
+ String promptText = prompt;
+ promptText.replace('\\', frame->backslashAsCurrencySymbol());
+ String defaultValueText = defaultValue;
+ defaultValueText.replace('\\', frame->backslashAsCurrencySymbol());
+
+ bool ok = m_client->runJavaScriptPrompt(frame, promptText, defaultValueText, result);
+
+ if (ok)
+ result.replace(frame->backslashAsCurrencySymbol(), '\\');
+
+ return ok;
+}
+
+void Chrome::setStatusbarText(Frame* frame, const String& status)
+{
+ ASSERT(frame);
+ String text = status;
+ text.replace('\\', frame->backslashAsCurrencySymbol());
+
+ m_client->setStatusbarText(text);
+}
+
+bool Chrome::shouldInterruptJavaScript()
+{
+ // Defer loads in case the client method runs a new event loop that would
+ // otherwise cause the load to continue while we're in the middle of executing JavaScript.
+ PageGroupLoadDeferrer deferrer(m_page, true);
+
+ return m_client->shouldInterruptJavaScript();
+}
+
+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)
+{
+ m_client->mouseDidMoveOverElement(result, modifierFlags);
+}
+
+void Chrome::setToolTip(const HitTestResult& result)
+{
+ // First priority is a potential toolTip representing a spelling or grammar error
+ String toolTip = result.spellingToolTip();
+
+ // Next priority is a toolTip from a URL beneath the mouse (if preference is set to show those).
+ if (toolTip.isEmpty() && m_page->settings()->showsURLsInToolTips()) {
+ if (Node* node = result.innerNonSharedNode()) {
+ // Get tooltip representing form action, if relevant
+ if (node->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+ if (input->inputType() == HTMLInputElement::SUBMIT)
+ if (HTMLFormElement* form = input->form())
+ toolTip = form->action();
+ }
+ }
+
+ // Get tooltip representing link's URL
+ if (toolTip.isEmpty())
+ // FIXME: Need to pass this URL through userVisibleString once that's in WebCore
+ toolTip = result.absoluteLinkURL().string();
+ }
+
+ // Lastly we'll consider a tooltip for element with "title" attribute
+ if (toolTip.isEmpty())
+ toolTip = result.title();
+
+ m_client->setToolTip(toolTip);
+}
+
+void Chrome::print(Frame* frame)
+{
+ m_client->print(frame);
+}
+
+PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page* page, bool deferSelf)
+{
+ const HashSet<Page*>* group = page->frameNamespace();
+
+ if (!group)
+ return;
+
+ HashSet<Page*>::const_iterator end = group->end();
+ for (HashSet<Page*>::const_iterator it = group->begin(); it != end; ++it) {
+ Page* otherPage = *it;
+ if ((deferSelf || otherPage != page)) {
+ if (!otherPage->defersLoading())
+ m_deferredFrames.append(otherPage->mainFrame());
+
+#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));
+ }
+ }
+#endif
+ }
+ }
+
+ size_t count = m_deferredFrames.size();
+ for (size_t i = 0; i < count; ++i)
+ if (Page* page = m_deferredFrames[i]->page())
+ page->setDefersLoading(true);
+}
+
+PageGroupLoadDeferrer::~PageGroupLoadDeferrer()
+{
+ size_t count = m_deferredFrames.size();
+ for (size_t i = 0; i < count; ++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;
+ }
+#endif
+}
+
+
+} // namespace WebCore
diff --git a/WebCore/page/Chrome.h b/WebCore/page/Chrome.h
new file mode 100644
index 0000000..9bc42e0
--- /dev/null
+++ b/WebCore/page/Chrome.h
@@ -0,0 +1,139 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * 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
+ * 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 Chrome_h
+#define Chrome_h
+
+#include "FocusDirection.h"
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RefPtr.h>
+
+#if PLATFORM(MAC)
+#ifndef __OBJC__
+class NSView;
+#endif
+#endif
+
+namespace WebCore {
+
+ class ChromeClient;
+ class ContextMenu;
+ class FloatRect;
+ class Frame;
+ class HitTestResult;
+ 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 {
+ public:
+ Chrome(Page*, ChromeClient*);
+ ~Chrome();
+
+ ChromeClient* client() { return m_client; }
+
+ void setWindowRect(const FloatRect&) const;
+ FloatRect windowRect() const;
+
+ FloatRect pageRect() const;
+
+ float scaleFactor();
+
+ void focus() const;
+ void unfocus() const;
+
+ bool canTakeFocus(FocusDirection) const;
+ void takeFocus(FocusDirection) const;
+
+ Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&) const;
+ void show() const;
+
+ bool canRunModal() const;
+ bool canRunModalNow() const;
+ void runModal() const;
+
+ void setToolbarsVisible(bool) const;
+ bool toolbarsVisible() const;
+
+ void setStatusbarVisible(bool) const;
+ bool statusbarVisible() const;
+
+ void setScrollbarsVisible(bool) const;
+ bool scrollbarsVisible() const;
+
+ void setMenubarVisible(bool) const;
+ bool menubarVisible() const;
+
+ 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);
+
+ void closeWindowSoon();
+
+ void runJavaScriptAlert(Frame*, const String&);
+ bool runJavaScriptConfirm(Frame*, const String&);
+ 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*);
+
+#if PLATFORM(MAC)
+ void focusNSView(NSView*);
+#endif
+
+ private:
+ Page* m_page;
+ ChromeClient* m_client;
+ };
+}
+
+#endif // Chrome_h
diff --git a/WebCore/page/ChromeClient.h b/WebCore/page/ChromeClient.h
new file mode 100644
index 0000000..5bce1af
--- /dev/null
+++ b/WebCore/page/ChromeClient.h
@@ -0,0 +1,113 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * 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
+ * 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 ChromeClient_h
+#define ChromeClient_h
+
+#include "FocusDirection.h"
+
+namespace WebCore {
+
+ class FloatRect;
+ class Frame;
+ class HitTestResult;
+ class IntRect;
+ class Page;
+ class String;
+
+ struct FrameLoadRequest;
+ struct WindowFeatures;
+
+ class ChromeClient {
+ public:
+ virtual void chromeDestroyed() = 0;
+
+ virtual void setWindowRect(const FloatRect&) = 0;
+ virtual FloatRect windowRect() = 0;
+
+ virtual FloatRect pageRect() = 0;
+
+ virtual float scaleFactor() = 0;
+
+ virtual void focus() = 0;
+ virtual void unfocus() = 0;
+
+ virtual bool canTakeFocus(FocusDirection) = 0;
+ virtual void takeFocus(FocusDirection) = 0;
+
+ // The Frame pointer provides the ChromeClient with context about which
+ // Frame wants to create the new Page. Also, the newly created window
+ // should not be shown to the user until the ChromeClient of the newly
+ // created Page has its show method called.
+ virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&) = 0;
+ virtual void show() = 0;
+
+ virtual bool canRunModal() = 0;
+ virtual void runModal() = 0;
+
+ virtual void setToolbarsVisible(bool) = 0;
+ virtual bool toolbarsVisible() = 0;
+
+ virtual void setStatusbarVisible(bool) = 0;
+ virtual bool statusbarVisible() = 0;
+
+ virtual void setScrollbarsVisible(bool) = 0;
+ virtual bool scrollbarsVisible() = 0;
+
+ virtual void setMenubarVisible(bool) = 0;
+ virtual bool menubarVisible() = 0;
+
+ virtual void setResizable(bool) = 0;
+
+ virtual void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID) = 0;
+
+ virtual bool canRunBeforeUnloadConfirmPanel() = 0;
+ virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame) = 0;
+
+ virtual void closeWindowSoon() = 0;
+
+ 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;
+
+ virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags) = 0;
+
+ virtual void setToolTip(const String&) = 0;
+
+ virtual void print(Frame*) = 0;
+
+ virtual void exceededDatabaseQuota(Frame*, const String& databaseName) = 0;
+
+ protected:
+ virtual ~ChromeClient() { }
+ };
+
+}
+
+#endif // ChromeClient_h
diff --git a/WebCore/page/Console.cpp b/WebCore/page/Console.cpp
new file mode 100644
index 0000000..d45af62
--- /dev/null
+++ b/WebCore/page/Console.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 "Console.h"
+
+#include "Chrome.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "Page.h"
+#include "PlatformString.h"
+
+namespace WebCore {
+
+Console::Console(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+void Console::disconnectFrame()
+{
+ m_frame = 0;
+}
+
+void Console::error(const String& message)
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 0, m_frame->loader()->url().prettyURL());
+}
+
+void Console::info(const String& message)
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ page->chrome()->addMessageToConsole(JSMessageSource, LogMessageLevel, message, 0, m_frame->loader()->url().prettyURL());
+}
+
+void Console::log(const String& message)
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ page->chrome()->addMessageToConsole(JSMessageSource, LogMessageLevel, message, 0, m_frame->loader()->url().prettyURL());
+}
+
+void Console::warn(const String& message)
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ page->chrome()->addMessageToConsole(JSMessageSource, WarningMessageLevel, message, 0, m_frame->loader()->url().prettyURL());
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/Console.h b/WebCore/page/Console.h
new file mode 100644
index 0000000..062cfdd
--- /dev/null
+++ b/WebCore/page/Console.h
@@ -0,0 +1,58 @@
+/*
+ * 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 Console_h
+#define Console_h
+
+#include <wtf/RefCounted.h>
+#include "PlatformString.h"
+
+namespace WebCore {
+
+ class Frame;
+
+ class Console : public RefCounted<Console> {
+ public:
+ 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);
+
+ private:
+ Console(Frame*);
+
+ Frame* m_frame;
+ };
+
+} // namespace WebCore
+
+#endif // Console_h
diff --git a/WebCore/page/Console.idl b/WebCore/page/Console.idl
new file mode 100644
index 0000000..3356c0e
--- /dev/null
+++ b/WebCore/page/Console.idl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+module window {
+
+ interface Console {
+ void error(in DOMString message);
+ void info(in DOMString message);
+ void log(in DOMString message);
+ void warn(in DOMString message);
+ };
+
+}
diff --git a/WebCore/page/ContextMenuClient.h b/WebCore/page/ContextMenuClient.h
new file mode 100644
index 0000000..775adc5
--- /dev/null
+++ b/WebCore/page/ContextMenuClient.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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 ContextMenuClient_h
+#define ContextMenuClient_h
+
+#include "PlatformMenuDescription.h"
+
+namespace WebCore {
+ class ContextMenu;
+ class ContextMenuItem;
+ class Frame;
+ class HitTestResult;
+ class KURL;
+ class String;
+
+ class ContextMenuClient {
+ public:
+ virtual ~ContextMenuClient() { }
+ virtual void contextMenuDestroyed() = 0;
+
+ virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*) = 0;
+ virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) = 0;
+
+ virtual void downloadURL(const KURL& url) = 0;
+ virtual void searchWithGoogle(const Frame*) = 0;
+ virtual void lookUpInDictionary(Frame*) = 0;
+ virtual void speak(const String&) = 0;
+ virtual void stopSpeaking() = 0;
+
+#if PLATFORM(MAC)
+ virtual void searchWithSpotlight() = 0;
+#endif
+ };
+}
+
+#endif
diff --git a/WebCore/page/ContextMenuController.cpp b/WebCore/page/ContextMenuController.cpp
new file mode 100644
index 0000000..d331b19
--- /dev/null
+++ b/WebCore/page/ContextMenuController.cpp
@@ -0,0 +1,298 @@
+/*
+ * 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 "ContextMenuController.h"
+
+#include "Chrome.h"
+#include "ContextMenu.h"
+#include "ContextMenuClient.h"
+#include "Document.h"
+#include "DocumentFragment.h"
+#include "DocumentLoader.h"
+#include "Editor.h"
+#include "EditorClient.h"
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoadRequest.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "InspectorController.h"
+#include "MouseEvent.h"
+#include "Node.h"
+#include "Page.h"
+#include "RenderLayer.h"
+#include "RenderObject.h"
+#include "ReplaceSelectionCommand.h"
+#include "ResourceRequest.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "TextIterator.h"
+#include "WindowFeatures.h"
+#include "markup.h"
+
+namespace WebCore {
+
+using namespace EventNames;
+
+ContextMenuController::ContextMenuController(Page* page, ContextMenuClient* client)
+ : m_page(page)
+ , m_client(client)
+ , m_contextMenu(0)
+{
+ ASSERT_ARG(page, page);
+ ASSERT_ARG(client, client);
+}
+
+ContextMenuController::~ContextMenuController()
+{
+ m_client->contextMenuDestroyed();
+}
+
+void ContextMenuController::clearContextMenu()
+{
+ m_contextMenu.set(0);
+}
+
+void ContextMenuController::handleContextMenuEvent(Event* event)
+{
+ ASSERT(event->type() == contextmenuEvent);
+ if (!event->isMouseEvent())
+ return;
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ IntPoint point = IntPoint(mouseEvent->pageX(), mouseEvent->pageY());
+ HitTestResult result(point);
+
+ if (Frame* frame = event->target()->toNode()->document()->frame())
+ result = frame->eventHandler()->hitTestResultAtPoint(point, false);
+
+ if (!result.innerNonSharedNode())
+ return;
+
+ m_contextMenu.set(new ContextMenu(result));
+ m_contextMenu->populate();
+ if (m_page->inspectorController()->enabled())
+ m_contextMenu->addInspectElementItem();
+
+ PlatformMenuDescription customMenu = m_client->getCustomMenuFromDefaultItems(m_contextMenu.get());
+ m_contextMenu->setPlatformDescription(customMenu);
+
+ event->setDefaultHandled();
+}
+
+static void openNewWindow(const KURL& urlToLoad, Frame* frame)
+{
+ if (Page* oldPage = frame->page()) {
+ WindowFeatures features;
+ if (Page* newPage = oldPage->chrome()->createWindow(frame,
+ FrameLoadRequest(ResourceRequest(urlToLoad, frame->loader()->outgoingReferrer())), features))
+ newPage->chrome()->show();
+ }
+}
+
+void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item)
+{
+ ASSERT(item->type() == ActionType || item->type() == CheckableActionType);
+
+ if (item->action() >= ContextMenuItemBaseApplicationTag) {
+ m_client->contextMenuItemSelected(item, m_contextMenu.get());
+ return;
+ }
+
+ HitTestResult result = m_contextMenu->hitTestResult();
+ Frame* frame = result.innerNonSharedNode()->document()->frame();
+ if (!frame)
+ return;
+
+ switch (item->action()) {
+ case ContextMenuItemTagOpenLinkInNewWindow:
+ openNewWindow(result.absoluteLinkURL(), frame);
+ break;
+ case ContextMenuItemTagDownloadLinkToDisk:
+ // FIXME: Some day we should be able to do this from within WebCore.
+ m_client->downloadURL(result.absoluteLinkURL());
+ break;
+ case ContextMenuItemTagCopyLinkToClipboard:
+ frame->editor()->copyURL(result.absoluteLinkURL(), result.textContent());
+ break;
+ case ContextMenuItemTagOpenImageInNewWindow:
+ openNewWindow(result.absoluteImageURL(), frame);
+ break;
+ case ContextMenuItemTagDownloadImageToDisk:
+ // FIXME: Some day we should be able to do this from within WebCore.
+ m_client->downloadURL(result.absoluteImageURL());
+ break;
+ case ContextMenuItemTagCopyImageToClipboard:
+ // FIXME: The Pasteboard class is not written yet
+ // For now, call into the client. This is temporary!
+ 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);
+ break;
+ }
+ case ContextMenuItemTagCopy:
+ frame->editor()->copy();
+ break;
+ case ContextMenuItemTagGoBack:
+ frame->loader()->goBackOrForward(-1);
+ break;
+ case ContextMenuItemTagGoForward:
+ frame->loader()->goBackOrForward(1);
+ break;
+ case ContextMenuItemTagStop:
+ frame->loader()->stop();
+ break;
+ case ContextMenuItemTagReload:
+ frame->loader()->reload();
+ break;
+ case ContextMenuItemTagCut:
+ frame->editor()->cut();
+ break;
+ case ContextMenuItemTagPaste:
+ frame->editor()->paste();
+ break;
+#if PLATFORM(GTK)
+ case ContextMenuItemTagDelete:
+ frame->editor()->performDelete();
+ break;
+ case ContextMenuItemTagSelectAll:
+ frame->editor()->command("SelectAll").execute();
+ break;
+#endif
+ case ContextMenuItemTagSpellingGuess:
+ ASSERT(frame->selectedText().length());
+ if (frame->editor()->shouldInsertText(item->title(), frame->selectionController()->toRange().get(),
+ EditorInsertActionPasted)) {
+ Document* document = frame->document();
+ RefPtr<ReplaceSelectionCommand> command =
+ new ReplaceSelectionCommand(document, createFragmentFromMarkup(document, item->title(), ""),
+ true, false, true);
+ applyCommand(command);
+ frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
+ }
+ break;
+ case ContextMenuItemTagIgnoreSpelling:
+ frame->editor()->ignoreSpelling();
+ break;
+ case ContextMenuItemTagLearnSpelling:
+ frame->editor()->learnSpelling();
+ break;
+ case ContextMenuItemTagSearchWeb:
+ m_client->searchWithGoogle(frame);
+ break;
+ case ContextMenuItemTagLookUpInDictionary:
+ // FIXME: Some day we may be able to do this from within WebCore.
+ m_client->lookUpInDictionary(frame);
+ 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>());
+ else
+ openNewWindow(result.absoluteLinkURL(), frame);
+ break;
+ case ContextMenuItemTagBold:
+ frame->editor()->command("ToggleBold").execute();
+ break;
+ case ContextMenuItemTagItalic:
+ frame->editor()->command("ToggleItalic").execute();
+ break;
+ case ContextMenuItemTagUnderline:
+ frame->editor()->toggleUnderline();
+ break;
+ case ContextMenuItemTagOutline:
+ // We actually never enable this because CSS does not have a way to specify an outline font,
+ // which may make this difficult to implement. Maybe a special case of text-shadow?
+ break;
+ case ContextMenuItemTagStartSpeaking: {
+ ExceptionCode ec;
+ RefPtr<Range> selectedRange = frame->selectionController()->toRange();
+ if (!selectedRange || selectedRange->collapsed(ec)) {
+ Document* document = result.innerNonSharedNode()->document();
+ selectedRange = document->createRange();
+ selectedRange->selectNode(document->documentElement(), ec);
+ }
+ m_client->speak(plainText(selectedRange.get()));
+ break;
+ }
+ case ContextMenuItemTagStopSpeaking:
+ m_client->stopSpeaking();
+ break;
+ case ContextMenuItemTagDefaultDirection:
+ frame->editor()->setBaseWritingDirection("inherit");
+ break;
+ case ContextMenuItemTagLeftToRight:
+ frame->editor()->setBaseWritingDirection("ltr");
+ break;
+ case ContextMenuItemTagRightToLeft:
+ frame->editor()->setBaseWritingDirection("rtl");
+ break;
+#if PLATFORM(MAC)
+ case ContextMenuItemTagSearchInSpotlight:
+ m_client->searchWithSpotlight();
+ break;
+#endif
+ case ContextMenuItemTagShowSpellingPanel:
+ frame->editor()->showSpellingGuessPanel();
+ break;
+ case ContextMenuItemTagCheckSpelling:
+ frame->editor()->advanceToNextMisspelling();
+ break;
+ case ContextMenuItemTagCheckSpellingWhileTyping:
+ frame->editor()->toggleContinuousSpellChecking();
+ break;
+#ifndef BUILDING_ON_TIGER
+ case ContextMenuItemTagCheckGrammarWithSpelling:
+ frame->editor()->toggleGrammarChecking();
+ break;
+#endif
+#if PLATFORM(MAC)
+ case ContextMenuItemTagShowFonts:
+ frame->editor()->showFontPanel();
+ break;
+ case ContextMenuItemTagStyles:
+ frame->editor()->showStylesPanel();
+ break;
+ case ContextMenuItemTagShowColors:
+ frame->editor()->showColorPanel();
+ break;
+#endif
+ case ContextMenuItemTagInspectElement:
+ if (Page* page = frame->page())
+ page->inspectorController()->inspect(result.innerNonSharedNode());
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/ContextMenuController.h b/WebCore/page/ContextMenuController.h
new file mode 100644
index 0000000..cb7e6ee
--- /dev/null
+++ b/WebCore/page/ContextMenuController.h
@@ -0,0 +1,61 @@
+/*
+ * 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 ContextMenuController_h
+#define ContextMenuController_h
+
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+ class ContextMenu;
+ class ContextMenuClient;
+ class ContextMenuItem;
+ class Event;
+ class Page;
+
+ class ContextMenuController : Noncopyable {
+ public:
+ ContextMenuController(Page*, ContextMenuClient*);
+ ~ContextMenuController();
+
+ ContextMenuClient* client() { return m_client; }
+
+ ContextMenu* contextMenu() const { return m_contextMenu.get(); }
+ void clearContextMenu();
+
+ void handleContextMenuEvent(Event*);
+ void contextMenuItemSelected(ContextMenuItem*);
+
+ private:
+ Page* m_page;
+ ContextMenuClient* m_client;
+ OwnPtr<ContextMenu> m_contextMenu;
+ };
+
+}
+
+#endif
diff --git a/WebCore/page/DOMSelection.cpp b/WebCore/page/DOMSelection.cpp
new file mode 100644
index 0000000..66b567a
--- /dev/null
+++ b/WebCore/page/DOMSelection.cpp
@@ -0,0 +1,427 @@
+/*
+ * 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 "DOMSelection.h"
+
+#include "ExceptionCode.h"
+#include "Frame.h"
+#include "htmlediting.h"
+#include "Node.h"
+#include "PlatformString.h"
+#include "Range.h"
+#include "SelectionController.h"
+#include "TextIterator.h"
+
+namespace WebCore {
+
+DOMSelection::DOMSelection(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+Frame* DOMSelection::frame() const
+{
+ return m_frame;
+}
+
+void DOMSelection::disconnectFrame()
+{
+ m_frame = 0;
+}
+
+Node* DOMSelection::anchorNode() const
+{
+ if (!m_frame)
+ return 0;
+
+ const Selection& selection = m_frame->selectionController()->selection();
+ Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
+ anchor = rangeCompliantEquivalent(anchor);
+ return anchor.node();
+}
+
+Node* DOMSelection::baseNode() const
+{
+ if (!m_frame)
+ return 0;
+ return rangeCompliantEquivalent(m_frame->selectionController()->selection().base()).node();
+}
+
+int DOMSelection::anchorOffset() const
+{
+ if (!m_frame)
+ return 0;
+
+ const Selection& selection = m_frame->selectionController()->selection();
+ Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
+ anchor = rangeCompliantEquivalent(anchor);
+ return anchor.offset();
+}
+
+int DOMSelection::baseOffset() const
+{
+ if (!m_frame)
+ return 0;
+ return rangeCompliantEquivalent(m_frame->selectionController()->selection().base()).offset();
+}
+
+Node* DOMSelection::focusNode() const
+{
+ if (!m_frame)
+ return 0;
+
+ const Selection& selection = m_frame->selectionController()->selection();
+ Position focus = selection.isBaseFirst() ? selection.end() : selection.start();
+ focus = rangeCompliantEquivalent(focus);
+ return focus.node();
+}
+
+Node* DOMSelection::extentNode() const
+{
+ if (!m_frame)
+ return 0;
+ return rangeCompliantEquivalent(m_frame->selectionController()->selection().extent()).node();
+}
+
+int DOMSelection::focusOffset() const
+{
+ if (!m_frame)
+ return 0;
+
+ const Selection& selection = m_frame->selectionController()->selection();
+ Position focus = selection.isBaseFirst() ? selection.end() : selection.start();
+ focus = rangeCompliantEquivalent(focus);
+ return focus.offset();
+}
+
+int DOMSelection::extentOffset() const
+{
+ if (!m_frame)
+ return 0;
+ return rangeCompliantEquivalent(m_frame->selectionController()->selection().extent()).offset();
+}
+
+bool DOMSelection::isCollapsed() const
+{
+ if (!m_frame)
+ return false;
+ return !m_frame->selectionController()->isRange();
+}
+
+String DOMSelection::type() const
+{
+ if (!m_frame)
+ return String();
+
+ SelectionController* selectionController = m_frame->selectionController();
+
+ if (selectionController->isNone())
+ return "None";
+ if (selectionController->isCaret())
+ return "Caret";
+ return "Range";
+}
+
+int DOMSelection::rangeCount() const
+{
+ if (!m_frame)
+ return 0;
+ return m_frame->selectionController()->isNone() ? 0 : 1;
+}
+
+void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec)
+{
+ if (!m_frame)
+ return;
+
+ if (offset < 0) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+ m_frame->selectionController()->moveTo(VisiblePosition(node, offset, DOWNSTREAM));
+}
+
+void DOMSelection::collapseToEnd()
+{
+ if (!m_frame)
+ return;
+
+ const Selection& selection = m_frame->selectionController()->selection();
+ m_frame->selectionController()->moveTo(VisiblePosition(selection.end(), DOWNSTREAM));
+}
+
+void DOMSelection::collapseToStart()
+{
+ if (!m_frame)
+ return;
+
+ const Selection& selection = m_frame->selectionController()->selection();
+ m_frame->selectionController()->moveTo(VisiblePosition(selection.start(), DOWNSTREAM));
+}
+
+void DOMSelection::empty()
+{
+ if (!m_frame)
+ return;
+ m_frame->selectionController()->moveTo(VisiblePosition());
+}
+
+void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec)
+{
+ if (!m_frame)
+ return;
+
+ if (baseOffset < 0 || extentOffset < 0) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+ VisiblePosition visibleBase = VisiblePosition(baseNode, baseOffset, DOWNSTREAM);
+ VisiblePosition visibleExtent = VisiblePosition(extentNode, extentOffset, DOWNSTREAM);
+
+ m_frame->selectionController()->moveTo(visibleBase, visibleExtent);
+}
+
+void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec)
+{
+ if (!m_frame)
+ return;
+ if (offset < 0) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+ m_frame->selectionController()->moveTo(VisiblePosition(node, offset, DOWNSTREAM));
+}
+
+void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
+{
+ if (!m_frame)
+ return;
+
+ String alterStringLower = alterString.lower();
+ SelectionController::EAlteration alter;
+ if (alterStringLower == "extend")
+ alter = SelectionController::EXTEND;
+ else if (alterStringLower == "move")
+ alter = SelectionController::MOVE;
+ else
+ return;
+
+ String directionStringLower = directionString.lower();
+ SelectionController::EDirection direction;
+ if (directionStringLower == "forward")
+ direction = SelectionController::FORWARD;
+ else if (directionStringLower == "backward")
+ direction = SelectionController::BACKWARD;
+ else if (directionStringLower == "left")
+ direction = SelectionController::LEFT;
+ else if (directionStringLower == "right")
+ direction = SelectionController::RIGHT;
+ else
+ return;
+
+ String granularityStringLower = granularityString.lower();
+ TextGranularity granularity;
+ if (granularityStringLower == "character")
+ granularity = CharacterGranularity;
+ else if (granularityStringLower == "word")
+ granularity = WordGranularity;
+ else if (granularityStringLower == "sentence")
+ granularity = SentenceGranularity;
+ else if (granularityStringLower == "line")
+ granularity = LineGranularity;
+ else if (granularityStringLower == "paragraph")
+ granularity = ParagraphGranularity;
+ else if (granularityStringLower == "lineboundary")
+ granularity = LineBoundary;
+ else if (granularityStringLower == "sentenceboundary")
+ granularity = SentenceBoundary;
+ else if (granularityStringLower == "paragraphboundary")
+ granularity = ParagraphBoundary;
+ else if (granularityStringLower == "documentboundary")
+ granularity = DocumentBoundary;
+ else
+ return;
+
+ m_frame->selectionController()->modify(alter, direction, granularity, false);
+}
+
+void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec)
+{
+ if (!m_frame)
+ return;
+
+ if (!node) {
+ ec = TYPE_MISMATCH_ERR;
+ return;
+ }
+ if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) {
+ ec = INDEX_SIZE_ERR;
+ return;
+ }
+
+ SelectionController* selectionController = m_frame->selectionController();
+ selectionController->expandUsingGranularity(CharacterGranularity);
+ selectionController->setExtent(VisiblePosition(node, offset, DOWNSTREAM));
+}
+
+PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec)
+{
+ if (!m_frame)
+ return 0;
+
+ if (index < 0 || index >= rangeCount()) {
+ ec = INDEX_SIZE_ERR;
+ return 0;
+ }
+
+ const Selection& selection = m_frame->selectionController()->selection();
+ return selection.toRange();
+}
+
+void DOMSelection::removeAllRanges()
+{
+ if (!m_frame)
+ return;
+ m_frame->selectionController()->clear();
+}
+
+void DOMSelection::addRange(Range* r)
+{
+ if (!m_frame)
+ return;
+ if (!r)
+ return;
+
+ SelectionController* selectionController = m_frame->selectionController();
+
+ if (selectionController->isNone()) {
+ selectionController->setSelection(Selection(r));
+ return;
+ }
+
+ RefPtr<Range> range = selectionController->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::END_TO_END, range.get(), ec) == -1)
+ // The original range and r intersect.
+ selectionController->setSelection(Selection(r->startPosition(), range->endPosition(), DOWNSTREAM));
+ else
+ // r contains the original range.
+ selectionController->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_END, range.get(), ec) == -1)
+ // The original range contains r.
+ selectionController->setSelection(Selection(range.get()));
+ else
+ // The original range and r intersect.
+ selectionController->setSelection(Selection(range->startPosition(), r->endPosition(), DOWNSTREAM));
+ }
+ }
+}
+
+void DOMSelection::deleteFromDocument()
+{
+ if (!m_frame)
+ return;
+
+ SelectionController* selectionController = m_frame->selectionController();
+
+ if (selectionController->isNone())
+ return;
+
+ if (isCollapsed())
+ selectionController->modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity);
+
+ RefPtr<Range> selectedRange = selectionController->selection().toRange();
+
+ ExceptionCode ec = 0;
+ selectedRange->deleteContents(ec);
+ ASSERT(!ec);
+
+ setBaseAndExtent(selectedRange->startContainer(ec), selectedRange->startOffset(ec), selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec);
+ ASSERT(!ec);
+}
+
+bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
+{
+ if (!m_frame)
+ return false;
+
+ SelectionController* selectionController = m_frame->selectionController();
+
+ if (!n || selectionController->isNone())
+ return false;
+
+ Node* parentNode = n->parentNode();
+ unsigned nodeIndex = n->nodeIndex();
+ RefPtr<Range> selectedRange = selectionController->selection().toRange();
+
+ if (!parentNode)
+ return false;
+
+ ExceptionCode ec = 0;
+ bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) >= 0
+ && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) <= 0;
+ ASSERT(!ec);
+ if (nodeFullySelected)
+ return true;
+
+ bool nodeFullyUnselected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) > 0
+ || Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) < 0;
+ ASSERT(!ec);
+ if (nodeFullyUnselected)
+ return false;
+
+ return allowPartial || n->isTextNode();
+}
+
+void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec)
+{
+ if (!n)
+ return;
+
+ // This doesn't (and shouldn't) select text node characters.
+ setBaseAndExtent(n, 0, n, n->childNodeCount(), ec);
+}
+
+String DOMSelection::toString()
+{
+ if (!m_frame)
+ return String();
+
+ return plainText(m_frame->selectionController()->selection().toRange().get());
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/DOMSelection.h b/WebCore/page/DOMSelection.h
new file mode 100644
index 0000000..fd8d1fc
--- /dev/null
+++ b/WebCore/page/DOMSelection.h
@@ -0,0 +1,102 @@
+/*
+ * 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 DOMSelection_h
+#define DOMSelection_h
+
+#include <wtf/RefCounted.h>
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+ class Frame;
+ class Range;
+ class Node;
+ class String;
+
+ typedef int ExceptionCode;
+
+ class DOMSelection : public RefCounted<DOMSelection> {
+ public:
+ static PassRefPtr<DOMSelection> create(Frame* frame) { return adoptRef(new DOMSelection(frame)); }
+
+ Frame* frame() const;
+ void disconnectFrame();
+
+ // Safari Selection Object API
+ // These methods return the valid equivalents of internal editing positions.
+ Node* baseNode() const;
+ Node* extentNode() const;
+ int baseOffset() const;
+ int extentOffset() const;
+ String type() const;
+ void setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode&);
+ void setPosition(Node*, int offset, ExceptionCode&);
+ void modify(const String& alter, const String& direction, const String& granularity);
+
+ // Mozilla Selection Object API
+ // In Firefox, anchor/focus are the equal to the start/end of the selection,
+ // but reflect the direction in which the selection was made by the user. That does
+ // not mean that they are base/extent, since the base/extent don't reflect
+ // expansion.
+ // These methods return the valid equivalents of internal editing positions.
+ Node* anchorNode() const;
+ int anchorOffset() const;
+ Node* focusNode() const;
+ int focusOffset() const;
+ bool isCollapsed() const;
+ int rangeCount() const;
+ void collapse(Node*, int offset, ExceptionCode&);
+ void collapseToEnd();
+ void collapseToStart();
+ void extend(Node*, int offset, ExceptionCode&);
+ PassRefPtr<Range> getRangeAt(int, ExceptionCode&);
+ void removeAllRanges();
+ void addRange(Range*);
+ void deleteFromDocument();
+ bool containsNode(const Node*, bool partlyContained) const;
+ void selectAllChildren(Node*, ExceptionCode&);
+
+ String toString();
+
+ // Microsoft Selection Object API
+ void empty();
+ //void clear();
+ //TextRange *createRange();
+
+ private:
+ DOMSelection(Frame*);
+
+ Frame* m_frame;
+ };
+
+} // namespace WebCore
+
+#endif // DOMSelection_h
diff --git a/WebCore/page/DOMSelection.idl b/WebCore/page/DOMSelection.idl
new file mode 100644
index 0000000..85d23bf
--- /dev/null
+++ b/WebCore/page/DOMSelection.idl
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+module window {
+
+ interface DOMSelection {
+ readonly attribute Node anchorNode;
+ readonly attribute long anchorOffset;
+ readonly attribute Node focusNode;
+ readonly attribute long focusOffset;
+ readonly attribute Node baseNode;
+ readonly attribute long baseOffset;
+ readonly attribute Node extentNode;
+ readonly attribute long extentOffset;
+ readonly attribute boolean isCollapsed;
+ readonly attribute DOMString type;
+ readonly attribute long rangeCount;
+
+ void collapse(in Node node, in long index)
+ raises(DOMException);
+ void collapseToEnd();
+ void collapseToStart();
+ void deleteFromDocument();
+ boolean containsNode(in Node node, in boolean allowPartial);
+ void selectAllChildren(in Node node)
+ raises(DOMException);
+ void empty();
+ void setBaseAndExtent(in Node baseNode, in long baseOffset, in Node extentNode, in long extentOffset)
+ raises(DOMException);
+ void setPosition(in Node node, in long offset)
+ raises(DOMException);
+ void modify(in DOMString alter, in DOMString direction, in DOMString granularity);
+ void extend(in Node node, in long offset)
+ raises(DOMException);
+ Range getRangeAt(in long index)
+ raises(DOMException);
+ void removeAllRanges();
+ void addRange(in Range range);
+
+#if defined(LANGUAGE_JAVASCRIPT)
+ [DontEnum] DOMString toString();
+#endif
+ };
+
+}
diff --git a/WebCore/page/DOMWindow.cpp b/WebCore/page/DOMWindow.cpp
new file mode 100644
index 0000000..22f7489
--- /dev/null
+++ b/WebCore/page/DOMWindow.cpp
@@ -0,0 +1,740 @@
+/*
+ * 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 "DOMWindow.h"
+
+#include "BarInfo.h"
+#include "CSSComputedStyleDeclaration.h"
+#include "CSSRuleList.h"
+#include "CSSStyleSelector.h"
+#include "Chrome.h"
+#include "Console.h"
+#include "DOMSelection.h"
+#include "Document.h"
+#include "Element.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "History.h"
+#include "MessageEvent.h"
+#include "Page.h"
+#include "PlatformScreen.h"
+#include "PlatformString.h"
+#include "Screen.h"
+#include <algorithm>
+#include <wtf/MathExtras.h>
+
+#if ENABLE(DATABASE)
+#include "Database.h"
+#endif
+
+using std::min;
+using std::max;
+
+namespace WebCore {
+
+// 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
+// bigger than the the float rect's dimensions.
+// 3) Constrain window rect to within the top and left boundaries of the screen rect
+// 4) Constraint the window rect to within the bottom and right boundaries of the
+// screen rect.
+// 5) Translate the window rect coordinates to be within the coordinate space of
+// the screen rect.
+void DOMWindow::adjustWindowRect(const FloatRect& screen, FloatRect& window, const FloatRect& pendingChanges)
+{
+ // Make sure we're in a valid state before adjusting dimensions.
+ ASSERT(isfinite(screen.x()));
+ ASSERT(isfinite(screen.y()));
+ ASSERT(isfinite(screen.width()));
+ ASSERT(isfinite(screen.height()));
+ ASSERT(isfinite(window.x()));
+ ASSERT(isfinite(window.y()));
+ ASSERT(isfinite(window.width()));
+ ASSERT(isfinite(window.height()));
+
+ // Update window values if new requested values are not NaN.
+ if (!isnan(pendingChanges.x()))
+ window.setX(pendingChanges.x());
+ if (!isnan(pendingChanges.y()))
+ window.setY(pendingChanges.y());
+ if (!isnan(pendingChanges.width()))
+ window.setWidth(pendingChanges.width());
+ if (!isnan(pendingChanges.height()))
+ window.setHeight(pendingChanges.height());
+
+ // Resize the window to between 100 and the screen width and height.
+ window.setWidth(min(max(100.0f, window.width()), screen.width()));
+ window.setHeight(min(max(100.0f, window.height()), screen.height()));
+
+ // Constrain the window position to the screen.
+ window.setX(max(screen.x(), min(window.x(), screen.right() - window.width())));
+ window.setY(max(screen.y(), min(window.y(), screen.bottom() - window.height())));
+}
+
+DOMWindow::DOMWindow(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+DOMWindow::~DOMWindow()
+{
+}
+
+void DOMWindow::disconnectFrame()
+{
+ m_frame = 0;
+ clear();
+}
+
+void DOMWindow::clear()
+{
+ if (m_screen)
+ m_screen->disconnectFrame();
+ m_screen = 0;
+
+ if (m_selection)
+ m_selection->disconnectFrame();
+ m_selection = 0;
+
+ if (m_history)
+ m_history->disconnectFrame();
+ m_history = 0;
+
+ if (m_locationbar)
+ m_locationbar->disconnectFrame();
+ m_locationbar = 0;
+
+ if (m_menubar)
+ m_menubar->disconnectFrame();
+ m_menubar = 0;
+
+ if (m_personalbar)
+ m_personalbar->disconnectFrame();
+ m_personalbar = 0;
+
+ if (m_scrollbars)
+ m_scrollbars->disconnectFrame();
+ m_scrollbars = 0;
+
+ if (m_statusbar)
+ m_statusbar->disconnectFrame();
+ m_statusbar = 0;
+
+ if (m_toolbar)
+ m_toolbar->disconnectFrame();
+ m_toolbar = 0;
+
+ if (m_console)
+ m_console->disconnectFrame();
+ m_console = 0;
+}
+
+Screen* DOMWindow::screen() const
+{
+ if (!m_screen)
+ m_screen = Screen::create(m_frame);
+ return m_screen.get();
+}
+
+History* DOMWindow::history() const
+{
+ if (!m_history)
+ m_history = History::create(m_frame);
+ return m_history.get();
+}
+
+BarInfo* DOMWindow::locationbar() const
+{
+ if (!m_locationbar)
+ m_locationbar = BarInfo::create(m_frame, BarInfo::Locationbar);
+ return m_locationbar.get();
+}
+
+BarInfo* DOMWindow::menubar() const
+{
+ if (!m_menubar)
+ m_menubar = BarInfo::create(m_frame, BarInfo::Menubar);
+ return m_menubar.get();
+}
+
+BarInfo* DOMWindow::personalbar() const
+{
+ if (!m_personalbar)
+ m_personalbar = BarInfo::create(m_frame, BarInfo::Personalbar);
+ return m_personalbar.get();
+}
+
+BarInfo* DOMWindow::scrollbars() const
+{
+ if (!m_scrollbars)
+ m_scrollbars = BarInfo::create(m_frame, BarInfo::Scrollbars);
+ return m_scrollbars.get();
+}
+
+BarInfo* DOMWindow::statusbar() const
+{
+ if (!m_statusbar)
+ m_statusbar = BarInfo::create(m_frame, BarInfo::Statusbar);
+ return m_statusbar.get();
+}
+
+BarInfo* DOMWindow::toolbar() const
+{
+ if (!m_toolbar)
+ m_toolbar = BarInfo::create(m_frame, BarInfo::Toolbar);
+ return m_toolbar.get();
+}
+
+Console* DOMWindow::console() const
+{
+ if (!m_console)
+ 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
+{
+ ExceptionCode ec;
+ document()->dispatchEvent(new MessageEvent(message, domain, uri, source), ec, true);
+}
+#endif
+
+DOMSelection* DOMWindow::getSelection()
+{
+ if (!m_selection)
+ m_selection = DOMSelection::create(m_frame);
+ return m_selection.get();
+}
+
+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();
+}
+
+void DOMWindow::focus()
+{
+ if (!m_frame)
+ return;
+
+ m_frame->focusWindow();
+}
+
+void DOMWindow::blur()
+{
+ if (!m_frame)
+ return;
+
+ m_frame->unfocusWindow();
+}
+
+void DOMWindow::close()
+{
+ if (!m_frame)
+ return;
+
+ if (m_frame->loader()->openedByDOM() || m_frame->loader()->getHistoryLength() <= 1)
+ m_frame->scheduleClose();
+}
+
+void DOMWindow::print()
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ page->chrome()->print(m_frame);
+}
+
+void DOMWindow::stop()
+{
+ if (!m_frame)
+ return;
+
+ // We must check whether the load is complete asynchronously, because we might still be parsing
+ // the document until the callstack unwinds.
+ m_frame->loader()->stopForUserCancel(true);
+}
+
+void DOMWindow::alert(const String& message)
+{
+ if (!m_frame)
+ return;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (doc)
+ doc->updateRendering();
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ page->chrome()->runJavaScriptAlert(m_frame, message);
+}
+
+bool DOMWindow::confirm(const String& message)
+{
+ if (!m_frame)
+ return false;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (doc)
+ doc->updateRendering();
+
+ Page* page = m_frame->page();
+ if (!page)
+ return false;
+
+ return page->chrome()->runJavaScriptConfirm(m_frame, message);
+}
+
+String DOMWindow::prompt(const String& message, const String& defaultValue)
+{
+ if (!m_frame)
+ return String();
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (doc)
+ doc->updateRendering();
+
+ Page* page = m_frame->page();
+ if (!page)
+ return String();
+
+ String returnValue;
+ if (page->chrome()->runJavaScriptPrompt(m_frame, message, defaultValue, returnValue))
+ return returnValue;
+
+ return String();
+}
+
+bool DOMWindow::find(const String& string, bool caseSensitive, bool backwards, bool wrap, bool wholeWord, bool searchInFrames, bool showDialog) const
+{
+ if (!m_frame)
+ return false;
+
+ // FIXME (13016): Support wholeWord, searchInFrames and showDialog
+ return m_frame->findString(string, !backwards, caseSensitive, wrap, false);
+}
+
+bool DOMWindow::offscreenBuffering() const
+{
+ return true;
+}
+
+int DOMWindow::outerHeight() const
+{
+ if (!m_frame)
+ return 0;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return 0;
+
+ return static_cast<int>(page->chrome()->windowRect().height());
+}
+
+int DOMWindow::outerWidth() const
+{
+ if (!m_frame)
+ return 0;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return 0;
+
+ return static_cast<int>(page->chrome()->windowRect().width());
+}
+
+int DOMWindow::innerHeight() const
+{
+ if (!m_frame)
+ return 0;
+
+ FrameView* view = m_frame->view();
+ if (!view)
+ return 0;
+
+ return view->height();
+}
+
+int DOMWindow::innerWidth() const
+{
+ if (!m_frame)
+ return 0;
+
+ FrameView* view = m_frame->view();
+ if (!view)
+ return 0;
+
+ return view->width();
+}
+
+int DOMWindow::screenX() const
+{
+ if (!m_frame)
+ return 0;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return 0;
+
+ return static_cast<int>(page->chrome()->windowRect().x());
+}
+
+int DOMWindow::screenY() const
+{
+ if (!m_frame)
+ return 0;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return 0;
+
+ return static_cast<int>(page->chrome()->windowRect().y());
+}
+
+int DOMWindow::scrollX() const
+{
+ if (!m_frame)
+ return 0;
+
+ FrameView* view = m_frame->view();
+ if (!view)
+ return 0;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (doc)
+ doc->updateLayoutIgnorePendingStylesheets();
+
+ return view->contentsX();
+}
+
+int DOMWindow::scrollY() const
+{
+ if (!m_frame)
+ return 0;
+
+ FrameView* view = m_frame->view();
+ if (!view)
+ return 0;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (doc)
+ doc->updateLayoutIgnorePendingStylesheets();
+
+ return view->contentsY();
+}
+
+bool DOMWindow::closed() const
+{
+ return !m_frame;
+}
+
+unsigned DOMWindow::length() const
+{
+ if (!m_frame)
+ return 0;
+
+ return m_frame->tree()->childCount();
+}
+
+String DOMWindow::name() const
+{
+ if (!m_frame)
+ return String();
+
+ return m_frame->tree()->name();
+}
+
+void DOMWindow::setName(const String& string)
+{
+ if (!m_frame)
+ return;
+
+ m_frame->tree()->setName(string);
+}
+
+String DOMWindow::status() const
+{
+ if (!m_frame)
+ return String();
+
+ return m_frame->jsStatusBarText();
+}
+
+void DOMWindow::setStatus(const String& string)
+{
+ if (!m_frame)
+ return;
+
+ m_frame->setJSStatusBarText(string);
+}
+
+String DOMWindow::defaultStatus() const
+{
+ if (!m_frame)
+ return String();
+
+ return m_frame->jsDefaultStatusBarText();
+}
+
+void DOMWindow::setDefaultStatus(const String& string)
+{
+ if (!m_frame)
+ return;
+
+ m_frame->setJSDefaultStatusBarText(string);
+}
+
+DOMWindow* DOMWindow::self() const
+{
+ if (!m_frame)
+ return 0;
+
+ return m_frame->domWindow();
+}
+
+DOMWindow* DOMWindow::opener() const
+{
+ if (!m_frame)
+ return 0;
+
+ Frame* opener = m_frame->loader()->opener();
+ if (!opener)
+ return 0;
+
+ return opener->domWindow();
+}
+
+DOMWindow* DOMWindow::parent() const
+{
+ if (!m_frame)
+ return 0;
+
+ Frame* parent = m_frame->tree()->parent();
+ if (parent)
+ return parent->domWindow();
+
+ return m_frame->domWindow();
+}
+
+DOMWindow* DOMWindow::top() const
+{
+ if (!m_frame)
+ return 0;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return 0;
+
+ return page->mainFrame()->domWindow();
+}
+
+Document* DOMWindow::document() const
+{
+ if (!m_frame)
+ return 0;
+
+ ASSERT(m_frame->document());
+ return m_frame->document();
+}
+
+PassRefPtr<CSSStyleDeclaration> DOMWindow::getComputedStyle(Element* elt, const String&) const
+{
+ if (!elt)
+ return 0;
+
+ // FIXME: This needs to work with pseudo elements.
+ return new CSSComputedStyleDeclaration(elt);
+}
+
+PassRefPtr<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* elt, const String& pseudoElt, bool authorOnly) const
+{
+ if (!m_frame)
+ return 0;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (!doc)
+ return 0;
+
+ if (!pseudoElt.isEmpty())
+ return doc->styleSelector()->pseudoStyleRulesForElement(elt, pseudoElt.impl(), authorOnly);
+ return doc->styleSelector()->styleRulesForElement(elt, authorOnly);
+}
+
+double DOMWindow::devicePixelRatio() const
+{
+ if (!m_frame)
+ return 0.0;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return 0.0;
+
+ return page->chrome()->scaleFactor();
+}
+
+#if ENABLE(DATABASE)
+PassRefPtr<Database> DOMWindow::openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, ExceptionCode& ec)
+{
+ if (!m_frame)
+ return 0;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (!doc)
+ return 0;
+
+ return Database::openDatabase(doc, name, version, displayName, estimatedSize, ec);
+}
+#endif
+
+void DOMWindow::scrollBy(int x, int y) const
+{
+ if (!m_frame)
+ return;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (doc)
+ doc->updateLayoutIgnorePendingStylesheets();
+
+ FrameView* view = m_frame->view();
+ if (!view)
+ return;
+
+ view->scrollBy(x, y);
+}
+
+void DOMWindow::scrollTo(int x, int y) const
+{
+ if (!m_frame)
+ return;
+
+ Document* doc = m_frame->document();
+ ASSERT(doc);
+ if (doc)
+ doc->updateLayoutIgnorePendingStylesheets();
+
+ FrameView* view = m_frame->view();
+ if (!view)
+ return;
+
+ view->setContentsPos(x, y);
+}
+
+void DOMWindow::moveBy(float x, float y) const
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ FloatRect fr = page->chrome()->windowRect();
+ FloatRect update = fr;
+ update.move(x, y);
+ // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
+ adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
+ page->chrome()->setWindowRect(fr);
+}
+
+void DOMWindow::moveTo(float x, float y) const
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ FloatRect fr = page->chrome()->windowRect();
+ FloatRect sr = screenAvailableRect(page->mainFrame()->view());
+ fr.setLocation(sr.location());
+ FloatRect update = fr;
+ update.move(x, y);
+ // Security check (the spec talks about UniversalBrowserWrite to disable this check...)
+ adjustWindowRect(sr, fr, update);
+ page->chrome()->setWindowRect(fr);
+}
+
+void DOMWindow::resizeBy(float x, float y) const
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ FloatRect fr = page->chrome()->windowRect();
+ FloatSize dest = fr.size() + FloatSize(x, y);
+ FloatRect update(fr.location(), dest);
+ adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
+ page->chrome()->setWindowRect(fr);
+}
+
+void DOMWindow::resizeTo(float width, float height) const
+{
+ if (!m_frame)
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ FloatRect fr = page->chrome()->windowRect();
+ FloatSize dest = FloatSize(width, height);
+ FloatRect update(fr.location(), dest);
+ adjustWindowRect(screenAvailableRect(page->mainFrame()->view()), fr, update);
+ page->chrome()->setWindowRect(fr);
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/DOMWindow.h b/WebCore/page/DOMWindow.h
new file mode 100644
index 0000000..adb4731
--- /dev/null
+++ b/WebCore/page/DOMWindow.h
@@ -0,0 +1,177 @@
+/*
+ * 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 DOMWindow_h
+#define DOMWindow_h
+
+#include "PlatformString.h"
+#include <wtf/RefCounted.h>
+#include <wtf/Forward.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+ class BarInfo;
+ class CSSRuleList;
+ class CSSStyleDeclaration;
+ class Console;
+ class DOMSelection;
+ class Database;
+ class Document;
+ class Element;
+ class FloatRect;
+ class Frame;
+ class History;
+ class Screen;
+
+ typedef int ExceptionCode;
+
+ class DOMWindow : public RefCounted<DOMWindow> {
+ public:
+ static PassRefPtr<DOMWindow> create(Frame* frame) { return adoptRef(new DOMWindow(frame)); }
+ virtual ~DOMWindow();
+
+ Frame* frame() { return m_frame; }
+ void disconnectFrame();
+
+ void clear();
+
+ static void adjustWindowRect(const FloatRect& screen, FloatRect& window, const FloatRect& pendingChanges);
+
+ // DOM Level 0
+ Screen* screen() const;
+ History* history() const;
+ BarInfo* locationbar() const;
+ BarInfo* menubar() const;
+ BarInfo* personalbar() const;
+ BarInfo* scrollbars() const;
+ BarInfo* statusbar() const;
+ BarInfo* toolbar() const;
+
+ DOMSelection* getSelection();
+
+ Element* frameElement() const;
+
+ void focus();
+ void blur();
+ void close();
+ void print();
+ void stop();
+
+ void alert(const String& message);
+ bool confirm(const String& message);
+ String prompt(const String& message, const String& defaultValue);
+
+ bool find(const String&, bool caseSensitive, bool backwards, bool wrap, bool wholeWord, bool searchInFrames, bool showDialog) const;
+
+ bool offscreenBuffering() const;
+
+ int outerHeight() const;
+ int outerWidth() const;
+ int innerHeight() const;
+ int innerWidth() const;
+ int screenX() const;
+ int screenY() const;
+ int screenLeft() const { return screenX(); }
+ int screenTop() const { return screenY(); }
+ int scrollX() const;
+ int scrollY() const;
+ int pageXOffset() const { return scrollX(); }
+ int pageYOffset() const { return scrollY(); }
+
+ bool closed() const;
+
+ unsigned length() const;
+
+ String name() const;
+ void setName(const String&);
+
+ String status() const;
+ void setStatus(const String&);
+ String defaultStatus() const;
+ void setDefaultStatus(const String&);
+ // This attribute is an alias of defaultStatus and is necessary for legacy uses.
+ String defaultstatus() const { return defaultStatus(); }
+ void setDefaultstatus(const String& status) { setDefaultStatus(status); }
+
+ // Self referential attributes
+ DOMWindow* self() const;
+ DOMWindow* window() const { return self(); }
+ DOMWindow* frames() const { return self(); }
+
+ DOMWindow* opener() const;
+ DOMWindow* parent() const;
+ DOMWindow* top() const;
+
+ // DOM Level 2 AbstractView Interface
+ Document* document() const;
+
+ // DOM Level 2 Style Interface
+ PassRefPtr<CSSStyleDeclaration> getComputedStyle(Element*, const String& pseudoElt) const;
+
+ // WebKit extensions
+ PassRefPtr<CSSRuleList> getMatchedCSSRules(Element*, const String& pseudoElt, bool authorOnly = true) const;
+ double devicePixelRatio() const;
+
+#if ENABLE(DATABASE)
+ // HTML 5 client-side database
+ PassRefPtr<Database> openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, ExceptionCode&);
+#endif
+
+ Console* console() const;
+
+#if ENABLE(CROSS_DOCUMENT_MESSAGING)
+ void postMessage(const String& message, const String& domain, const String& uri, DOMWindow* source) const;
+#endif
+
+ void scrollBy(int x, int y) const;
+ void scrollTo(int x, int y) const;
+ void scroll(int x, int y) const { scrollTo(x, y); }
+
+ void moveBy(float x, float y) const;
+ void moveTo(float x, float y) const;
+
+ void resizeBy(float x, float y) const;
+ void resizeTo(float width, float height) const;
+
+ private:
+ DOMWindow(Frame*);
+
+ Frame* m_frame;
+ mutable RefPtr<Screen> m_screen;
+ mutable RefPtr<DOMSelection> m_selection;
+ mutable RefPtr<History> m_history;
+ mutable RefPtr<BarInfo> m_locationbar;
+ mutable RefPtr<BarInfo> m_menubar;
+ mutable RefPtr<BarInfo> m_personalbar;
+ mutable RefPtr<BarInfo> m_scrollbars;
+ mutable RefPtr<BarInfo> m_statusbar;
+ mutable RefPtr<BarInfo> m_toolbar;
+ mutable RefPtr<Console> m_console;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/WebCore/page/DOMWindow.idl b/WebCore/page/DOMWindow.idl
new file mode 100644
index 0000000..655d6ab
--- /dev/null
+++ b/WebCore/page/DOMWindow.idl
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+module window {
+
+ interface [
+ LegacyParent=KJS::Window,
+ DoNotCache,
+ CheckDomainSecurity,
+ GenerateNativeConverter,
+ CustomGetOwnPropertySlot,
+ CustomPutFunction,
+ CustomDeleteProperty,
+ CustomGetPropertyNames
+ ] DOMWindow {
+ // DOM Level 0
+ readonly attribute Screen screen;
+ readonly attribute [DoNotCheckDomainSecurity] History history;
+ attribute [Replaceable] BarInfo locationbar;
+ attribute [Replaceable] BarInfo menubar;
+ attribute [Replaceable] BarInfo personalbar;
+ attribute [Replaceable] BarInfo scrollbars;
+ attribute [Replaceable] BarInfo statusbar;
+ attribute [Replaceable] BarInfo toolbar;
+
+ DOMSelection getSelection();
+
+ readonly attribute [CheckNodeSecurity] Element frameElement;
+
+ [DoNotCheckDomainSecurity] void focus();
+ [DoNotCheckDomainSecurity] void blur();
+ [DoNotCheckDomainSecurity] void close();
+
+ void print();
+ void stop();
+
+ void alert(in DOMString message);
+ boolean confirm(in DOMString message);
+ [ConvertNullStringTo=Null] DOMString prompt(in DOMString message,
+ in [ConvertUndefinedOrNullToNullString] DOMString defaultValue);
+
+ boolean find(in DOMString string,
+ in boolean caseSensitive,
+ in boolean backwards,
+ in boolean wrap,
+ in boolean wholeWord,
+ in boolean searchInFrames,
+ in boolean showDialog);
+
+ attribute [Replaceable] boolean offscreenBuffering;
+
+ attribute [Replaceable] long outerHeight;
+ attribute [Replaceable] long outerWidth;
+ attribute [Replaceable] long innerHeight;
+ attribute [Replaceable] long innerWidth;
+ attribute [Replaceable] long screenX;
+ attribute [Replaceable] long screenY;
+ attribute [Replaceable] long screenLeft;
+ attribute [Replaceable] long screenTop;
+ attribute [Replaceable] long scrollX;
+ attribute [Replaceable] long scrollY;
+ readonly attribute long pageXOffset;
+ readonly attribute long pageYOffset;
+
+ [RequiresAllArguments] void scrollBy(in long x, in long y);
+ [RequiresAllArguments] void scrollTo(in long x, in long y);
+ [RequiresAllArguments] void scroll(in long x, in long y);
+ [RequiresAllArguments] void moveBy(in float x, in float y); // FIXME: this should take longs not floats.
+ [RequiresAllArguments] void moveTo(in float x, in float y); // FIXME: this should take longs not floats.
+ [RequiresAllArguments] void resizeBy(in float x, in float y); // FIXME: this should take longs not floats.
+ [RequiresAllArguments] void resizeTo(in float width, in float height); // FIXME: this should take longs not floats.
+
+ readonly attribute [DoNotCheckDomainSecurity] boolean closed;
+
+ attribute [Replaceable, DoNotCheckDomainSecurityOnGet] unsigned long length;
+
+ attribute DOMString name;
+
+ attribute DOMString status;
+ attribute DOMString defaultStatus;
+#if defined(LANGUAGE_JAVASCRIPT)
+ // This attribute is an alias of defaultStatus and is necessary for legacy uses.
+ attribute DOMString defaultstatus;
+#endif
+
+ // Self referential attributes
+ attribute [Replaceable, DoNotCheckDomainSecurityOnGet] DOMWindow self;
+ readonly attribute [DoNotCheckDomainSecurity] DOMWindow window;
+ attribute [Replaceable, DoNotCheckDomainSecurityOnGet] DOMWindow frames;
+
+ attribute [Replaceable, DoNotCheckDomainSecurityOnGet] DOMWindow opener;
+ attribute [Replaceable, DoNotCheckDomainSecurity] DOMWindow parent;
+ attribute [Replaceable, DoNotCheckDomainSecurity] DOMWindow top;
+
+ // DOM Level 2 AbstractView Interface
+ readonly attribute Document document;
+
+ // DOM Level 2 Style Interface
+ CSSStyleDeclaration getComputedStyle(in Element element,
+ in DOMString pseudoElement);
+
+ // WebKit extensions
+ CSSRuleList getMatchedCSSRules(in Element element,
+ in DOMString pseudoElement,
+ in [Optional] boolean authorOnly);
+ attribute [Replaceable] double devicePixelRatio;
+
+#if defined(ENABLE_DATABASE)
+ Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName, in unsigned long estimatedSize)
+ raises(DOMException);
+#endif
+
+ attribute [Replaceable] Console console;
+
+#if defined(ENABLE_CROSS_DOCUMENT_MESSAGING)
+ // cross-document messaging
+ [DoNotCheckDomainSecurity, Custom] void postMessage(in DOMString message);
+#endif
+
+#if defined(LANGUAGE_JAVASCRIPT)
+ // Global constructors
+ attribute StyleSheetConstructor StyleSheet;
+ attribute CSSStyleSheetConstructor CSSStyleSheet;
+
+ attribute CSSValueConstructor CSSValue;
+ attribute CSSPrimitiveValueConstructor CSSPrimitiveValue;
+ attribute CSSValueListConstructor CSSValueList;
+
+ attribute CSSRuleConstructor CSSRule;
+ attribute CSSCharsetRuleConstructor CSSCharsetRule;
+ attribute CSSFontFaceRuleConstructor CSSFontFaceRule;
+ attribute CSSImportRuleConstructor CSSImportRule;
+ attribute CSSMediaRuleConstructor CSSMediaRule;
+ attribute CSSPageRuleConstructor CSSPageRule;
+ attribute CSSStyleRuleConstructor CSSStyleRule;
+
+ attribute CSSStyleDeclarationConstructor CSSStyleDeclaration;
+ attribute MediaListConstructor MediaList;
+ attribute CounterConstructor Counter;
+ attribute CSSRuleListConstructor CSSRuleList;
+ attribute RectConstructor Rect;
+ attribute StyleSheetListConstructor StyleSheetList;
+
+ // FIXME: Implement the commented-out global constructors for interfaces listed in DOM Level 3 Core specification.
+ attribute DOMCoreExceptionConstructor DOMException;
+// attribute DOMStringListConstructor DOMStringList;
+// attribute NameListConstructor NameList;
+// attribute DOMImplementationListConstructor DOMImplementationList;
+// attribute DOMImplementationSourceConstructor DOMImplementationSource;
+ attribute DOMImplementationConstructor DOMImplementation;
+ attribute DocumentFragmentConstructor DocumentFragment;
+ attribute DocumentConstructor Document;
+ attribute NodeConstructor Node;
+ attribute NodeListConstructor NodeList;
+ attribute NamedNodeMapConstructor NamedNodeMap;
+ attribute CharacterDataConstructor CharacterData;
+ attribute AttrConstructor Attr;
+ attribute ElementConstructor Element;
+ attribute TextConstructor Text;
+ attribute CommentConstructor Comment;
+// attribute TypeInfoConstructor TypeInfo;
+// attribute UserDataHandlerConstructor UserDataHandler;
+// attribute DOMErrorConstructor DOMError;
+// attribute DOMErrorHandlerConstructor DOMErrorHandler
+// attribute DOMLocatorConstructor DOMLocator;
+// attribute DOMConfigurationConstructor DOMConfiguration;
+ attribute CDATASectionConstructor CDATASection;
+ attribute DocumentTypeConstructor DocumentType;
+ attribute NotationConstructor Notation;
+ attribute EntityConstructor Entity;
+ attribute EntityReferenceConstructor EntityReference;
+ attribute ProcessingInstructionConstructor ProcessingInstruction;
+
+ attribute HTMLDocumentConstructor HTMLDocument;
+
+ attribute HTMLElementConstructor HTMLElement;
+ attribute HTMLAnchorElementConstructor HTMLAnchorElement;
+ attribute HTMLAppletElementConstructor HTMLAppletElement;
+ attribute HTMLAreaElementConstructor HTMLAreaElement;
+ attribute HTMLBRElementConstructor HTMLBRElement;
+ attribute HTMLBaseElementConstructor HTMLBaseElement;
+ attribute HTMLBaseFontElementConstructor HTMLBaseFontElement;
+ attribute HTMLBlockquoteElementConstructor HTMLBlockquoteElement;
+ attribute HTMLBodyElementConstructor HTMLBodyElement;
+ attribute HTMLButtonElementConstructor HTMLButtonElement;
+ attribute HTMLCanvasElementConstructor HTMLCanvasElement;
+ attribute HTMLDListElementConstructor HTMLDListElement;
+ attribute HTMLDirectoryElementConstructor HTMLDirectoryElement;
+ attribute HTMLDivElementConstructor HTMLDivElement;
+ attribute HTMLEmbedElementConstructor HTMLEmbedElement;
+ attribute HTMLFieldSetElementConstructor HTMLFieldSetElement;
+ attribute HTMLFontElementConstructor HTMLFontElement;
+ attribute HTMLFormElementConstructor HTMLFormElement;
+ attribute HTMLFrameElementConstructor HTMLFrameElement;
+ attribute HTMLFrameSetElementConstructor HTMLFrameSetElement;
+ attribute HTMLHRElementConstructor HTMLHRElement;
+ attribute HTMLHeadElementConstructor HTMLHeadElement;
+ attribute HTMLHeadingElementConstructor HTMLHeadingElement;
+ attribute HTMLHtmlElementConstructor HTMLHtmlElement;
+ attribute HTMLIFrameElementConstructor HTMLIFrameElement;
+ attribute HTMLImageElementConstructor HTMLImageElement;
+ attribute HTMLInputElementConstructor HTMLInputElement;
+ attribute HTMLIsIndexElementConstructor HTMLIsIndexElement;
+ attribute HTMLLIElementConstructor HTMLLIElement;
+ attribute HTMLLabelElementConstructor HTMLLabelElement;
+ attribute HTMLLegendElementConstructor HTMLLegendElement;
+ attribute HTMLLinkElementConstructor HTMLLinkElement;
+ attribute HTMLMapElementConstructor HTMLMapElement;
+ attribute HTMLMarqueeElementConstructor HTMLMarqueeElement;
+ attribute HTMLMenuElementConstructor HTMLMenuElement;
+ attribute HTMLMetaElementConstructor HTMLMetaElement;
+ attribute HTMLModElementConstructor HTMLModElement;
+ attribute HTMLOListElementConstructor HTMLOListElement;
+ attribute HTMLObjectElementConstructor HTMLObjectElement;
+ attribute HTMLOptGroupElementConstructor HTMLOptGroupElement;
+ attribute HTMLOptionElementConstructor HTMLOptionElement;
+ attribute HTMLParagraphElementConstructor HTMLParagraphElement;
+ attribute HTMLParamElementConstructor HTMLParamElement;
+ attribute HTMLPreElementConstructor HTMLPreElement;
+ attribute HTMLQuoteElementConstructor HTMLQuoteElement;
+ attribute HTMLScriptElementConstructor HTMLScriptElement;
+ attribute HTMLSelectElementConstructor HTMLSelectElement;
+ attribute HTMLStyleElementConstructor HTMLStyleElement;
+ attribute HTMLTableCaptionElementConstructor HTMLTableCaptionElement;
+ attribute HTMLTableCellElementConstructor HTMLTableCellElement;
+ attribute HTMLTableColElementConstructor HTMLTableColElement;
+ attribute HTMLTableElementConstructor HTMLTableElement;
+ attribute HTMLTableRowElementConstructor HTMLTableRowElement;
+ attribute HTMLTableSectionElementConstructor HTMLTableSectionElement;
+ attribute HTMLTextAreaElementConstructor HTMLTextAreaElement;
+ attribute HTMLTitleElementConstructor HTMLTitleElement;
+ attribute HTMLUListElementConstructor HTMLUListElement;
+
+ attribute EventConstructor Event;
+ attribute KeyboardEventConstructor KeyboardEvent;
+ attribute MouseEventConstructor MouseEvent;
+ attribute MutationEventConstructor MutationEvent;
+ attribute OverflowEventConstructor OverflowEvent;
+ attribute ProgressEventConstructor ProgressEvent;
+ attribute TextEventConstructor TextEvent;
+ attribute UIEventConstructor UIEvent;
+ attribute WheelEventConstructor WheelEvent;
+ attribute EventExceptionConstructor EventException;
+
+ attribute NodeFilterConstructor NodeFilter;
+ attribute RangeConstructor Range;
+ attribute RangeExceptionConstructor RangeException;
+
+ // Mozilla has a separate XMLDocument object for XML documents.
+ // We just use Document for this.
+ attribute DocumentConstructor XMLDocument;
+
+ attribute DOMParserConstructor DOMParser;
+ attribute XMLSerializerConstructor XMLSerializer;
+
+ attribute XMLHttpRequestExceptionConstructor XMLHttpRequestException;
+
+#if defined(ENABLE_CROSS_DOCUMENT_MESSAGING)
+ attribute MessageEventConstructor MessageEvent;
+#endif
+
+#if defined(ENABLE_VIDEO)
+ attribute HTMLAudioElementConstructor HTMLAudioElement;
+ attribute HTMLMediaElementConstructor HTMLMediaElement;
+ attribute HTMLVideoElementConstructor HTMLVideoElement;
+ attribute MediaErrorConstructor MediaError;
+#endif
+
+#if defined(ENABLE_XPATH)
+ attribute XPathEvaluatorConstructor XPathEvaluator;
+ attribute XPathResultConstructor XPathResult;
+ attribute XPathExceptionConstructor XPathException;
+#endif
+
+#if defined(ENABLE_SVG)
+ attribute SVGAngleConstructor SVGAngle;
+ attribute SVGColorConstructor SVGColor;
+// attribute SVGCSSRuleConstructor SVGCSSRule;
+ attribute SVGExceptionConstructor SVGException;
+ attribute SVGGradientElementConstructor SVGGradientElement;
+ attribute SVGLengthConstructor SVGLength;
+ attribute SVGMarkerElementConstructor SVGMarkerElement;
+ attribute SVGPaintConstructor SVGPaint;
+ attribute SVGPathSegConstructor SVGPathSeg;
+ attribute SVGPreserveAspectRatioConstructor SVGPreserveAspectRatio;
+ attribute SVGRenderingIntentConstructor SVGRenderingIntent;
+ attribute SVGTextContentElementConstructor SVGTextContentElement;
+ attribute SVGTextPathElementConstructor SVGTextPathElement;
+ attribute SVGTransformConstructor SVGTransform;
+ attribute SVGUnitTypesConstructor SVGUnitTypes;
+// attribute SVGZoomAndPanConstructor SVGZoomAndPan;
+#endif
+
+#if defined(ENABLED_SVG_FILTERS)
+ attribute SVGComponentTransferFunctionElementConstructor SVGComponentTransferFunctionElement;
+ attribute SVGFEBlendElementConstructor SVGFEBlendElement;
+ attribute SVGFEColorMatrixElementConstructor SVGFEColorMatrixElement;
+ attribute SVGFECompositeElementConstructor SVGFECompositeElement;
+// attribute SVGFEConvolveMatrixElementConstructor SVGFEConvolveMatrixElement;
+ attribute SVGFEDisplacementMapElementConstructor SVGFEDisplacementMapElement;
+// attribute SVGFEMorphologyElementConstructor SVGFEMorphologyElement;
+ attribute SVGFETurbulenceElementConstructor SVGFETurbulenceElement;
+#endif
+
+#endif // defined(LANGUAGE_JAVASCRIPT)
+ };
+
+}
diff --git a/WebCore/page/DragActions.h b/WebCore/page/DragActions.h
new file mode 100644
index 0000000..37b783b
--- /dev/null
+++ b/WebCore/page/DragActions.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ *
+ * 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 DragActions_h
+#define DragActions_h
+
+#include <limits.h>
+
+namespace WebCore {
+
+ // WebCoreDragDestinationAction should be kept in sync with WebDragDestinationAction
+ typedef enum {
+ DragDestinationActionNone = 0,
+ DragDestinationActionDHTML = 1,
+ DragDestinationActionEdit = 2,
+ DragDestinationActionLoad = 4,
+ DragDestinationActionAny = UINT_MAX
+ } DragDestinationAction;
+
+ // WebCoreDragSourceAction should be kept in sync with WebDragSourceAction
+ typedef enum {
+ DragSourceActionNone = 0,
+ DragSourceActionDHTML = 1,
+ DragSourceActionImage = 2,
+ DragSourceActionLink = 4,
+ DragSourceActionSelection = 8,
+ DragSourceActionAny = UINT_MAX
+ } DragSourceAction;
+
+ //matches NSDragOperation
+ typedef enum {
+ DragOperationNone = 0,
+ DragOperationCopy = 1,
+ DragOperationLink = 2,
+ DragOperationGeneric = 4,
+ DragOperationPrivate = 8,
+ DragOperationMove = 16,
+ DragOperationDelete = 32,
+ DragOperationEvery = UINT_MAX
+ } DragOperation;
+
+}
+
+#endif // !DragActions_h
diff --git a/WebCore/page/DragClient.h b/WebCore/page/DragClient.h
new file mode 100644
index 0000000..4f343a0
--- /dev/null
+++ b/WebCore/page/DragClient.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ *
+ * 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 DragClient_h
+#define DragClient_h
+
+#include "DragActions.h"
+#include "DragImage.h"
+#include "IntPoint.h"
+
+#if PLATFORM(MAC)
+#ifdef __OBJC__
+@class DOMElement;
+@class NSURL;
+@class NSString;
+@class NSPasteboard;
+#else
+class DOMElement;
+class NSURL;
+class NSString;
+class NSPasteboard;
+#endif
+#endif
+
+namespace WebCore {
+
+ class Clipboard;
+ class DragData;
+ class Frame;
+ class Image;
+ class HTMLImageElement;
+
+ class DragClient {
+ public:
+ virtual void willPerformDragDestinationAction(DragDestinationAction, DragData*) = 0;
+ virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard*) = 0;
+ virtual DragDestinationAction actionMaskForDrag(DragData*) = 0;
+ //We work in window rather than view coordinates here
+ virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint& windowPoint) = 0;
+
+ virtual void startDrag(DragImageRef dragImage, const IntPoint& dragImageOrigin, const IntPoint& eventPos, Clipboard*, Frame*, bool linkDrag = false) = 0;
+ virtual DragImageRef createDragImageForLink(KURL&, const String& label, Frame*) = 0;
+
+ virtual void dragControllerDestroyed() = 0;
+#if PLATFORM(MAC)
+ //Mac specific helper functions to allow access to functionality in webkit -- such as
+ //web archives and NSPasteboard extras
+ //not abstract as that would require another #if PLATFORM(MAC) for the SVGImage client empty impl
+ virtual void declareAndWriteDragImage(NSPasteboard*, DOMElement*, NSURL*, NSString*, Frame*) {};
+#endif
+
+ virtual ~DragClient() {};
+ };
+
+}
+
+#endif // !DragClient_h
+
diff --git a/WebCore/page/DragController.cpp b/WebCore/page/DragController.cpp
new file mode 100644
index 0000000..8eec6e0
--- /dev/null
+++ b/WebCore/page/DragController.cpp
@@ -0,0 +1,769 @@
+/*
+ * 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.
+ *
+ * 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 "DragController.h"
+
+#include "CSSStyleDeclaration.h"
+#include "Clipboard.h"
+#include "ClipboardAccessPolicy.h"
+#include "DocLoader.h"
+#include "Document.h"
+#include "DocumentFragment.h"
+#include "DragActions.h"
+#include "DragClient.h"
+#include "DragData.h"
+#include "Editor.h"
+#include "EditorClient.h"
+#include "Element.h"
+#include "EventHandler.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HitTestResult.h"
+#include "Image.h"
+#include "MoveSelectionCommand.h"
+#include "Node.h"
+#include "Page.h"
+#include "PluginInfoStore.h"
+#include "RenderFileUploadControl.h"
+#include "RenderImage.h"
+#include "ReplaceSelectionCommand.h"
+#include "ResourceRequest.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SystemTime.h"
+#include "Text.h"
+#include "markup.h"
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+static PlatformMouseEvent createMouseEvent(DragData* dragData)
+{
+ // FIXME: We should fake modifier keys here.
+ return PlatformMouseEvent(dragData->clientPosition(), dragData->globalPosition(),
+ LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime());
+
+}
+
+DragController::DragController(Page* page, DragClient* client)
+ : m_page(page)
+ , m_client(client)
+ , m_document(0)
+ , m_dragInitiator(0)
+ , m_dragDestinationAction(DragDestinationActionNone)
+ , m_dragSourceAction(DragSourceActionNone)
+ , m_didInitiateDrag(false)
+ , m_isHandlingDrag(false)
+ , m_dragOperation(DragOperationNone)
+{
+}
+
+DragController::~DragController()
+{
+ m_client->dragControllerDestroyed();
+}
+
+static PassRefPtr<DocumentFragment> documentFragmentFromDragData(DragData* dragData, RefPtr<Range> context,
+ bool allowPlainText, bool& chosePlainText)
+{
+ ASSERT(dragData);
+ chosePlainText = false;
+
+ Document* document = context->ownerDocument();
+ ASSERT(document);
+ if (document && dragData->containsCompatibleContent()) {
+ if (PassRefPtr<DocumentFragment> fragment = dragData->asFragment(document))
+ return fragment;
+
+ if (dragData->containsURL()) {
+ String title;
+ String url = dragData->asURL(&title);
+ if (!url.isEmpty()) {
+ ExceptionCode ec;
+ RefPtr<HTMLAnchorElement> anchor = static_cast<HTMLAnchorElement*>(document->createElement("a", ec).get());
+ anchor->setHref(url);
+ RefPtr<Node> anchorText = document->createTextNode(title);
+ anchor->appendChild(anchorText, ec);
+ RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
+ fragment->appendChild(anchor, ec);
+ return fragment.get();
+ }
+ }
+ }
+ if (allowPlainText && dragData->containsPlainText()) {
+ chosePlainText = true;
+ return createFragmentFromText(context.get(), dragData->asPlainText()).get();
+ }
+
+ return 0;
+}
+
+bool DragController::dragIsMove(SelectionController* selectionController, DragData* dragData)
+{
+ return m_document == m_dragInitiator
+ && selectionController->isContentEditable()
+ && !isCopyKeyDown();
+}
+
+void DragController::cancelDrag()
+{
+ m_page->dragCaretController()->clear();
+}
+
+void DragController::dragEnded()
+{
+ m_dragInitiator = 0;
+ m_didInitiateDrag = false;
+ m_page->dragCaretController()->clear();
+}
+
+DragOperation DragController::dragEntered(DragData* dragData)
+{
+ return dragEnteredOrUpdated(dragData);
+}
+
+void DragController::dragExited(DragData* dragData)
+{
+ ASSERT(dragData);
+ Frame* mainFrame = m_page->mainFrame();
+
+ if (RefPtr<FrameView> v = mainFrame->view()) {
+ ClipboardAccessPolicy policy = mainFrame->loader()->baseURL().isLocalFile() ? ClipboardReadable : ClipboardTypesReadable;
+ RefPtr<Clipboard> clipboard = dragData->createClipboard(policy);
+ clipboard->setSourceOperation(dragData->draggingSourceOperationMask());
+ mainFrame->eventHandler()->cancelDragAndDrop(createMouseEvent(dragData), clipboard.get());
+ clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security
+ }
+
+ cancelDrag();
+ m_document = 0;
+}
+
+DragOperation DragController::dragUpdated(DragData* dragData)
+{
+ return dragEnteredOrUpdated(dragData);
+}
+
+bool DragController::performDrag(DragData* dragData)
+{
+ ASSERT(dragData);
+ m_document = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
+ if (m_isHandlingDrag) {
+ ASSERT(m_dragDestinationAction & DragDestinationActionDHTML);
+ m_client->willPerformDragDestinationAction(DragDestinationActionDHTML, dragData);
+ RefPtr<Frame> mainFrame = m_page->mainFrame();
+ if (mainFrame->view()) {
+ // Sending an event can result in the destruction of the view and part.
+ RefPtr<Clipboard> clipboard = dragData->createClipboard(ClipboardReadable);
+ clipboard->setSourceOperation(dragData->draggingSourceOperationMask());
+ mainFrame->eventHandler()->performDragAndDrop(createMouseEvent(dragData), clipboard.get());
+ clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security
+ }
+ m_document = 0;
+ return true;
+ }
+
+ if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeDrag(dragData, m_dragDestinationAction)) {
+ m_document = 0;
+ return true;
+ }
+
+ m_document = 0;
+
+ if (operationForLoad(dragData) == DragOperationNone)
+ return false;
+
+ m_client->willPerformDragDestinationAction(DragDestinationActionLoad, dragData);
+ m_page->mainFrame()->loader()->load(ResourceRequest(dragData->asURL()));
+ return true;
+}
+
+DragOperation DragController::dragEnteredOrUpdated(DragData* dragData)
+{
+ ASSERT(dragData);
+ IntPoint windowPoint = dragData->clientPosition();
+
+ Document* newDraggingDoc = 0;
+ if (Frame* frame = m_page->mainFrame())
+ newDraggingDoc = frame->documentAtPoint(windowPoint);
+ if (m_document != newDraggingDoc) {
+ if (m_document)
+ cancelDrag();
+ m_document = newDraggingDoc;
+ }
+
+ m_dragDestinationAction = m_client->actionMaskForDrag(dragData);
+
+ DragOperation operation = DragOperationNone;
+
+ if (m_dragDestinationAction == DragDestinationActionNone)
+ cancelDrag();
+ else {
+ operation = tryDocumentDrag(dragData, m_dragDestinationAction);
+ if (operation == DragOperationNone && (m_dragDestinationAction & DragDestinationActionLoad))
+ return operationForLoad(dragData);
+ }
+
+ return operation;
+}
+
+static HTMLInputElement* asFileInput(Node* node)
+{
+ ASSERT(node);
+
+ // The button for a FILE input is a sub element with no set input type
+ // In order to get around this problem we assume any non-FILE input element
+ // is this internal button, and try querying the shadow parent node.
+ if (node->hasTagName(HTMLNames::inputTag) && node->isShadowNode() && static_cast<HTMLInputElement*>(node)->inputType() != HTMLInputElement::FILE)
+ node = node->shadowParentNode();
+
+ if (!node || !node->hasTagName(HTMLNames::inputTag))
+ return 0;
+
+ HTMLInputElement* inputElem = static_cast<HTMLInputElement*>(node);
+ if (inputElem->inputType() == HTMLInputElement::FILE)
+ return inputElem;
+
+ return 0;
+}
+
+DragOperation DragController::tryDocumentDrag(DragData* dragData, DragDestinationAction actionMask)
+{
+ ASSERT(dragData);
+
+ if (!m_document)
+ return DragOperationNone;
+
+ DragOperation operation = DragOperationNone;
+ if (actionMask & DragDestinationActionDHTML)
+ operation = tryDHTMLDrag(dragData);
+ m_isHandlingDrag = operation != DragOperationNone;
+
+ RefPtr<FrameView> frameView = m_document->view();
+ if (!frameView)
+ return operation;
+
+ if ((actionMask & DragDestinationActionEdit) && !m_isHandlingDrag && canProcessDrag(dragData)) {
+ if (dragData->containsColor())
+ return DragOperationGeneric;
+
+ IntPoint dragPos = dragData->clientPosition();
+ IntPoint point = frameView->windowToContents(dragPos);
+ Element* element = m_document->elementFromPoint(point.x(), point.y());
+ ASSERT(element);
+ Frame* innerFrame = element->document()->frame();
+ ASSERT(innerFrame);
+ if (!asFileInput(element)) {
+ Selection dragCaret;
+ if (Frame* frame = m_document->frame())
+ dragCaret = frame->visiblePositionForPoint(point);
+ m_page->dragCaretController()->setSelection(dragCaret);
+ }
+
+ return dragIsMove(innerFrame->selectionController(), dragData) ? DragOperationMove : DragOperationCopy;
+ }
+
+ m_page->dragCaretController()->clear();
+ return operation;
+}
+
+DragSourceAction DragController::delegateDragSourceAction(const IntPoint& windowPoint)
+{
+ m_dragSourceAction = m_client->dragSourceActionMaskForPoint(windowPoint);
+ return m_dragSourceAction;
+}
+
+DragOperation DragController::operationForLoad(DragData* dragData)
+{
+ ASSERT(dragData);
+ Document* doc = 0;
+ doc = m_page->mainFrame()->documentAtPoint(dragData->clientPosition());
+ if (doc && (m_didInitiateDrag || doc->isPluginDocument() || (doc->frame() && doc->frame()->editor()->clientIsEditable())))
+ return DragOperationNone;
+ return dragOperation(dragData);
+}
+
+static bool setSelectionToDragCaret(Frame* frame, Selection& dragCaret, RefPtr<Range>& range, const IntPoint& point)
+{
+ frame->selectionController()->setSelection(dragCaret);
+ if (frame->selectionController()->isNone()) {
+ dragCaret = frame->visiblePositionForPoint(point);
+ frame->selectionController()->setSelection(dragCaret);
+ range = dragCaret.toRange();
+ }
+ return !frame->selectionController()->isNone() && frame->selectionController()->isContentEditable();
+}
+
+bool DragController::concludeDrag(DragData* dragData, DragDestinationAction actionMask)
+{
+ ASSERT(dragData);
+ ASSERT(!m_isHandlingDrag);
+ ASSERT(actionMask & DragDestinationActionEdit);
+
+ if (!m_document)
+ return false;
+
+ IntPoint point = m_document->view()->windowToContents(dragData->clientPosition());
+ Element* element = m_document->elementFromPoint(point.x(), point.y());
+ ASSERT(element);
+ Frame* innerFrame = element->ownerDocument()->frame();
+ ASSERT(innerFrame);
+
+ if (dragData->containsColor()) {
+ Color color = dragData->asColor();
+ if (!color.isValid())
+ return false;
+ if (!innerFrame)
+ return false;
+ RefPtr<Range> innerRange = innerFrame->selectionController()->toRange();
+ RefPtr<CSSStyleDeclaration> style = m_document->createCSSStyleDeclaration();
+ ExceptionCode ec;
+ style->setProperty("color", color.name(), ec);
+ if (!innerFrame->editor()->shouldApplyStyle(style.get(), innerRange.get()))
+ return false;
+ m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
+ innerFrame->editor()->applyStyle(style.get(), EditActionSetColor);
+ return true;
+ }
+
+ if (!m_page->dragController()->canProcessDrag(dragData)) {
+ m_page->dragCaretController()->clear();
+ return false;
+ }
+
+ if (HTMLInputElement* fileInput = asFileInput(element)) {
+ if (!dragData->containsFiles())
+ return false;
+
+ Vector<String> filenames;
+ dragData->asFilenames(filenames);
+ if (filenames.isEmpty())
+ return false;
+
+ // Ugly. For security none of the API's available to us to set the input value
+ // on file inputs. Even forcing a change in HTMLInputElement doesn't work as
+ // RenderFileUploadControl clears the file when doing updateFromElement()
+ RenderFileUploadControl* renderer = static_cast<RenderFileUploadControl*>(fileInput->renderer());
+
+ if (!renderer)
+ return false;
+
+ // Only take the first filename as <input type="file" /> can only accept one
+ renderer->receiveDroppedFile(filenames[0]);
+ return true;
+ }
+
+ Selection dragCaret(m_page->dragCaretController()->selection());
+ m_page->dragCaretController()->clear();
+ RefPtr<Range> range = dragCaret.toRange();
+
+ // For range to be null a WebKit client must have done something bad while
+ // manually controlling drag behaviour
+ if (!range)
+ return false;
+ DocLoader* loader = range->ownerDocument()->docLoader();
+ loader->setAllowStaleResources(true);
+ if (dragIsMove(innerFrame->selectionController(), dragData) || dragCaret.isContentRichlyEditable()) {
+ bool chosePlainText = false;
+ RefPtr<DocumentFragment> fragment = documentFragmentFromDragData(dragData, range, true, chosePlainText);
+ if (!fragment || !innerFrame->editor()->shouldInsertFragment(fragment, range, EditorInsertActionDropped)) {
+ loader->setAllowStaleResources(false);
+ return false;
+ }
+
+ m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
+ if (dragIsMove(innerFrame->selectionController(), dragData)) {
+ bool smartMove = innerFrame->selectionGranularity() == WordGranularity
+ && innerFrame->editor()->smartInsertDeleteEnabled()
+ && dragData->canSmartReplace();
+ applyCommand(new MoveSelectionCommand(fragment, dragCaret.base(), smartMove));
+ } else {
+ if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
+ applyCommand(new ReplaceSelectionCommand(m_document, fragment, true, dragData->canSmartReplace(), chosePlainText));
+ }
+ } else {
+ String text = dragData->asPlainText();
+ if (text.isEmpty() || !innerFrame->editor()->shouldInsertText(text, range.get(), EditorInsertActionDropped)) {
+ loader->setAllowStaleResources(false);
+ return false;
+ }
+
+ m_client->willPerformDragDestinationAction(DragDestinationActionEdit, dragData);
+ if (setSelectionToDragCaret(innerFrame, dragCaret, range, point))
+ applyCommand(new ReplaceSelectionCommand(m_document, createFragmentFromText(range.get(), text), true, false, true));
+ }
+ loader->setAllowStaleResources(false);
+
+ return true;
+}
+
+
+bool DragController::canProcessDrag(DragData* dragData)
+{
+ ASSERT(dragData);
+
+ if (!dragData->containsCompatibleContent())
+ return false;
+
+ IntPoint point = m_page->mainFrame()->view()->windowToContents(dragData->clientPosition());
+ HitTestResult result = HitTestResult(point);
+ if (!m_page->mainFrame()->renderer())
+ return false;
+
+ result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, true);
+
+ if (!result.innerNonSharedNode())
+ return false;
+
+ if (dragData->containsFiles() && asFileInput(result.innerNonSharedNode()))
+ return true;
+
+ if (!result.innerNonSharedNode()->isContentEditable())
+ return false;
+
+ if (m_didInitiateDrag && m_document == m_dragInitiator && result.isSelected())
+ return false;
+
+ return true;
+}
+
+DragOperation DragController::tryDHTMLDrag(DragData* dragData)
+{
+ ASSERT(dragData);
+ ASSERT(m_document);
+ DragOperation op = DragOperationNone;
+ RefPtr<Frame> frame = m_page->mainFrame();
+ RefPtr<FrameView> viewProtector = frame->view();
+ if (!viewProtector)
+ return DragOperationNone;
+
+ ClipboardAccessPolicy policy = frame->loader()->baseURL().isLocalFile() ? ClipboardReadable : ClipboardTypesReadable;
+ RefPtr<Clipboard> clipboard = dragData->createClipboard(policy);
+ DragOperation srcOp = dragData->draggingSourceOperationMask();
+ clipboard->setSourceOperation(srcOp);
+
+ PlatformMouseEvent event = createMouseEvent(dragData);
+ if (frame->eventHandler()->updateDragAndDrop(event, clipboard.get())) {
+ // *op unchanged if no source op was set
+ if (!clipboard->destinationOperation(op)) {
+ // The element accepted but they didn't pick an operation, so we pick one for them
+ // (as does WinIE).
+ if (srcOp & DragOperationCopy)
+ op = DragOperationCopy;
+ else if (srcOp & DragOperationMove || srcOp & DragOperationGeneric)
+ op = DragOperationMove;
+ else if (srcOp & DragOperationLink)
+ op = DragOperationLink;
+ else
+ op = DragOperationGeneric;
+ } else if (!(op & srcOp)) {
+ op = DragOperationNone;
+ }
+
+ clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security
+ return op;
+ }
+ return op;
+}
+
+bool DragController::mayStartDragAtEventLocation(const Frame* frame, const IntPoint& framePos)
+{
+ ASSERT(frame);
+ ASSERT(frame->settings());
+
+ if (!frame->view() || !frame->renderer())
+ return false;
+
+ HitTestResult mouseDownTarget = HitTestResult(framePos);
+
+ mouseDownTarget = frame->eventHandler()->hitTestResultAtPoint(framePos, true);
+
+ if (mouseDownTarget.image()
+ && !mouseDownTarget.absoluteImageURL().isEmpty()
+ && frame->settings()->loadsImagesAutomatically()
+ && m_dragSourceAction & DragSourceActionImage)
+ return true;
+
+ if (!mouseDownTarget.absoluteLinkURL().isEmpty()
+ && m_dragSourceAction & DragSourceActionLink
+ && mouseDownTarget.isLiveLink())
+ return true;
+
+ if (mouseDownTarget.isSelected()
+ && m_dragSourceAction & DragSourceActionSelection)
+ return true;
+
+ return false;
+
+}
+
+static CachedImage* getCachedImage(Element* element)
+{
+ ASSERT(element);
+ RenderObject* renderer = element->renderer();
+ if (!renderer || !renderer->isImage())
+ return 0;
+ RenderImage* image = static_cast<RenderImage*>(renderer);
+ return image->cachedImage();
+}
+
+static Image* getImage(Element* element)
+{
+ ASSERT(element);
+ RenderObject* renderer = element->renderer();
+ if (!renderer || !renderer->isImage())
+ return 0;
+
+ RenderImage* image = static_cast<RenderImage*>(renderer);
+ if (image->cachedImage() && !image->cachedImage()->errorOccurred())
+ return image->cachedImage()->image();
+ return 0;
+}
+
+static void prepareClipboardForImageDrag(Frame* src, Clipboard* clipboard, Element* node, const KURL& linkURL, const KURL& imageURL, const String& label)
+{
+ RefPtr<Range> range = src->document()->createRange();
+ ExceptionCode ec = 0;
+ range->selectNode(node, ec);
+ ASSERT(ec == 0);
+ src->selectionController()->setSelection(Selection(range.get(), DOWNSTREAM));
+ clipboard->declareAndWriteDragImage(node, !linkURL.isEmpty() ? linkURL : imageURL, label, src);
+}
+
+static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const IntPoint& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage)
+{
+ // dragImageOffset is the cursor position relative to the lower-left corner of the image.
+#if PLATFORM(MAC)
+ // We add in the Y dimension because we are a flipped view, so adding moves the image down.
+ const int yOffset = dragImageOffset.y();
+#else
+ const int yOffset = -dragImageOffset.y();
+#endif
+
+ if (isLinkImage)
+ return IntPoint(mouseDraggedPoint.x() - dragImageOffset.x(), mouseDraggedPoint.y() + yOffset);
+
+ return IntPoint(dragOrigin.x() - dragImageOffset.x(), dragOrigin.y() + yOffset);
+}
+
+static IntPoint dragLocForSelectionDrag(Frame* src)
+{
+ IntRect draggingRect = enclosingIntRect(src->selectionRect());
+ int xpos = draggingRect.right();
+ xpos = draggingRect.x() < xpos ? draggingRect.x() : xpos;
+ int ypos = draggingRect.bottom();
+#if PLATFORM(MAC)
+ // Deal with flipped coordinates on Mac
+ ypos = draggingRect.y() > ypos ? draggingRect.y() : ypos;
+#else
+ ypos = draggingRect.y() < ypos ? draggingRect.y() : ypos;
+#endif
+ return IntPoint(xpos, ypos);
+}
+
+bool DragController::startDrag(Frame* src, Clipboard* clipboard, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, bool isDHTMLDrag)
+{
+ ASSERT(src);
+ ASSERT(clipboard);
+
+ if (!src->view() || !src->renderer())
+ return false;
+
+ HitTestResult dragSource = HitTestResult(dragOrigin);
+ dragSource = src->eventHandler()->hitTestResultAtPoint(dragOrigin, true);
+ KURL linkURL = dragSource.absoluteLinkURL();
+ KURL imageURL = dragSource.absoluteImageURL();
+ bool isSelected = dragSource.isSelected();
+
+ IntPoint mouseDraggedPoint = src->view()->windowToContents(dragEvent.pos());
+
+ m_draggingImageURL = KURL();
+ m_dragOperation = srcOp;
+
+ DragImageRef dragImage = 0;
+ IntPoint dragLoc(0, 0);
+ IntPoint dragImageOffset(0, 0);
+
+ if (isDHTMLDrag)
+ dragImage = clipboard->createDragImage(dragImageOffset);
+
+ // We allow DHTML/JS to set the drag image, even if its a link, image or text we're dragging.
+ // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp.
+ if (dragImage) {
+ dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOffset, !linkURL.isEmpty());
+ m_dragOffset = dragImageOffset;
+ }
+
+ bool startedDrag = true; // optimism - we almost always manage to start the drag
+
+ Node* node = dragSource.innerNonSharedNode();
+
+ if (!imageURL.isEmpty() && node && node->isElementNode()
+ && getImage(static_cast<Element*>(node))
+ && (m_dragSourceAction & DragSourceActionImage)) {
+ Element* element = static_cast<Element*>(node);
+ if (!clipboard->hasData()) {
+ m_draggingImageURL = imageURL;
+ prepareClipboardForImageDrag(src, clipboard, element, linkURL, imageURL, dragSource.altDisplayString());
+ }
+
+ m_client->willPerformDragSourceAction(DragSourceActionImage, dragOrigin, clipboard);
+
+ if (!dragImage) {
+ IntRect imageRect = dragSource.imageRect();
+ imageRect.setLocation(m_page->mainFrame()->view()->windowToContents(src->view()->contentsToWindow(imageRect.location())));
+ doImageDrag(element, dragOrigin, dragSource.imageRect(), clipboard, src, m_dragOffset);
+ } else
+ // DHTML defined drag image
+ doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
+
+ } else if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)) {
+ if (!clipboard->hasData())
+ // 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);
+
+ m_client->willPerformDragSourceAction(DragSourceActionLink, dragOrigin, clipboard);
+ if (!dragImage) {
+ dragImage = m_client->createDragImageForLink(linkURL, dragSource.textContent(), src);
+ IntSize size = dragImageSize(dragImage);
+ m_dragOffset = IntPoint(-size.width() / 2, -LinkDragBorderInset);
+ dragLoc = IntPoint(mouseDraggedPoint.x() + m_dragOffset.x(), mouseDraggedPoint.y() + m_dragOffset.y());
+ }
+ doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, clipboard, src, true);
+ } else if (isSelected && (m_dragSourceAction & DragSourceActionSelection)) {
+ RefPtr<Range> selectionRange = src->selectionController()->toRange();
+ ASSERT(selectionRange);
+ if (!clipboard->hasData())
+ clipboard->writeRange(selectionRange.get(), src);
+ m_client->willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, clipboard);
+ if (!dragImage) {
+ dragImage = createDragImageForSelection(src);
+ dragLoc = dragLocForSelectionDrag(src);
+ m_dragOffset = IntPoint((int)(dragOrigin.x() - dragLoc.x()), (int)(dragOrigin.y() - dragLoc.y()));
+ }
+ doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
+ } else if (isDHTMLDrag) {
+ ASSERT(m_dragSourceAction & DragSourceActionDHTML);
+ m_client->willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, clipboard);
+ doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false);
+ } else {
+ // Only way I know to get here is if to get here is if the original element clicked on in the mousedown is no longer
+ // under the mousedown point, so linkURL, imageURL and isSelected are all false/empty.
+ startedDrag = false;
+ }
+
+ if (dragImage)
+ deleteDragImage(dragImage);
+ return startedDrag;
+}
+
+void DragController::doImageDrag(Element* element, const IntPoint& dragOrigin, const IntRect& rect, Clipboard* clipboard, Frame* frame, IntPoint& dragImageOffset)
+{
+ IntPoint mouseDownPoint = dragOrigin;
+ DragImageRef dragImage;
+ IntPoint origin;
+
+ Image* image = getImage(element);
+ if (image && image->size().height() * image->size().width() <= MaxOriginalImageArea
+ && (dragImage = createDragImageFromImage(image))) {
+ IntSize originalSize = rect.size();
+ origin = rect.location();
+
+ dragImage = fitDragImageToMaxSize(dragImage, rect.size(), maxDragImageSize());
+ dragImage = dissolveDragImageToFraction(dragImage, DragImageAlpha);
+ IntSize newSize = dragImageSize(dragImage);
+
+ // Properly orient the drag image and orient it differently if it's smaller than the original
+ float scale = newSize.width() / (float)originalSize.width();
+ float dx = origin.x() - mouseDownPoint.x();
+ dx *= scale;
+ origin.setX((int)(dx + 0.5));
+#if PLATFORM(MAC)
+ //Compensate for accursed flipped coordinates in cocoa
+ origin.setY(origin.y() + originalSize.height());
+#endif
+ float dy = origin.y() - mouseDownPoint.y();
+ dy *= scale;
+ origin.setY((int)(dy + 0.5));
+ } else {
+ dragImage = createDragImageIconForCachedImage(getCachedImage(element));
+ if (dragImage)
+ origin = IntPoint(DragIconRightInset - dragImageSize(dragImage).width(), DragIconBottomInset);
+ }
+
+ dragImageOffset.setX(mouseDownPoint.x() + origin.x());
+ dragImageOffset.setY(mouseDownPoint.y() + origin.y());
+ doSystemDrag(dragImage, dragImageOffset, dragOrigin, clipboard, frame, false);
+
+ deleteDragImage(dragImage);
+}
+
+void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, const IntPoint& eventPos, Clipboard* clipboard, Frame* frame, bool forLink)
+{
+ m_didInitiateDrag = true;
+ m_dragInitiator = frame->document();
+ // Protect this frame and view, as a load may occur mid drag and attempt to unload this frame
+ RefPtr<Frame> frameProtector = m_page->mainFrame();
+ RefPtr<FrameView> viewProtector = frameProtector->view();
+ m_client->startDrag(image, viewProtector->windowToContents(frame->view()->contentsToWindow(dragLoc)),
+ viewProtector->windowToContents(frame->view()->contentsToWindow(eventPos)), clipboard, frameProtector.get(), forLink);
+
+ // Drag has ended, dragEnded *should* have been called, however it is possible
+ // for the UIDelegate to take over the drag, and fail to send the appropriate
+ // drag termination event. As dragEnded just resets drag variables, we just
+ // call it anyway to be on the safe side
+ dragEnded();
+}
+
+// Manual drag caret manipulation
+void DragController::placeDragCaret(const IntPoint& windowPoint)
+{
+ Frame* mainFrame = m_page->mainFrame();
+ Document* newDraggingDoc = mainFrame->documentAtPoint(windowPoint);
+ if (m_document != newDraggingDoc) {
+ if (m_document)
+ cancelDrag();
+ m_document = newDraggingDoc;
+ }
+ if (!m_document)
+ return;
+ Frame* frame = m_document->frame();
+ ASSERT(frame);
+ FrameView* frameView = frame->view();
+ if (!frameView)
+ return;
+ IntPoint framePoint = frameView->windowToContents(windowPoint);
+ Selection dragCaret(frame->visiblePositionForPoint(framePoint));
+ m_page->dragCaretController()->setSelection(dragCaret);
+}
+
+}
diff --git a/WebCore/page/DragController.h b/WebCore/page/DragController.h
new file mode 100644
index 0000000..efa8292
--- /dev/null
+++ b/WebCore/page/DragController.h
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ *
+ * 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 DragController_h
+#define DragController_h
+
+#include "DragActions.h"
+#include "DragImage.h"
+#include "IntPoint.h"
+#include "IntRect.h"
+#include "KURL.h"
+
+namespace WebCore {
+
+ class Clipboard;
+ class Document;
+ class DragClient;
+ class DragData;
+ class Element;
+ class Frame;
+ class Image;
+ class Node;
+ class Page;
+ class PlatformMouseEvent;
+ class Range;
+ class SelectionController;
+
+ class DragController {
+ public:
+ DragController(Page*, DragClient*);
+ ~DragController();
+ DragClient* client() const { return m_client; }
+
+ DragOperation dragEntered(DragData*);
+ void dragExited(DragData*);
+ DragOperation dragUpdated(DragData*);
+ bool performDrag(DragData*);
+
+ //FIXME: It should be possible to remove a number of these accessors once all
+ //drag logic is in WebCore
+ void setDidInitiateDrag(bool initiated) { m_didInitiateDrag = initiated; }
+ bool didInitiateDrag() const { return m_didInitiateDrag; }
+ void setIsHandlingDrag(bool handling) { m_isHandlingDrag = handling; }
+ bool isHandlingDrag() const { return m_isHandlingDrag; }
+ void setDragOperation(DragOperation dragOp) { m_dragOperation = dragOp; }
+ DragOperation dragOperation() const { return m_dragOperation; }
+ void setDraggingImageURL(const KURL& url) { m_draggingImageURL = url; }
+ const KURL& draggingImageURL() const { return m_draggingImageURL; }
+ void setDragInitiator(Document* initiator) { m_dragInitiator = initiator; m_didInitiateDrag = true; }
+ Document* dragInitiator() const { return m_dragInitiator; }
+ void setDragOffset(const IntPoint& offset) { m_dragOffset = offset; }
+ const IntPoint& dragOffset() const { return m_dragOffset; }
+ DragSourceAction dragSourceAction() const { return m_dragSourceAction; }
+
+ Document* document() const { return m_document; }
+ DragDestinationAction dragDestinationAction() const { return m_dragDestinationAction; }
+ DragSourceAction delegateDragSourceAction(const IntPoint& pagePoint);
+
+ bool mayStartDragAtEventLocation(const Frame*, const IntPoint& framePos);
+ void dragEnded();
+
+ void placeDragCaret(const IntPoint&);
+
+ bool startDrag(Frame* src, Clipboard*, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin, bool isDHTMLDrag);
+ static const IntSize& maxDragImageSize();
+
+ static const int LinkDragBorderInset;
+ static const int MaxOriginalImageArea;
+ static const int DragIconRightInset;
+ static const int DragIconBottomInset;
+ static const float DragImageAlpha;
+ private:
+ bool canProcessDrag(DragData*);
+ bool concludeDrag(DragData*, DragDestinationAction);
+ DragOperation dragEnteredOrUpdated(DragData*);
+ DragOperation operationForLoad(DragData*);
+ DragOperation tryDocumentDrag(DragData*, DragDestinationAction);
+ DragOperation tryDHTMLDrag(DragData*);
+ DragOperation dragOperation(DragData*);
+ void cancelDrag();
+ bool dragIsMove(SelectionController*, DragData*);
+ bool isCopyKeyDown();
+
+ IntRect selectionDraggingRect(Frame*);
+ bool doDrag(Frame* src, Clipboard* clipboard, DragImageRef dragImage, const KURL& linkURL, const KURL& imageURL, Node* node, IntPoint& dragLoc, IntPoint& dragImageOffset);
+ void doImageDrag(Element*, const IntPoint&, const IntRect&, Clipboard*, Frame*, IntPoint&);
+ void doSystemDrag(DragImageRef, const IntPoint&, const IntPoint&, Clipboard*, Frame*, bool forLink);
+ Page* m_page;
+ DragClient* m_client;
+
+ //The Document the mouse was last dragged over
+ Document* m_document;
+
+ //The Document (if any) that initiated the drag
+ Document* m_dragInitiator;
+
+ DragDestinationAction m_dragDestinationAction;
+ DragSourceAction m_dragSourceAction;
+ bool m_didInitiateDrag;
+ bool m_isHandlingDrag;
+ DragOperation m_dragOperation;
+ IntPoint m_dragOffset;
+ KURL m_draggingImageURL;
+
+ };
+
+}
+
+#endif
diff --git a/WebCore/page/EditorClient.h b/WebCore/page/EditorClient.h
new file mode 100644
index 0000000..802aa4f
--- /dev/null
+++ b/WebCore/page/EditorClient.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Trolltech ASA
+ *
+ * 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(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)
+ // FIXME: This should become SelectionController::toWebArchive()
+ virtual NSData* dataForArchivedSelection(Frame*) = 0;
+
+ 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
new file mode 100644
index 0000000..adb4086
--- /dev/null
+++ b/WebCore/page/EventHandler.cpp
@@ -0,0 +1,1866 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ *
+ * 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 "EventHandler.h"
+
+#include "CachedImage.h"
+#include "ChromeClient.h"
+#include "Cursor.h"
+#include "Document.h"
+#include "DragController.h"
+#include "Editor.h"
+#include "EventNames.h"
+#include "FloatPoint.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "HTMLFrameSetElement.h"
+#include "HTMLFrameElementBase.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "Image.h"
+#include "KeyboardEvent.h"
+#include "MouseEvent.h"
+#include "MouseEventWithHitTestResults.h"
+#include "Page.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformScrollBar.h"
+#include "PlatformWheelEvent.h"
+#include "RenderWidget.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "TextEvent.h"
+
+#if ENABLE(SVG)
+#include "SVGDocument.h"
+#include "SVGNames.h"
+#endif
+
+namespace WebCore {
+
+using namespace EventNames;
+using namespace HTMLNames;
+
+// The link drag hysteresis is much larger than the others because there
+// needs to be enough space to cancel the link press without starting a link drag,
+// and because dragging links is rare.
+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;
+const int CompositionEventKeyCode = 229;
+
+#if ENABLE(SVG)
+using namespace SVGNames;
+#endif
+
+const double autoscrollInterval = 0.1;
+
+static Frame* subframeForTargetNode(Node* node);
+
+EventHandler::EventHandler(Frame* frame)
+ : m_frame(frame)
+ , m_mousePressed(false)
+ , m_mouseDownMayStartSelect(false)
+ , m_mouseDownMayStartDrag(false)
+ , m_mouseDownWasSingleClickInSelection(false)
+ , m_beganSelectingText(false)
+ , m_hoverTimer(this, &EventHandler::hoverTimerFired)
+ , m_autoscrollTimer(this, &EventHandler::autoscrollTimerFired)
+ , m_autoscrollRenderer(0)
+ , m_mouseDownMayStartAutoscroll(false)
+ , m_mouseDownWasInSubframe(false)
+#if ENABLE(SVG)
+ , m_svgPan(false)
+#endif
+ , m_resizeLayer(0)
+ , m_capturingMouseEventsNode(0)
+ , m_clickCount(0)
+ , m_mouseDownTimestamp(0)
+#if PLATFORM(MAC)
+ , m_mouseDownView(nil)
+ , m_sendingEventToSubview(false)
+ , m_activationEventNumber(0)
+#endif
+{
+}
+
+EventHandler::~EventHandler()
+{
+}
+
+EventHandler::EventHandlerDragState& EventHandler::dragState()
+{
+ static EventHandlerDragState state;
+ return state;
+}
+
+void EventHandler::clear()
+{
+ m_hoverTimer.stop();
+ m_resizeLayer = 0;
+ m_nodeUnderMouse = 0;
+ m_lastNodeUnderMouse = 0;
+ m_lastMouseMoveEventSubframe = 0;
+ m_lastScrollbarUnderMouse = 0;
+ m_clickCount = 0;
+ m_clickNode = 0;
+ m_frameSetBeingResized = 0;
+ m_dragTarget = 0;
+ m_currentMousePosition = IntPoint();
+ m_mousePressNode = 0;
+ m_mousePressed = false;
+ m_capturingMouseEventsNode = 0;
+}
+
+void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
+{
+ Node* innerNode = result.targetNode();
+ Selection newSelection;
+
+ if (innerNode && innerNode->renderer() && m_mouseDownMayStartSelect) {
+ VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
+ if (pos.isNotNull()) {
+ newSelection = Selection(pos);
+ newSelection.expandUsingGranularity(WordGranularity);
+ }
+
+ if (newSelection.isRange()) {
+ m_frame->setSelectionGranularity(WordGranularity);
+ m_beganSelectingText = true;
+ }
+
+ if (m_frame->shouldChangeSelection(newSelection))
+ m_frame->selectionController()->setSelection(newSelection);
+ }
+}
+
+void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
+{
+ if (!result.hitTestResult().isLiveLink())
+ return selectClosestWordFromMouseEvent(result);
+
+ Node* innerNode = result.targetNode();
+
+ if (innerNode && innerNode->renderer() && m_mouseDownMayStartSelect) {
+ Selection newSelection;
+ Element* URLElement = result.hitTestResult().URLElement();
+ VisiblePosition pos(innerNode->renderer()->positionForPoint(result.localPoint()));
+ if (pos.isNotNull() && pos.deepEquivalent().node()->isDescendantOf(URLElement))
+ newSelection = Selection::selectionFromContentsOfNode(URLElement);
+
+ if (newSelection.isRange()) {
+ m_frame->setSelectionGranularity(WordGranularity);
+ m_beganSelectingText = true;
+ }
+
+ if (m_frame->shouldChangeSelection(newSelection))
+ m_frame->selectionController()->setSelection(newSelection);
+ }
+}
+
+bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
+{
+ if (event.event().button() != LeftButton)
+ return false;
+
+ if (m_frame->selectionController()->isRange())
+ // A double-click when range is already selected
+ // should not change the selection. So, do not call
+ // selectClosestWordFromMouseEvent, but do set
+ // m_beganSelectingText to prevent handleMouseReleaseEvent
+ // from setting caret selection.
+ m_beganSelectingText = true;
+ else
+ selectClosestWordFromMouseEvent(event);
+
+ return true;
+}
+
+bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event)
+{
+ if (event.event().button() != LeftButton)
+ return false;
+
+ Node* innerNode = event.targetNode();
+ if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect))
+ return false;
+
+ Selection newSelection;
+ VisiblePosition pos(innerNode->renderer()->positionForPoint(event.localPoint()));
+ if (pos.isNotNull()) {
+ newSelection = Selection(pos);
+ newSelection.expandUsingGranularity(ParagraphGranularity);
+ }
+ if (newSelection.isRange()) {
+ m_frame->setSelectionGranularity(ParagraphGranularity);
+ m_beganSelectingText = true;
+ }
+
+ if (m_frame->shouldChangeSelection(newSelection))
+ m_frame->selectionController()->setSelection(newSelection);
+
+ return true;
+}
+
+bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event)
+{
+ if (event.event().button() != LeftButton)
+ return false;
+
+ Node* innerNode = event.targetNode();
+ if (!(innerNode && innerNode->renderer() && m_mouseDownMayStartSelect))
+ return false;
+
+ // Extend the selection if the Shift key is down, unless the click is in a link.
+ bool extendSelection = event.event().shiftKey() && !event.isOverLink();
+
+ // 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)) {
+ m_mouseDownWasSingleClickInSelection = true;
+ return false;
+ }
+
+ VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(event.localPoint()));
+ if (visiblePos.isNull())
+ visiblePos = VisiblePosition(innerNode, 0, DOWNSTREAM);
+ Position pos = visiblePos.deepEquivalent();
+
+ Selection newSelection = m_frame->selectionController()->selection();
+ if (extendSelection && newSelection.isCaretOrRange()) {
+ m_frame->selectionController()->setLastChangeWasHorizontalExtension(false);
+
+ // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection
+ // was created right-to-left
+ Position start = newSelection.start();
+ Position end = newSelection.end();
+ short before = Range::compareBoundaryPoints(pos.node(), pos.offset(), start.node(), start.offset());
+ if (before <= 0)
+ newSelection = Selection(pos, end);
+ else
+ newSelection = Selection(start, pos);
+
+ if (m_frame->selectionGranularity() != CharacterGranularity)
+ newSelection.expandUsingGranularity(m_frame->selectionGranularity());
+ m_beganSelectingText = true;
+ } else {
+ newSelection = Selection(visiblePos);
+ m_frame->setSelectionGranularity(CharacterGranularity);
+ }
+
+ if (m_frame->shouldChangeSelection(newSelection))
+ m_frame->selectionController()->setSelection(newSelection);
+
+ return true;
+}
+
+bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event)
+{
+ // Reset drag state.
+ dragState().m_dragSrc = 0;
+
+ bool singleClick = event.event().clickCount() <= 1;
+
+ // If we got the event back, that must mean it wasn't prevented,
+ // so it's allowed to start a drag or selection.
+ m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode());
+
+ // Careful that the drag starting logic stays in sync with eventMayStartDrag()
+ m_mouseDownMayStartDrag = singleClick;
+
+ m_mouseDownWasSingleClickInSelection = false;
+
+ if (passWidgetMouseDownEventToWidget(event))
+ return true;
+
+#if ENABLE(SVG)
+ if (m_frame->document()->isSVGDocument() &&
+ static_cast<SVGDocument*>(m_frame->document())->zoomAndPanEnabled()) {
+ if (event.event().shiftKey() && singleClick) {
+ m_svgPan = true;
+ static_cast<SVGDocument*>(m_frame->document())->startPan(event.event().pos());
+ return true;
+ }
+ }
+#endif
+
+ // We don't do this at the start of mouse down handling,
+ // because we don't want to do it until we know we didn't hit a widget.
+ if (singleClick)
+ focusDocumentView();
+
+ Node* innerNode = event.targetNode();
+
+ m_mousePressNode = innerNode;
+ m_dragStartPos = event.event().pos();
+
+ bool swallowEvent = false;
+ if (event.event().button() == LeftButton || event.event().button() == MiddleButton) {
+ m_frame->selectionController()->setCaretBlinkingSuspended(true);
+ m_mousePressed = true;
+ m_beganSelectingText = false;
+
+ if (event.event().clickCount() == 2)
+ swallowEvent = handleMousePressEventDoubleClick(event);
+ else if (event.event().clickCount() >= 3)
+ swallowEvent = handleMousePressEventTripleClick(event);
+ else
+ swallowEvent = handleMousePressEventSingleClick(event);
+ }
+
+ m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect ||
+ (m_mousePressNode && m_mousePressNode->renderer() && m_mousePressNode->renderer()->shouldAutoscroll());
+
+ return swallowEvent;
+}
+
+bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event)
+{
+ if (handleDrag(event))
+ return true;
+
+ if (!m_mousePressed)
+ return false;
+
+ Node* targetNode = event.targetNode();
+ if (event.event().button() != LeftButton || !targetNode || !targetNode->renderer())
+ return false;
+
+#if PLATFORM(MAC) // FIXME: Why does this assertion fire on other platforms?
+ ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll);
+#endif
+
+ m_mouseDownMayStartDrag = false;
+
+ if (m_mouseDownMayStartAutoscroll) {
+ // 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)
+ handleAutoscroll(renderer);
+ }
+
+ updateSelectionForMouseDrag(targetNode, event.localPoint());
+ return true;
+}
+
+bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const
+{
+ // This is a pre-flight check of whether the event might lead to a drag being started. Be careful
+ // 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()
+ || event.button() != LeftButton || event.clickCount() != 1)
+ return false;
+
+ bool DHTMLFlag;
+ bool UAFlag;
+ allowDHTMLDrag(DHTMLFlag, UAFlag);
+ if (!DHTMLFlag && !UAFlag)
+ return false;
+
+ HitTestRequest request(true, false);
+ HitTestResult result(m_frame->view()->windowToContents(event.pos()));
+ m_frame->renderer()->layer()->hitTest(request, result);
+ bool srcIsDHTML;
+ return result.innerNode() && result.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, result.point().x(), result.point().y(), srcIsDHTML);
+}
+
+void EventHandler::updateSelectionForMouseDrag()
+{
+ FrameView* view = m_frame->view();
+ if (!view)
+ return;
+ RenderObject* renderer = m_frame->renderer();
+ if (!renderer)
+ return;
+ RenderLayer* layer = renderer->layer();
+ if (!layer)
+ return;
+
+ HitTestResult result(view->windowToContents(m_currentMousePosition));
+ layer->hitTest(HitTestRequest(true, true, true), result);
+ updateSelectionForMouseDrag(result.innerNode(), result.localPoint());
+}
+
+void EventHandler::updateSelectionForMouseDrag(Node* targetNode, const IntPoint& localPoint)
+{
+ if (!m_mouseDownMayStartSelect)
+ return;
+
+ if (!targetNode)
+ return;
+
+ RenderObject* targetRenderer = targetNode->renderer();
+ if (!targetRenderer)
+ return;
+
+ if (!canMouseDragExtendSelect(targetNode))
+ return;
+
+ VisiblePosition targetPosition(targetRenderer->positionForPoint(localPoint));
+
+ // Don't modify the selection if we're not on a node.
+ if (targetPosition.isNull())
+ return;
+
+ // 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();
+
+#if ENABLE(SVG)
+ // Special case to limit selection to the containing block for SVG text.
+ // FIXME: Isn't there a better non-SVG-specific way to do this?
+ if (Node* selectionBaseNode = newSelection.base().node())
+ if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer())
+ if (selectionBaseRenderer->isSVGText())
+ if (targetNode->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
+ return;
+#endif
+
+ if (!m_beganSelectingText) {
+ m_beganSelectingText = true;
+ newSelection = Selection(targetPosition);
+ }
+
+ newSelection.setExtent(targetPosition);
+ if (m_frame->selectionGranularity() != CharacterGranularity)
+ newSelection.expandUsingGranularity(m_frame->selectionGranularity());
+
+ if (m_frame->shouldChangeSelection(newSelection)) {
+ m_frame->selectionController()->setLastChangeWasHorizontalExtension(false);
+ m_frame->selectionController()->setSelection(newSelection);
+ }
+}
+
+bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event)
+{
+ if (eventLoopHandleMouseUp(event))
+ return true;
+
+ // If this was the first click in the window, we don't even want to clear the selection.
+ // This case occurs when the user clicks on a draggable element, since we have to process
+ // the mouse down and drag events to see if we might start a drag. For other first clicks
+ // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
+ // ignored upstream of this layer.
+ return eventActivatedView(event.event());
+}
+
+bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
+{
+ 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_mousePressed = false;
+ m_mouseDownMayStartDrag = false;
+ m_mouseDownMayStartSelect = false;
+ m_mouseDownMayStartAutoscroll = false;
+ m_mouseDownWasInSubframe = false;
+
+ bool handled = false;
+
+ // Clear the selection if the mouse didn't move after the last mouse press.
+ // We do this so when clicking on the selection, the selection goes away.
+ // However, if we are editing, place the caret.
+ if (m_mouseDownWasSingleClickInSelection && !m_beganSelectingText
+ && m_dragStartPos == event.event().pos()
+ && m_frame->selectionController()->isRange()) {
+ Selection newSelection;
+ Node *node = event.targetNode();
+ if (node && node->isContentEditable() && node->renderer()) {
+ VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint());
+ newSelection = Selection(pos);
+ }
+ if (m_frame->shouldChangeSelection(newSelection))
+ m_frame->selectionController()->setSelection(newSelection);
+
+ handled = true;
+ }
+
+ m_frame->notifyRendererOfSelectionChange(true);
+
+ m_frame->selectionController()->selectFrameElementInParentIfFullySelected();
+
+ return handled;
+}
+
+void EventHandler::handleAutoscroll(RenderObject* renderer)
+{
+ if (m_autoscrollTimer.isActive())
+ return;
+ setAutoscrollRenderer(renderer);
+ startAutoscrollTimer();
+}
+
+void EventHandler::autoscrollTimerFired(Timer<EventHandler>*)
+{
+ if (!m_mousePressed) {
+ stopAutoscrollTimer();
+ return;
+ }
+ if (RenderObject* r = autoscrollRenderer())
+ r->autoscroll();
+}
+
+RenderObject* EventHandler::autoscrollRenderer() const
+{
+ return m_autoscrollRenderer;
+}
+
+void EventHandler::setAutoscrollRenderer(RenderObject* renderer)
+{
+ m_autoscrollRenderer = renderer;
+}
+
+void EventHandler::allowDHTMLDrag(bool& flagDHTML, bool& flagUA) const
+{
+ if (!m_frame || !m_frame->document()) {
+ flagDHTML = false;
+ flagUA = false;
+ }
+
+ unsigned mask = m_frame->page()->dragController()->delegateDragSourceAction(m_frame->view()->contentsToWindow(m_mouseDownPos));
+ flagDHTML = (mask & DragSourceActionDHTML) != DragSourceActionNone;
+ flagUA = ((mask & DragSourceActionImage) || (mask & DragSourceActionLink) || (mask & DragSourceActionSelection));
+}
+
+HitTestResult EventHandler::hitTestResultAtPoint(const IntPoint& point, bool allowShadowContent)
+{
+ HitTestResult result(point);
+ if (!m_frame->renderer())
+ return result;
+ m_frame->renderer()->layer()->hitTest(HitTestRequest(true, true), result);
+
+ while (true) {
+ Node* n = result.innerNode();
+ if (!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())
+ 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());
+ HitTestResult widgetHitTestResult(widgetPoint);
+ frame->renderer()->layer()->hitTest(HitTestRequest(true, true), widgetHitTestResult);
+ result = widgetHitTestResult;
+ }
+
+ if (!allowShadowContent)
+ result.setToNonShadowAncestor();
+
+ return result;
+}
+
+
+void EventHandler::startAutoscrollTimer()
+{
+ m_autoscrollTimer.startRepeating(autoscrollInterval);
+}
+
+void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed)
+{
+ if (m_mouseDownWasInSubframe) {
+ if (Frame* subframe = subframeForTargetNode(m_mousePressNode.get()))
+ subframe->eventHandler()->stopAutoscrollTimer(rendererIsBeingDestroyed);
+ return;
+ }
+
+ if (!rendererIsBeingDestroyed && autoscrollRenderer())
+ autoscrollRenderer()->stopAutoscroll();
+ setAutoscrollRenderer(0);
+ m_autoscrollTimer.stop();
+}
+
+Node* EventHandler::mousePressNode() const
+{
+ return m_mousePressNode.get();
+}
+
+void EventHandler::setMousePressNode(PassRefPtr<Node> node)
+{
+ m_mousePressNode = node;
+}
+
+bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity)
+{
+ if (!m_frame->document())
+ return false;
+
+ Node* node = m_frame->document()->focusedNode();
+ if (!node)
+ node = m_mousePressNode.get();
+
+ if (node) {
+ RenderObject *r = node->renderer();
+ if (r && !r->isListBox())
+ return r->scroll(direction, granularity);
+ }
+
+ return false;
+}
+
+IntPoint EventHandler::currentMousePosition() const
+{
+ return m_currentMousePosition;
+}
+
+Frame* subframeForTargetNode(Node* node)
+{
+ if (!node)
+ return 0;
+
+ RenderObject* renderer = node->renderer();
+ if (!renderer || !renderer->isWidget())
+ return 0;
+
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ if (!widget || !widget->isFrameView())
+ return 0;
+
+ return static_cast<FrameView*>(widget)->frame();
+}
+
+static bool isSubmitImage(Node* node)
+{
+ return node && node->hasTagName(inputTag)
+ && static_cast<HTMLInputElement*>(node)->inputType() == HTMLInputElement::IMAGE;
+}
+
+// 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();
+}
+
+Cursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, PlatformScrollbar* 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)
+ return iBeamCursor();
+
+ Node* node = event.targetNode();
+ RenderObject* renderer = node ? node->renderer() : 0;
+ RenderStyle* style = renderer ? renderer->style() : 0;
+
+ if (style && style->cursors()) {
+ const CursorList* cursors = style->cursors();
+ for (unsigned i = 0; i < cursors->size(); ++i) {
+ CachedImage* cimage = (*cursors)[i].cursorImage;
+ IntPoint hotSpot = (*cursors)[i].hotSpot;
+ if (!cimage)
+ continue;
+ if (cimage->image()->isNull())
+ break;
+ if (!cimage->errorOccurred())
+ return Cursor(cimage->image(), hotSpot);
+ }
+ }
+
+ switch (style ? style->cursor() : CURSOR_AUTO) {
+ case CURSOR_AUTO: {
+ bool editable = (node && node->isContentEditable());
+ bool editableLinkEnabled = false;
+
+ // If the link is editable, then we need to check the settings to see whether or not the link should be followed
+ if (editable) {
+ ASSERT(m_frame->settings());
+ switch(m_frame->settings()->editableLinkBehavior()) {
+ default:
+ case EditableLinkDefaultBehavior:
+ case EditableLinkAlwaysLive:
+ editableLinkEnabled = true;
+ break;
+
+ case EditableLinkNeverLive:
+ editableLinkEnabled = false;
+ break;
+
+ case EditableLinkLiveWhenNotFocused:
+ editableLinkEnabled = nodeIsNotBeingEdited(node, m_frame) || event.event().shiftKey();
+ break;
+
+ case EditableLinkOnlyLiveWithShiftKey:
+ editableLinkEnabled = event.event().shiftKey();
+ break;
+ }
+ }
+
+ if ((event.isOverLink() || isSubmitImage(node)) && (!editable || editableLinkEnabled))
+ return handCursor();
+ RenderLayer* layer = renderer ? renderer->enclosingLayer() : 0;
+ bool inResizer = false;
+ if (m_frame->view() && layer && layer->isPointInResizeControl(m_frame->view()->windowToContents(event.event().pos())))
+ inResizer = true;
+ if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !scrollbar)
+ return iBeamCursor();
+ return pointerCursor();
+ }
+ case CURSOR_CROSS:
+ return crossCursor();
+ case CURSOR_POINTER:
+ return handCursor();
+ case CURSOR_MOVE:
+ return moveCursor();
+ case CURSOR_ALL_SCROLL:
+ return moveCursor();
+ case CURSOR_E_RESIZE:
+ return eastResizeCursor();
+ case CURSOR_W_RESIZE:
+ return westResizeCursor();
+ case CURSOR_N_RESIZE:
+ return northResizeCursor();
+ case CURSOR_S_RESIZE:
+ return southResizeCursor();
+ case CURSOR_NE_RESIZE:
+ return northEastResizeCursor();
+ case CURSOR_SW_RESIZE:
+ return southWestResizeCursor();
+ case CURSOR_NW_RESIZE:
+ return northWestResizeCursor();
+ case CURSOR_SE_RESIZE:
+ return southEastResizeCursor();
+ case CURSOR_NS_RESIZE:
+ return northSouthResizeCursor();
+ case CURSOR_EW_RESIZE:
+ return eastWestResizeCursor();
+ case CURSOR_NESW_RESIZE:
+ return northEastSouthWestResizeCursor();
+ case CURSOR_NWSE_RESIZE:
+ return northWestSouthEastResizeCursor();
+ case CURSOR_COL_RESIZE:
+ return columnResizeCursor();
+ case CURSOR_ROW_RESIZE:
+ return rowResizeCursor();
+ case CURSOR_TEXT:
+ return iBeamCursor();
+ case CURSOR_WAIT:
+ return waitCursor();
+ case CURSOR_HELP:
+ return helpCursor();
+ case CURSOR_VERTICAL_TEXT:
+ return verticalTextCursor();
+ case CURSOR_CELL:
+ return cellCursor();
+ case CURSOR_CONTEXT_MENU:
+ return contextMenuCursor();
+ case CURSOR_PROGRESS:
+ return progressCursor();
+ case CURSOR_NO_DROP:
+ return noDropCursor();
+ case CURSOR_ALIAS:
+ return aliasCursor();
+ case CURSOR_COPY:
+ return copyCursor();
+ case CURSOR_NONE:
+ return noneCursor();
+ case CURSOR_NOT_ALLOWED:
+ return notAllowedCursor();
+ case CURSOR_DEFAULT:
+ return pointerCursor();
+ case CURSOR_WEBKIT_ZOOM_IN:
+ return zoomInCursor();
+ case CURSOR_WEBKIT_ZOOM_OUT:
+ return zoomOutCursor();
+ }
+ return pointerCursor();
+}
+
+bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent)
+{
+ if (!m_frame->document())
+ return false;
+
+ RefPtr<FrameView> protector(m_frame->view());
+
+ m_mousePressed = true;
+ m_currentMousePosition = mouseEvent.pos();
+ m_mouseDownTimestamp = mouseEvent.timestamp();
+ m_mouseDownMayStartDrag = false;
+ m_mouseDownMayStartSelect = false;
+ m_mouseDownMayStartAutoscroll = false;
+ m_mouseDownPos = m_frame->view()->windowToContents(mouseEvent.pos());
+ m_mouseDownWasInSubframe = false;
+
+ MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, true), mouseEvent);
+
+ if (!mev.targetNode()) {
+ invalidateClick();
+ return false;
+ }
+
+ m_mousePressNode = mev.targetNode();
+
+ Frame* subframe = subframeForTargetNode(mev.targetNode());
+ 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.
+ if (m_mousePressed)
+ m_capturingMouseEventsNode = mev.targetNode();
+ invalidateClick();
+ return true;
+ }
+
+ m_clickCount = mouseEvent.clickCount();
+ m_clickNode = mev.targetNode();
+
+ RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0;
+ IntPoint p = m_frame->view()->windowToContents(mouseEvent.pos());
+ if (layer && layer->isPointInResizeControl(p)) {
+ layer->setInResizeMode(true);
+ m_resizeLayer = layer;
+ m_offsetFromResizeCorner = layer->offsetFromResizeCorner(p);
+ invalidateClick();
+ return true;
+ }
+
+ bool swallowEvent = dispatchMouseEvent(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.
+ if (mev.scrollbar()) {
+ const bool wasLastScrollBar = mev.scrollbar() == m_lastScrollbarUnderMouse.get();
+ mev = prepareMouseEvent(HitTestRequest(true, true), mouseEvent);
+
+ if (wasLastScrollBar && mev.scrollbar() != m_lastScrollbarUnderMouse.get())
+ m_lastScrollbarUnderMouse = 0;
+ }
+
+ if (swallowEvent) {
+ // scrollbars should get events anyway, even disabled controls might be scrollable
+ if (mev.scrollbar())
+ passMousePressEventToScrollbar(mev, mev.scrollbar());
+ } else {
+ // Refetch the event target node if it currently is the shadow node inside an <input> element.
+ // If a mouse event handler changes the input element type to one that has a widget associated,
+ // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the
+ // event target node can't still be the shadow node.
+ if (mev.targetNode()->isShadowNode() && mev.targetNode()->shadowParentNode()->hasTagName(inputTag))
+ mev = prepareMouseEvent(HitTestRequest(true, true), mouseEvent);
+
+ PlatformScrollbar* scrollbar = m_frame->view()->scrollbarUnderMouse(mouseEvent);
+ if (!scrollbar)
+ scrollbar = mev.scrollbar();
+ if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar))
+ swallowEvent = true;
+ else
+ swallowEvent = handleMousePressEvent(mev);
+ }
+
+ return swallowEvent;
+}
+
+// This method only exists for platforms that don't know how to deliver
+bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& mouseEvent)
+{
+ if (!m_frame->document())
+ return false;
+
+ RefPtr<FrameView> protector(m_frame->view());
+
+ // We get this instead of a second mouse-up
+ m_mousePressed = false;
+ m_currentMousePosition = mouseEvent.pos();
+
+ MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, true), mouseEvent);
+ Frame* subframe = subframeForTargetNode(mev.targetNode());
+ 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 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);
+
+ bool swallowMouseReleaseEvent = false;
+ if (!swallowMouseUpEvent)
+ swallowMouseReleaseEvent = handleMouseReleaseEvent(mev);
+
+ invalidateClick();
+
+ return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
+}
+
+bool EventHandler::mouseMoved(const PlatformMouseEvent& event)
+{
+ HitTestResult hoveredNode = HitTestResult(IntPoint());
+ bool result = handleMouseMoveEvent(event, &hoveredNode);
+
+ Page* page = m_frame->page();
+ if (!page)
+ return result;
+
+ hoveredNode.setToNonShadowAncestor();
+ page->chrome()->mouseDidMoveOverElement(hoveredNode, event.modifierFlags());
+ page->chrome()->setToolTip(hoveredNode);
+ return result;
+}
+
+bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, HitTestResult* hoveredNode)
+{
+ // in Radar 3703768 we saw frequent crashes apparently due to the
+ // part being null here, which seems impossible, so check for nil
+ // but also assert so that we can try to figure this out in debug
+ // builds, if it happens.
+ ASSERT(m_frame);
+ if (!m_frame || !m_frame->document())
+ return false;
+
+ RefPtr<FrameView> protector(m_frame->view());
+ m_currentMousePosition = mouseEvent.pos();
+
+ if (m_hoverTimer.isActive())
+ m_hoverTimer.stop();
+
+#if ENABLE(SVG)
+ if (m_svgPan) {
+ static_cast<SVGDocument*>(m_frame->document())->updatePan(m_currentMousePosition);
+ return true;
+ }
+#endif
+
+ if (m_frameSetBeingResized)
+ return dispatchMouseEvent(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);
+
+ // Treat mouse move events while the mouse is pressed as "read-only" in prepareMouseEvent
+ // if we are allowed to select.
+ // This means that :hover and :active freeze in the state they were in when the mouse
+ // was pressed, rather than updating for nodes the mouse moves over as you hold the mouse down.
+ HitTestRequest request(m_mousePressed && m_mouseDownMayStartSelect, m_mousePressed, true);
+ MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent);
+ if (hoveredNode)
+ *hoveredNode = mev.hitTestResult();
+
+ PlatformScrollbar* scrollbar = 0;
+
+ if (m_resizeLayer && m_resizeLayer->inResizeMode())
+ m_resizeLayer->resize(mouseEvent, m_offsetFromResizeCorner);
+ else {
+ if (m_frame->view())
+ scrollbar = m_frame->view()->scrollbarUnderMouse(mouseEvent);
+
+ if (!scrollbar)
+ scrollbar = mev.scrollbar();
+
+ if (m_lastScrollbarUnderMouse != scrollbar) {
+ // Send mouse exited to the old scrollbar.
+ if (m_lastScrollbarUnderMouse)
+ m_lastScrollbarUnderMouse->handleMouseOutEvent(mouseEvent);
+ m_lastScrollbarUnderMouse = m_mousePressed ? 0 : scrollbar;
+ }
+ }
+
+ bool swallowEvent = false;
+ Node* targetNode = m_capturingMouseEventsNode ? m_capturingMouseEventsNode.get() : mev.targetNode();
+ RefPtr<Frame> newSubframe = subframeForTargetNode(targetNode);
+
+ // 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());
+
+ if (newSubframe) {
+ // Update over/out state before passing the event to the subframe.
+ updateMouseEventTargetNode(mev.targetNode(), mouseEvent, true);
+ 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())
+ m_frame->view()->setCursor(selectCursor(mev, scrollbar));
+ }
+
+ m_lastMouseMoveEventSubframe = newSubframe;
+
+ if (swallowEvent)
+ return true;
+
+ swallowEvent = dispatchMouseEvent(mousemoveEvent, mev.targetNode(), false, 0, mouseEvent, true);
+ if (!swallowEvent)
+ swallowEvent = handleMouseDraggedEvent(mev);
+
+ return swallowEvent;
+}
+
+void EventHandler::invalidateClick()
+{
+ m_clickCount = 0;
+ m_clickNode = 0;
+}
+
+bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent)
+{
+ if (!m_frame->document())
+ return false;
+
+ RefPtr<FrameView> protector(m_frame->view());
+
+ m_mousePressed = false;
+ m_currentMousePosition = mouseEvent.pos();
+
+#if ENABLE(SVG)
+ if (m_svgPan) {
+ m_svgPan = false;
+ static_cast<SVGDocument*>(m_frame->document())->updatePan(m_currentMousePosition);
+ return true;
+ }
+#endif
+
+ if (m_frameSetBeingResized)
+ return dispatchMouseEvent(mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false);
+
+ if (m_lastScrollbarUnderMouse) {
+ invalidateClick();
+ return m_lastScrollbarUnderMouse->handleMouseReleaseEvent(mouseEvent);
+ }
+
+ MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, false, false, true), mouseEvent);
+ Node* targetNode = m_capturingMouseEventsNode.get() ? m_capturingMouseEventsNode.get() : mev.targetNode();
+ Frame* subframe = subframeForTargetNode(targetNode);
+ if (subframe && passMouseReleaseEventToSubframe(mev, subframe)) {
+ m_capturingMouseEventsNode = 0;
+ return true;
+ }
+
+ bool swallowMouseUpEvent = dispatchMouseEvent(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);
+
+ if (m_resizeLayer) {
+ m_resizeLayer->setInResizeMode(false);
+ m_resizeLayer = 0;
+ }
+
+ bool swallowMouseReleaseEvent = false;
+ if (!swallowMouseUpEvent)
+ swallowMouseReleaseEvent = handleMouseReleaseEvent(mev);
+
+ invalidateClick();
+
+ return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent;
+}
+
+bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTarget, const PlatformMouseEvent& event, Clipboard* clipboard)
+{
+ IntPoint contentsPos = m_frame->view()->windowToContents(event.pos());
+
+ RefPtr<MouseEvent> me = new MouseEvent(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);
+ return me->defaultPrevented();
+}
+
+bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
+{
+ bool accept = false;
+
+ if (!m_frame->document())
+ return false;
+
+ if (!m_frame->view())
+ return false;
+
+ MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(true, false), event);
+
+ // Drag events should never go to text nodes (following IE, and proper mouseover/out dispatch)
+ Node* newTarget = mev.targetNode();
+ if (newTarget && newTarget->isTextNode())
+ newTarget = newTarget->parentNode();
+ if (newTarget)
+ newTarget = newTarget->shadowAncestorNode();
+
+ if (m_dragTarget != newTarget) {
+ // FIXME: this ordering was explicitly chosen to match WinIE. However,
+ // it is sometimes incorrect when dragging within subframes, as seen with
+ // LayoutTests/fast/events/drag-in-frames.html.
+ if (newTarget)
+ if (newTarget->hasTagName(frameTag) || newTarget->hasTagName(iframeTag))
+ accept = static_cast<HTMLFrameElementBase*>(newTarget)->contentFrame()->eventHandler()->updateDragAndDrop(event, clipboard);
+ else
+ accept = dispatchDragEvent(dragenterEvent, newTarget, event, clipboard);
+
+ if (m_dragTarget) {
+ Frame* frame = (m_dragTarget->hasTagName(frameTag) || m_dragTarget->hasTagName(iframeTag))
+ ? static_cast<HTMLFrameElementBase*>(m_dragTarget.get())->contentFrame() : 0;
+ if (frame)
+ accept = frame->eventHandler()->updateDragAndDrop(event, clipboard);
+ else
+ dispatchDragEvent(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);
+ }
+ m_dragTarget = newTarget;
+
+ return accept;
+}
+
+void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
+{
+ if (m_dragTarget) {
+ Frame* frame = (m_dragTarget->hasTagName(frameTag) || m_dragTarget->hasTagName(iframeTag))
+ ? static_cast<HTMLFrameElementBase*>(m_dragTarget.get())->contentFrame() : 0;
+ if (frame)
+ frame->eventHandler()->cancelDragAndDrop(event, clipboard);
+ else
+ dispatchDragEvent(dragleaveEvent, m_dragTarget.get(), event, clipboard);
+ }
+ clearDragState();
+}
+
+bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard)
+{
+ bool accept = false;
+ if (m_dragTarget) {
+ Frame* frame = (m_dragTarget->hasTagName(frameTag) || m_dragTarget->hasTagName(iframeTag))
+ ? static_cast<HTMLFrameElementBase*>(m_dragTarget.get())->contentFrame() : 0;
+ if (frame)
+ accept = frame->eventHandler()->performDragAndDrop(event, clipboard);
+ else
+ accept = dispatchDragEvent(dropEvent, m_dragTarget.get(), event, clipboard);
+ }
+ clearDragState();
+ return accept;
+}
+
+void EventHandler::clearDragState()
+{
+ m_dragTarget = 0;
+ m_capturingMouseEventsNode = 0;
+#if PLATFORM(MAC)
+ m_sendingEventToSubview = false;
+#endif
+}
+
+Node* EventHandler::nodeUnderMouse() const
+{
+ return m_nodeUnderMouse.get();
+}
+
+void EventHandler::setCapturingMouseEventsNode(PassRefPtr<Node> n)
+{
+ m_capturingMouseEventsNode = n;
+}
+
+MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mev)
+{
+ ASSERT(m_frame);
+ ASSERT(m_frame->document());
+
+ IntPoint documentPoint = m_frame->view()->windowToContents(mev.pos());
+ return m_frame->document()->prepareMouseEvent(request, documentPoint, mev);
+}
+
+void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& mouseEvent, bool fireMouseOverOut)
+{
+ Node* result = targetNode;
+
+ // If we're capturing, we always go right to that node.
+ if (m_capturingMouseEventsNode)
+ result = m_capturingMouseEventsNode.get();
+ else {
+ // If the target node is a text node, dispatch on the parent node - rdar://4196646
+ if (result && result->isTextNode())
+ result = result->parentNode();
+ if (result)
+ result = result->shadowAncestorNode();
+ }
+ m_nodeUnderMouse = result;
+
+ // 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 (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());
+ // send mouseover event to the new node
+ if (m_nodeUnderMouse)
+ EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, mouseoverEvent, 0, m_lastNodeUnderMouse.get());
+ }
+ m_lastNodeUnderMouse = m_nodeUnderMouse;
+ }
+}
+
+bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool cancelable, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder)
+{
+ updateMouseEventTargetNode(targetNode, mouseEvent, setUnder);
+
+ bool swallowEvent = false;
+
+ if (m_nodeUnderMouse)
+ swallowEvent = EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, eventType, clickCount);
+
+ if (!swallowEvent && eventType == 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.
+ Node* node = m_nodeUnderMouse.get();
+ RenderObject* renderer = node ? node->renderer() : 0;
+
+ // Walk up the render tree to search for a node to focus.
+ // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields.
+ while (renderer) {
+ node = renderer->element();
+ if (node && node->isFocusable()) {
+ // To fix <rdar://problem/4895428> Can't drag selected ToDo, we don't focus a
+ // node on mouse down if it's selected and inside a focused node. It will be
+ // focused if the user does a mouseup over it, however, because the mouseup
+ // 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 &&
+ n->isDescendantOf(m_frame->document()->focusedNode()))
+ return false;
+
+ break;
+ }
+
+ renderer = renderer->parent();
+ }
+ // If focus shift is blocked, we eat the event. Note we should never clear swallowEvent
+ // if the page already set it (e.g., by canceling default behavior).
+ if (node && node->isMouseFocusable()) {
+ if (!m_frame->page()->focusController()->setFocusedNode(node, m_frame))
+ swallowEvent = true;
+ } else if (!node || !node->focused()) {
+ if (!m_frame->page()->focusController()->setFocusedNode(0, m_frame))
+ swallowEvent = true;
+ }
+ }
+
+ return swallowEvent;
+}
+
+bool EventHandler::handleWheelEvent(PlatformWheelEvent& e)
+{
+ Document* doc = m_frame->document();
+ if (!doc)
+ return false;
+
+ RenderObject* docRenderer = doc->renderer();
+ if (!docRenderer)
+ return false;
+
+ IntPoint vPoint = m_frame->view()->windowToContents(e.pos());
+
+ HitTestRequest request(true, false);
+ HitTestResult result(vPoint);
+ doc->renderer()->layer()->hitTest(request, result);
+ Node* node = result.innerNode();
+
+ if (node) {
+ // Figure out which view to send the event to.
+ RenderObject* target = node->renderer();
+
+ if (target && target->isWidget()) {
+ Widget* widget = static_cast<RenderWidget*>(target)->widget();
+
+ if (widget && passWheelEventToWidget(e, widget)) {
+ e.accept();
+ return true;
+ }
+ }
+
+ node = node->shadowAncestorNode();
+ EventTargetNodeCast(node)->dispatchWheelEvent(e);
+ if (e.isAccepted())
+ return true;
+
+ 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();
+ }
+ }
+
+ if (!e.isAccepted())
+ m_frame->view()->wheelEvent(e);
+
+ return e.isAccepted();
+}
+
+bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event)
+{
+ Document* doc = m_frame->document();
+ FrameView* v = m_frame->view();
+ if (!doc || !v)
+ return false;
+
+ bool swallowEvent;
+ IntPoint viewportPos = v->windowToContents(event.pos());
+ MouseEventWithHitTestResults mev = doc->prepareMouseEvent(HitTestRequest(false, true), viewportPos, event);
+
+ if (!m_frame->selectionController()->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_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
+ selectClosestWordOrLinkFromMouseEvent(mev);
+ }
+
+ swallowEvent = dispatchMouseEvent(contextmenuEvent, mev.targetNode(), true, 0, event, true);
+
+ return swallowEvent;
+}
+
+void EventHandler::scheduleHoverStateUpdate()
+{
+ if (!m_hoverTimer.isActive())
+ m_hoverTimer.startOneShot(0);
+}
+
+// Whether or not a mouse down can begin the creation of a selection. Fires the selectStart event.
+bool EventHandler::canMouseDownStartSelect(Node* node)
+{
+ if (!node || !node->renderer())
+ return true;
+
+ // Some controls and images can't start a select on a mouse down.
+ if (!node->canStartSelection())
+ return false;
+
+ for (RenderObject* curr = node->renderer(); curr; curr = curr->parent())
+ if (Node* node = curr->element())
+ return EventTargetNodeCast(node)->dispatchHTMLEvent(selectstartEvent, true, true);
+
+ return true;
+}
+
+bool EventHandler::canMouseDragExtendSelect(Node* node)
+{
+ if (!node || !node->renderer())
+ return true;
+
+ for (RenderObject* curr = node->renderer(); curr; curr = curr->parent())
+ if (Node* node = curr->element())
+ return EventTargetNodeCast(node)->dispatchHTMLEvent(selectstartEvent, true, true);
+
+ return true;
+}
+
+void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet)
+{
+ m_frameSetBeingResized = frameSet;
+}
+
+void EventHandler::resizeLayerDestroyed()
+{
+ ASSERT(m_resizeLayer);
+ m_resizeLayer = 0;
+}
+
+void EventHandler::hoverTimerFired(Timer<EventHandler>*)
+{
+ m_hoverTimer.stop();
+
+ ASSERT(m_frame);
+ ASSERT(m_frame->document());
+
+ if (RenderObject* renderer = m_frame->renderer()) {
+ HitTestResult result(m_frame->view()->windowToContents(m_currentMousePosition));
+ renderer->layer()->hitTest(HitTestRequest(false, false, true), result);
+ m_frame->document()->updateRendering();
+ }
+}
+
+static EventTargetNode* eventTargetNodeForDocument(Document* doc)
+{
+ if (!doc)
+ return 0;
+ Node* node = doc->focusedNode();
+ if (!node) {
+ if (doc->isHTMLDocument())
+ node = doc->body();
+ else
+ node = doc->documentElement();
+ if (!node)
+ return 0;
+ }
+ return EventTargetNodeCast(node);
+}
+
+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 !PLATFORM(MAC)
+bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
+{
+ return false;
+}
+#endif
+
+bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
+{
+ // 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());
+ if (!node)
+ return false;
+
+ // FIXME: what is this doing here, in keyboard event handler?
+ m_frame->loader()->resetMultipleFormSubmissionProtection();
+
+ // In IE, access keys are special, they are handled after default keydown processing, but cannot be canceled - this is hard to match.
+ // On Mac OS X, we process them before dispatching keydown, as the default keydown handler implements Emacs key bindings, which may conflict
+ // with access keys. Then we dispatch keydown, but suppress its default handling.
+ // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatching a keypress event for WM_SYSCHAR messages.
+ // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events.
+ bool matchedAnAccessKey = false;
+ if (initialKeyEvent.type() == PlatformKeyboardEvent::KeyDown)
+ matchedAnAccessKey = handleAccessKey(initialKeyEvent);
+
+ // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch.
+ if (initialKeyEvent.type() == PlatformKeyboardEvent::KeyUp || initialKeyEvent.type() == PlatformKeyboardEvent::Char)
+ return !node->dispatchKeyEvent(initialKeyEvent);
+
+ bool backwardCompatibilityMode = needsKeyboardEventDisambiguationQuirks();
+
+ ExceptionCode ec;
+ PlatformKeyboardEvent keyDownEvent = initialKeyEvent;
+ if (keyDownEvent.type() != PlatformKeyboardEvent::RawKeyDown)
+ keyDownEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown, backwardCompatibilityMode);
+ RefPtr<KeyboardEvent> keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView());
+ if (matchedAnAccessKey)
+ keydown->setDefaultPrevented(true);
+ keydown->setTarget(node);
+
+ if (initialKeyEvent.type() == PlatformKeyboardEvent::RawKeyDown) {
+ node->dispatchEvent(keydown, ec, true);
+ return keydown->defaultHandled() || keydown->defaultPrevented();
+ }
+
+ // Run input method in advance of DOM event handling. This may result in the IM
+ // modifying the page prior the keydown event, but this behaviour is necessary
+ // in order to match IE:
+ // 1. preventing default handling of keydown and keypress events has no effect on IM input;
+ // 2. if an input method handles the event, its keyCode is set to 229 in keydown event.
+ m_frame->editor()->handleInputMethodKeydown(keydown.get());
+
+ bool handledByInputMethod = keydown->defaultHandled();
+
+ if (handledByInputMethod) {
+ keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode);
+ keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView());
+ keydown->setTarget(node);
+ keydown->setDefaultHandled();
+ }
+
+ node->dispatchEvent(keydown, ec, true);
+ bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented();
+ if (handledByInputMethod || (keydownResult && !backwardCompatibilityMode))
+ return keydownResult;
+
+ // Focus may have changed during keydown handling, so refetch node.
+ // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original node.
+ if (!keydownResult) {
+ node = eventTargetNodeForDocument(m_frame->document());
+ if (!node)
+ return false;
+ }
+
+ PlatformKeyboardEvent keyPressEvent = initialKeyEvent;
+ keyPressEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::Char, backwardCompatibilityMode);
+ if (keyPressEvent.text().isEmpty())
+ return keydownResult;
+ RefPtr<KeyboardEvent> keypress = new KeyboardEvent(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);
+
+ return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled();
+}
+
+void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event)
+{
+ if (event->type() == keydownEvent) {
+ m_frame->editor()->handleKeyboardEvent(event);
+ if (event->defaultHandled())
+ return;
+ if (event->keyIdentifier() == "U+0009")
+ defaultTabEventHandler(event);
+ }
+ if (event->type() == keypressEvent) {
+ m_frame->editor()->handleKeyboardEvent(event);
+ if (event->defaultHandled())
+ return;
+ }
+}
+
+bool EventHandler::dragHysteresisExceeded(const FloatPoint& floatDragViewportLocation) const
+{
+ IntPoint dragViewportLocation((int)floatDragViewportLocation.x(), (int)floatDragViewportLocation.y());
+ return dragHysteresisExceeded(dragViewportLocation);
+}
+
+bool EventHandler::dragHysteresisExceeded(const IntPoint& dragViewportLocation) const
+{
+ IntPoint dragLocation = m_frame->view()->windowToContents(dragViewportLocation);
+ IntSize delta = dragLocation - m_mouseDownPos;
+
+ int threshold = GeneralDragHysteresis;
+ if (dragState().m_dragSrcIsImage)
+ threshold = ImageDragHysteresis;
+ else if (dragState().m_dragSrcIsLink)
+ threshold = LinkDragHysteresis;
+ else if (dragState().m_dragSrcInSelection)
+ threshold = TextDragHysteresis;
+
+ return abs(delta.width()) >= threshold || abs(delta.height()) >= threshold;
+}
+
+void EventHandler::freeClipboard()
+{
+ if (dragState().m_dragClipboard)
+ dragState().m_dragClipboard->setAccessPolicy(ClipboardNumb);
+}
+
+bool EventHandler::shouldDragAutoNode(Node* node, const IntPoint& point) const
+{
+ ASSERT(node);
+ if (node->hasChildNodes() || !m_frame->view())
+ return false;
+ return m_frame->page() && m_frame->page()->dragController()->mayStartDragAtEventLocation(m_frame, point);
+}
+
+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);
+}
+
+void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation)
+{
+ 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);
+ }
+ freeClipboard();
+ dragState().m_dragSrc = 0;
+}
+
+// returns if we should continue "default processing", i.e., whether eventhandler canceled
+bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event)
+{
+ return !dispatchDragEvent(eventType, dragState().m_dragSrc.get(), event, dragState().m_dragClipboard.get());
+}
+
+bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event)
+{
+ if (event.event().button() != LeftButton || event.event().eventType() != MouseEventMoved) {
+ // If we allowed the other side of the bridge to handle a drag
+ // last time, then m_mousePressed might still be set. So we
+ // clear it now to make sure the next move after a drag
+ // doesn't look like a drag.
+ m_mousePressed = false;
+ return false;
+ }
+
+ if (eventLoopHandleMouseDragged(event))
+ return true;
+
+ // Careful that the drag starting logic stays in sync with eventMayStartDrag()
+
+ if (m_mouseDownMayStartDrag && !dragState().m_dragSrc) {
+ allowDHTMLDrag(dragState().m_dragSrcMayBeDHTML, dragState().m_dragSrcMayBeUA);
+ if (!dragState().m_dragSrcMayBeDHTML && !dragState().m_dragSrcMayBeUA)
+ m_mouseDownMayStartDrag = false; // no element is draggable
+ }
+
+ if (m_mouseDownMayStartDrag && !dragState().m_dragSrc) {
+ // 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);
+ Node* node = result.innerNode();
+ if (node && node->renderer())
+ dragState().m_dragSrc = node->renderer()->draggableNode(dragState().m_dragSrcMayBeDHTML, dragState().m_dragSrcMayBeUA,
+ m_mouseDownPos.x(), m_mouseDownPos.y(), dragState().m_dragSrcIsDHTML);
+ else
+ dragState().m_dragSrc = 0;
+
+ if (!dragState().m_dragSrc)
+ m_mouseDownMayStartDrag = false; // no element is draggable
+ else {
+ // remember some facts about this source, while we have a HitTestResult handy
+ node = result.URLElement();
+ dragState().m_dragSrcIsLink = node && node->isLink();
+
+ node = result.innerNonSharedNode();
+ dragState().m_dragSrcIsImage = node && node->renderer() && node->renderer()->isImage();
+
+ dragState().m_dragSrcInSelection = m_frame->selectionController()->contains(m_mouseDownPos);
+ }
+ }
+
+ // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
+ // or else we bail on the dragging stuff and allow selection to occur
+ if (m_mouseDownMayStartDrag && !dragState().m_dragSrcIsImage && dragState().m_dragSrcInSelection && event.event().timestamp() - m_mouseDownTimestamp < TextDragDelay) {
+ m_mouseDownMayStartDrag = false;
+ dragState().m_dragSrc = 0;
+ // ...but if this was the first click in the window, we don't even want to start selection
+ if (eventActivatedView(event.event()))
+ m_mouseDownMayStartSelect = false;
+ }
+
+ if (!m_mouseDownMayStartDrag)
+ return !mouseDownMayStartSelect() && !m_mouseDownMayStartAutoscroll;
+
+ // We are starting a text/image/url drag, so the cursor should be an arrow
+ m_frame->view()->setCursor(pointerCursor());
+
+ if (!dragHysteresisExceeded(event.event().pos()))
+ return true;
+
+ // Once we're past the hysteresis point, we don't want to treat this gesture as a click
+ invalidateClick();
+
+ DragOperation srcOp = DragOperationNone;
+
+ freeClipboard(); // would only happen if we missed a dragEnd. Do it anyway, just
+ // to make sure it gets numbified
+ dragState().m_dragClipboard = createDraggingClipboard();
+
+ if (dragState().m_dragSrcMayBeDHTML) {
+ // Check to see if the is a DOM based drag, if it is get the DOM specified drag
+ // 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);
+ }
+
+ m_mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown)
+ && !m_frame->selectionController()->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.
+ dragState().m_dragClipboard->setAccessPolicy(ClipboardImageWritable);
+
+ if (m_mouseDownMayStartDrag) {
+ // gather values from DHTML element, if it set any
+ dragState().m_dragClipboard->sourceOperation(srcOp);
+
+ // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
+ // dragImage! Because of that dumb reentrancy, we may think we've not started the
+ // drag when that happens. So we have to assume it's started before we kick it off.
+ dragState().m_dragClipboard->setDragHasStarted();
+ }
+ }
+
+ if (m_mouseDownMayStartDrag) {
+ DragController* dragController = m_frame->page() ? m_frame->page()->dragController() : 0;
+ 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());
+ m_mouseDownMayStartDrag = false;
+ }
+ }
+
+ if (!m_mouseDownMayStartDrag) {
+ // something failed to start the drag, cleanup
+ freeClipboard();
+ dragState().m_dragSrc = 0;
+ }
+
+ // No more default handling (like selection), whether we're past the hysteresis bounds or not
+ return true;
+}
+
+bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent,
+ bool isLineBreak, bool isBackTab)
+{
+ if (!m_frame)
+ return false;
+#ifndef NDEBUG
+ // 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);
+#endif
+ EventTarget* target;
+ if (underlyingEvent)
+ target = underlyingEvent->target();
+ else
+ target = eventTargetNodeForDocument(m_frame->document());
+ if (!target)
+ return false;
+ RefPtr<TextEvent> event = new TextEvent(m_frame->domWindow(), text);
+ event->setUnderlyingEvent(underlyingEvent);
+ event->setIsLineBreak(isLineBreak);
+ event->setIsBackTab(isBackTab);
+ ExceptionCode ec;
+ return target->dispatchEvent(event.release(), ec, true);
+}
+
+
+#if !PLATFORM(MAC) && !PLATFORM(QT)
+bool EventHandler::invertSenseOfTabsToLinks(KeyboardEvent*) const
+{
+ return false;
+}
+#endif
+
+bool EventHandler::tabsToLinks(KeyboardEvent* event) const
+{
+ Page* page = m_frame->page();
+ if (!page)
+ return false;
+
+ if (page->chrome()->client()->tabsToLinks())
+ return !invertSenseOfTabsToLinks(event);
+
+ return invertSenseOfTabsToLinks(event);
+}
+
+void EventHandler::defaultTextInputEventHandler(TextEvent* event)
+{
+ String data = event->data();
+ if (data == "\n") {
+ if (event->isLineBreak()) {
+ if (m_frame->editor()->insertLineBreak())
+ event->setDefaultHandled();
+ } else {
+ if (m_frame->editor()->insertParagraphSeparator())
+ event->setDefaultHandled();
+ }
+ } else {
+ if (m_frame->editor()->insertTextWithoutSendingTextEvent(data, false, event))
+ event->setDefaultHandled();
+ }
+}
+
+void EventHandler::defaultTabEventHandler(KeyboardEvent* event)
+{
+ // We should only advance focus on tabs if no special modifier keys are held down.
+ if (event->ctrlKey() || event->metaKey() || event->altGraphKey())
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+ if (!page->tabKeyCyclesThroughElements())
+ return;
+
+ FocusDirection focusDirection = event->shiftKey() ? FocusDirectionBackward : FocusDirectionForward;
+
+ // Tabs can be used in design mode editing.
+ if (m_frame->document()->inDesignMode())
+ return;
+
+ if (page->focusController()->advanceFocus(focusDirection, event))
+ event->setDefaultHandled();
+}
+
+void EventHandler::capsLockStateMayHaveChanged()
+{
+ if (Document* d = m_frame->document())
+ if (Node* node = d->focusedNode())
+ if (RenderObject* r = node->renderer())
+ r->capsLockStateMayHaveChanged();
+}
+
+}
diff --git a/WebCore/page/EventHandler.h b/WebCore/page/EventHandler.h
new file mode 100644
index 0000000..95a976d
--- /dev/null
+++ b/WebCore/page/EventHandler.h
@@ -0,0 +1,325 @@
+/*
+ * 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 EventHandler_h
+#define EventHandler_h
+
+#include "DragActions.h"
+#include "FocusDirection.h"
+#include "PlatformMouseEvent.h"
+#include "ScrollTypes.h"
+#include "Timer.h"
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/Platform.h>
+#include <wtf/RefPtr.h>
+
+#if PLATFORM(MAC)
+#include "WebCoreKeyboardUIMode.h"
+#ifndef __OBJC__
+class NSEvent;
+class NSView;
+#endif
+#endif
+
+namespace WebCore {
+
+class AtomicString;
+class Clipboard;
+class Cursor;
+class EventTargetNode;
+class Event;
+class FloatPoint;
+class FloatRect;
+class Frame;
+class HitTestResult;
+class HTMLFrameSetElement;
+class KeyboardEvent;
+class MouseEventWithHitTestResults;
+class Node;
+class PlatformKeyboardEvent;
+class PlatformScrollbar;
+class PlatformWheelEvent;
+class RenderLayer;
+class RenderObject;
+class RenderWidget;
+class String;
+class TextEvent;
+class VisiblePosition;
+class Widget;
+
+struct HitTestRequest;
+
+extern const int LinkDragHysteresis;
+extern const int ImageDragHysteresis;
+extern const int TextDragHysteresis;
+extern const int GeneralDragHysteresis;
+extern const double TextDragDelay;
+
+class EventHandler : Noncopyable {
+public:
+ EventHandler(Frame*);
+ ~EventHandler();
+
+ void clear();
+
+ void updateSelectionForMouseDrag();
+
+ Node* mousePressNode() const;
+ void setMousePressNode(PassRefPtr<Node>);
+
+ void stopAutoscrollTimer(bool rendererIsBeingDestroyed = false);
+ RenderObject* autoscrollRenderer() const;
+
+ HitTestResult hitTestResultAtPoint(const IntPoint&, bool allowShadowContent);
+
+ bool mousePressed() const { return m_mousePressed; }
+ void setMousePressed(bool pressed) { m_mousePressed = pressed; }
+
+ void setCapturingMouseEventsNode(PassRefPtr<Node>);
+
+ bool updateDragAndDrop(const PlatformMouseEvent&, Clipboard*);
+ void cancelDragAndDrop(const PlatformMouseEvent&, Clipboard*);
+ bool performDragAndDrop(const PlatformMouseEvent&, Clipboard*);
+
+ void scheduleHoverStateUpdate();
+
+ void setResizingFrameSet(HTMLFrameSetElement*);
+
+ void resizeLayerDestroyed();
+
+ IntPoint currentMousePosition() const;
+
+ void setIgnoreWheelEvents(bool);
+
+ bool scrollOverflow(ScrollDirection, ScrollGranularity);
+
+ bool shouldDragAutoNode(Node*, const IntPoint&) const; // -webkit-user-drag == auto
+
+ bool tabsToLinks(KeyboardEvent*) const;
+ bool tabsToAllControls(KeyboardEvent*) const;
+
+ bool mouseDownMayStartSelect() const { return m_mouseDownMayStartSelect; }
+
+ bool mouseMoved(const PlatformMouseEvent&);
+
+ bool handleMousePressEvent(const PlatformMouseEvent&);
+ bool handleMouseMoveEvent(const PlatformMouseEvent&, HitTestResult* hoveredNode = 0);
+ bool handleMouseReleaseEvent(const PlatformMouseEvent&);
+ bool handleWheelEvent(PlatformWheelEvent&);
+
+ bool sendContextMenuEvent(const PlatformMouseEvent&);
+
+ void setMouseDownMayStartAutoscroll() { m_mouseDownMayStartAutoscroll = true; }
+
+ bool needsKeyboardEventDisambiguationQuirks() const;
+
+ bool handleAccessKey(const PlatformKeyboardEvent&);
+ bool keyEvent(const PlatformKeyboardEvent&);
+ void defaultKeyboardEventHandler(KeyboardEvent*);
+
+ bool handleTextInputEvent(const String& text, Event* underlyingEvent = 0,
+ bool isLineBreak = false, bool isBackTab = false);
+ void defaultTextInputEventHandler(TextEvent*);
+
+ bool eventMayStartDrag(const PlatformMouseEvent&) const;
+
+ void dragSourceMovedTo(const PlatformMouseEvent&);
+ void dragSourceEndedAt(const PlatformMouseEvent&, DragOperation);
+
+ void focusDocumentView();
+
+ void capsLockStateMayHaveChanged();
+
+#if PLATFORM(MAC)
+ PassRefPtr<KeyboardEvent> currentKeyboardEvent() const;
+
+ void mouseDown(NSEvent*);
+ void mouseDragged(NSEvent*);
+ void mouseUp(NSEvent*);
+ void mouseMoved(NSEvent*);
+ bool keyEvent(NSEvent*);
+ bool wheelEvent(NSEvent*);
+
+ void sendFakeEventsAfterWidgetTracking(NSEvent* initiatingEvent);
+
+ void setActivationEventNumber(int num) { m_activationEventNumber = num; }
+
+ NSEvent *currentNSEvent();
+#endif
+
+private:
+ struct EventHandlerDragState {
+ RefPtr<Node> m_dragSrc; // element that may be a drag source, for the current mouse gesture
+ bool m_dragSrcIsLink;
+ bool m_dragSrcIsImage;
+ bool m_dragSrcInSelection;
+ bool m_dragSrcMayBeDHTML;
+ bool m_dragSrcMayBeUA; // Are DHTML and/or the UserAgent allowed to drag out?
+ bool m_dragSrcIsDHTML;
+ RefPtr<Clipboard> m_dragClipboard; // used on only the source side of dragging
+ };
+ static EventHandlerDragState& dragState();
+
+ Clipboard* createDraggingClipboard() const;
+
+ bool eventActivatedView(const PlatformMouseEvent&) const;
+ void selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& event);
+ void selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& event);
+
+ bool handleMouseDoubleClickEvent(const PlatformMouseEvent&);
+
+ bool handleMousePressEvent(const MouseEventWithHitTestResults&);
+ bool handleMousePressEventSingleClick(const MouseEventWithHitTestResults&);
+ bool handleMousePressEventDoubleClick(const MouseEventWithHitTestResults&);
+ bool handleMousePressEventTripleClick(const MouseEventWithHitTestResults&);
+ bool handleMouseDraggedEvent(const MouseEventWithHitTestResults&);
+ bool handleMouseReleaseEvent(const MouseEventWithHitTestResults&);
+
+ Cursor selectCursor(const MouseEventWithHitTestResults&, PlatformScrollbar*);
+
+ void hoverTimerFired(Timer<EventHandler>*);
+
+ static bool canMouseDownStartSelect(Node*);
+ static bool canMouseDragExtendSelect(Node*);
+
+ void handleAutoscroll(RenderObject*);
+ void startAutoscrollTimer();
+ void setAutoscrollRenderer(RenderObject*);
+
+ void autoscrollTimerFired(Timer<EventHandler>*);
+
+ void invalidateClick();
+
+ Node* nodeUnderMouse() const;
+
+ void updateMouseEventTargetNode(Node*, const PlatformMouseEvent&, bool fireMouseOverOut);
+ void fireMouseOverOut(bool fireMouseOver = true, bool fireMouseOut = true, bool updateLastNodeUnderMouse = true);
+
+ MouseEventWithHitTestResults prepareMouseEvent(const HitTestRequest&, const PlatformMouseEvent&);
+
+ bool dispatchMouseEvent(const AtomicString& eventType, Node* target, bool cancelable, int clickCount, const PlatformMouseEvent&, bool setUnder);
+ bool dispatchDragEvent(const AtomicString& eventType, Node* target, const PlatformMouseEvent&, Clipboard*);
+
+ void freeClipboard();
+
+ bool handleDrag(const MouseEventWithHitTestResults&);
+ bool handleMouseUp(const MouseEventWithHitTestResults&);
+ void clearDragState();
+
+ bool dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent&);
+
+ bool dragHysteresisExceeded(const FloatPoint&) const;
+ bool dragHysteresisExceeded(const IntPoint&) const;
+
+ bool passMousePressEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe);
+ bool passMouseMoveEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe, HitTestResult* hoveredNode = 0);
+ bool passMouseReleaseEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe);
+
+ bool passSubframeEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe, HitTestResult* hoveredNode = 0);
+
+ bool passMousePressEventToScrollbar(MouseEventWithHitTestResults&, PlatformScrollbar*);
+
+ bool passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults&);
+ bool passWidgetMouseDownEventToWidget(RenderWidget*);
+
+ bool passMouseDownEventToWidget(Widget*);
+ bool passWheelEventToWidget(PlatformWheelEvent&, Widget*);
+
+ void defaultTabEventHandler(KeyboardEvent*);
+
+ void allowDHTMLDrag(bool& flagDHTML, bool& flagUA) const;
+
+ // The following are called at the beginning of handleMouseUp and handleDrag.
+ // If they return true it indicates that they have consumed the event.
+#if PLATFORM(MAC)
+ bool eventLoopHandleMouseUp(const MouseEventWithHitTestResults&);
+ bool eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&);
+ NSView *mouseDownViewIfStillGood();
+#else
+ bool eventLoopHandleMouseUp(const MouseEventWithHitTestResults&) { return false; }
+ bool eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&) { return false; }
+#endif
+
+ bool invertSenseOfTabsToLinks(KeyboardEvent*) const;
+
+ void updateSelectionForMouseDrag(Node* targetNode, const IntPoint& localPoint);
+
+ Frame* m_frame;
+
+ bool m_mousePressed;
+ RefPtr<Node> m_mousePressNode;
+
+ bool m_mouseDownMayStartSelect;
+ bool m_mouseDownMayStartDrag;
+ bool m_mouseDownWasSingleClickInSelection;
+ bool m_beganSelectingText;
+
+ IntPoint m_dragStartPos;
+
+ Timer<EventHandler> m_hoverTimer;
+
+ Timer<EventHandler> m_autoscrollTimer;
+ RenderObject* m_autoscrollRenderer;
+ bool m_mouseDownMayStartAutoscroll;
+ bool m_mouseDownWasInSubframe;
+#if ENABLE(SVG)
+ bool m_svgPan;
+#endif
+
+ RenderLayer* m_resizeLayer;
+
+ RefPtr<Node> m_capturingMouseEventsNode;
+
+ RefPtr<Node> m_nodeUnderMouse;
+ RefPtr<Node> m_lastNodeUnderMouse;
+ RefPtr<Frame> m_lastMouseMoveEventSubframe;
+ RefPtr<PlatformScrollbar> m_lastScrollbarUnderMouse;
+
+ int m_clickCount;
+ RefPtr<Node> m_clickNode;
+
+ RefPtr<Node> m_dragTarget;
+
+ RefPtr<HTMLFrameSetElement> m_frameSetBeingResized;
+
+ IntSize m_offsetFromResizeCorner;
+
+ IntPoint m_currentMousePosition;
+ IntPoint m_mouseDownPos; // in our view's coords
+ double m_mouseDownTimestamp;
+ PlatformMouseEvent m_mouseDown;
+
+#if PLATFORM(MAC)
+ NSView *m_mouseDownView;
+ bool m_sendingEventToSubview;
+ int m_activationEventNumber;
+#endif
+
+};
+
+} // namespace WebCore
+
+#endif // EventHandler_h
diff --git a/WebCore/page/FocusController.cpp b/WebCore/page/FocusController.cpp
new file mode 100644
index 0000000..a3542aa
--- /dev/null
+++ b/WebCore/page/FocusController.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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 "FocusController.h"
+
+#include "AXObjectCache.h"
+#include "Chrome.h"
+#include "Document.h"
+#include "Editor.h"
+#include "EditorClient.h"
+#include "Element.h"
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "FrameTree.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HTMLNames.h"
+#include "KeyboardEvent.h"
+#include "Page.h"
+#include "Range.h"
+#include "RenderObject.h"
+#include "RenderWidget.h"
+#include "SelectionController.h"
+#include "Widget.h"
+#include <wtf/Platform.h>
+
+namespace WebCore {
+
+using namespace EventNames;
+using namespace HTMLNames;
+
+FocusController::FocusController(Page* page)
+ : m_page(page)
+ , m_isActive(false)
+{
+}
+
+void FocusController::setFocusedFrame(PassRefPtr<Frame> frame)
+{
+ if (m_focusedFrame == frame)
+ return;
+
+ if (m_focusedFrame)
+ m_focusedFrame->selectionController()->setFocused(false);
+
+ m_focusedFrame = frame;
+
+ if (m_focusedFrame)
+ m_focusedFrame->selectionController()->setFocused(true);
+}
+
+Frame* FocusController::focusedOrMainFrame()
+{
+ if (Frame* frame = focusedFrame())
+ return frame;
+ return m_page->mainFrame();
+}
+
+static Node* deepFocusableNode(FocusDirection direction, Node* node, KeyboardEvent* event)
+{
+ // The node we found might be a HTMLFrameOwnerElement, so descend down the frame tree until we find either:
+ // 1) a focusable node, or
+ // 2) the deepest-nested HTMLFrameOwnerElement
+ while (node && node->isFrameOwnerElement()) {
+ HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node);
+ if (!owner->contentFrame())
+ break;
+
+ Document* document = owner->contentFrame()->document();
+ if (!document)
+ break;
+
+ node = (direction == FocusDirectionForward)
+ ? document->nextFocusableNode(0, event)
+ : document->previousFocusableNode(0, event);
+ if (!node) {
+ node = owner;
+ break;
+ }
+ }
+
+ return node;
+}
+
+bool FocusController::setInitialFocus(FocusDirection direction, KeyboardEvent* event)
+{
+ return advanceFocus(direction, event, true);
+}
+
+bool FocusController::advanceFocus(FocusDirection direction, KeyboardEvent* event, bool initialFocus)
+{
+ Frame* frame = focusedOrMainFrame();
+ ASSERT(frame);
+ Document* document = frame->document();
+ if (!document)
+ return false;
+
+ Node* node = (direction == FocusDirectionForward)
+ ? document->nextFocusableNode(document->focusedNode(), event)
+ : document->previousFocusableNode(document->focusedNode(), event);
+
+ // If there's no focusable node to advance to, move up the frame tree until we find one.
+ while (!node && frame) {
+ Frame* parentFrame = frame->tree()->parent();
+ if (!parentFrame)
+ break;
+
+ Document* parentDocument = parentFrame->document();
+ if (!parentDocument)
+ break;
+
+ HTMLFrameOwnerElement* owner = frame->ownerElement();
+ if (!owner)
+ break;
+
+ node = (direction == FocusDirectionForward)
+ ? parentDocument->nextFocusableNode(owner, event)
+ : parentDocument->previousFocusableNode(owner, event);
+
+ frame = parentFrame;
+ }
+
+ node = deepFocusableNode(direction, node, event);
+
+ if (!node) {
+ // We didn't find a node to focus, so we should try to pass focus to Chrome.
+ if (!initialFocus && m_page->chrome()->canTakeFocus(direction)) {
+ document->setFocusedNode(0);
+ setFocusedFrame(0);
+ m_page->chrome()->takeFocus(direction);
+ return true;
+ }
+
+ // Chrome doesn't want focus, so we should wrap focus.
+ if (Document* d = m_page->mainFrame()->document())
+ node = (direction == FocusDirectionForward)
+ ? d->nextFocusableNode(0, event)
+ : d->previousFocusableNode(0, event);
+
+ node = deepFocusableNode(direction, node, event);
+
+ if (!node)
+ return false;
+ }
+
+ ASSERT(node);
+
+ if (node == document->focusedNode())
+ // Focus wrapped around to the same node.
+ return true;
+
+ if (!node->isElementNode())
+ // FIXME: May need a way to focus a document here.
+ return false;
+
+ if (node->isFrameOwnerElement()) {
+ // We focus frames rather than frame owners.
+ // FIXME: We should not focus frames that have no scrollbars, as focusing them isn't useful to the user.
+ HTMLFrameOwnerElement* owner = static_cast<HTMLFrameOwnerElement*>(node);
+ if (!owner->contentFrame())
+ return false;
+
+ document->setFocusedNode(0);
+ setFocusedFrame(owner->contentFrame());
+ return true;
+ }
+
+ // FIXME: It would be nice to just be able to call setFocusedNode(node) here, but we can't do
+ // that because some elements (e.g. HTMLInputElement and HTMLTextAreaElement) do extra work in
+ // their focus() methods.
+
+ Document* newDocument = node->document();
+
+ if (newDocument != document)
+ // Focus is going away from this document, so clear the focused node.
+ document->setFocusedNode(0);
+
+ if (newDocument)
+ setFocusedFrame(newDocument->frame());
+
+ static_cast<Element*>(node)->focus(false);
+ return true;
+}
+
+static bool relinquishesEditingFocus(Node *node)
+{
+ ASSERT(node);
+ ASSERT(node->isContentEditable());
+
+ Node* root = node->rootEditableElement();
+ Frame* frame = node->document()->frame();
+ if (!frame || !root)
+ return false;
+
+ return frame->editor()->shouldEndEditing(rangeOfContents(root).get());
+}
+
+static void clearSelectionIfNeeded(Frame* oldFocusedFrame, Frame* newFocusedFrame, Node* newFocusedNode)
+{
+ if (!oldFocusedFrame || !newFocusedFrame)
+ return;
+
+ if (oldFocusedFrame->document() != newFocusedFrame->document())
+ return;
+
+ SelectionController* s = oldFocusedFrame->selectionController();
+ if (s->isNone())
+ return;
+
+ Node* selectionStartNode = s->selection().start().node();
+ if (selectionStartNode == newFocusedNode || selectionStartNode->isDescendantOf(newFocusedNode) || selectionStartNode->shadowAncestorNode() == newFocusedNode)
+ return;
+
+ if (Node* mousePressNode = newFocusedFrame->eventHandler()->mousePressNode())
+ if (mousePressNode->renderer() && !mousePressNode->canStartSelection())
+ if (Node* root = s->rootEditableElement())
+ if (Node* shadowAncestorNode = root->shadowAncestorNode())
+ // Don't do this for textareas and text fields, when they lose focus their selections should be cleared
+ // and then restored when they regain focus, to match other browsers.
+ if (!shadowAncestorNode->hasTagName(inputTag) && !shadowAncestorNode->hasTagName(textareaTag))
+ return;
+
+ s->clear();
+}
+
+bool FocusController::setFocusedNode(Node* node, PassRefPtr<Frame> newFocusedFrame)
+{
+ RefPtr<Frame> oldFocusedFrame = focusedFrame();
+ RefPtr<Document> oldDocument = oldFocusedFrame ? oldFocusedFrame->document() : 0;
+
+ Node* oldFocusedNode = oldDocument ? oldDocument->focusedNode() : 0;
+ if (oldFocusedNode == node)
+ return true;
+
+ if (oldFocusedNode && oldFocusedNode->rootEditableElement() == oldFocusedNode && !relinquishesEditingFocus(oldFocusedNode))
+ return false;
+
+ clearSelectionIfNeeded(oldFocusedFrame.get(), newFocusedFrame.get(), node);
+
+ if (!node) {
+ if (oldDocument)
+ oldDocument->setFocusedNode(0);
+ m_page->editorClient()->setInputMethodState(false);
+ return true;
+ }
+
+ RefPtr<Document> newDocument = node ? node->document() : 0;
+
+ if (newDocument && newDocument->focusedNode() == node) {
+ m_page->editorClient()->setInputMethodState(node->shouldUseInputMethod());
+ return true;
+ }
+
+ if (oldDocument && oldDocument != newDocument)
+ oldDocument->setFocusedNode(0);
+
+ setFocusedFrame(newFocusedFrame);
+
+ if (newDocument)
+ newDocument->setFocusedNode(node);
+
+ m_page->editorClient()->setInputMethodState(node->shouldUseInputMethod());
+
+ return true;
+}
+
+void FocusController::setActive(bool active)
+{
+ if (m_isActive == active)
+ return;
+
+ 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
+
+ focusedOrMainFrame()->selectionController()->pageActivationChanged();
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/FocusController.h b/WebCore/page/FocusController.h
new file mode 100644
index 0000000..f4a6632
--- /dev/null
+++ b/WebCore/page/FocusController.h
@@ -0,0 +1,64 @@
+/*
+ * 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 FocusController_h
+#define FocusController_h
+
+#include "FocusDirection.h"
+#include <wtf/Forward.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+ class Frame;
+ class KeyboardEvent;
+ class Node;
+ class Page;
+
+ class FocusController {
+ public:
+ FocusController(Page*);
+
+ void setFocusedFrame(PassRefPtr<Frame>);
+ Frame* focusedFrame() const { return m_focusedFrame.get(); }
+ Frame* focusedOrMainFrame();
+
+ bool setInitialFocus(FocusDirection, KeyboardEvent*);
+ bool advanceFocus(FocusDirection, KeyboardEvent*, bool initialFocus = false);
+
+ bool setFocusedNode(Node*, PassRefPtr<Frame>);
+
+ void setActive(bool);
+ bool isActive() const { return m_isActive; }
+
+ private:
+ Page* m_page;
+ RefPtr<Frame> m_focusedFrame;
+ bool m_isActive;
+ };
+
+} // namespace WebCore
+
+#endif // FocusController_h
diff --git a/WebCore/page/FocusDirection.h b/WebCore/page/FocusDirection.h
new file mode 100644
index 0000000..261c745
--- /dev/null
+++ b/WebCore/page/FocusDirection.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006 Apple Computer, 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 FocusDirection_h
+#define FocusDirection_h
+
+namespace WebCore {
+ enum FocusDirection {
+ FocusDirectionForward = 0,
+ FocusDirectionBackward
+ };
+}
+
+#endif // FocusDirection_h
diff --git a/WebCore/page/Frame.cpp b/WebCore/page/Frame.cpp
new file mode 100644
index 0000000..2552ed4
--- /dev/null
+++ b/WebCore/page/Frame.cpp
@@ -0,0 +1,1886 @@
+/*
+ * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ * 1999 Lars Knoll <knoll@kde.org>
+ * 1999 Antti Koivisto <koivisto@kde.org>
+ * 2000 Simon Hausmann <hausmann@kde.org>
+ * 2000 Stefan Schimanski <1Stein@gmx.de>
+ * 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
+ *
+ * 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 "Frame.h"
+#include "FramePrivate.h"
+
+#include "ApplyStyleCommand.h"
+#include "BeforeUnloadEvent.h"
+#include "CSSComputedStyleDeclaration.h"
+#include "CSSProperty.h"
+#include "CSSPropertyNames.h"
+#include "CachedCSSStyleSheet.h"
+#include "DOMWindow.h"
+#include "DocLoader.h"
+#include "DocumentType.h"
+#include "EditingText.h"
+#include "EditorClient.h"
+#include "EventNames.h"
+#include "FocusController.h"
+#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 "HTMLNames.h"
+#include "HTMLTableCellElement.h"
+#include "Logging.h"
+#include "MediaFeatureNames.h"
+#include "NodeList.h"
+#include "Page.h"
+#include "RegularExpression.h"
+#include "RenderPart.h"
+#include "RenderTableCell.h"
+#include "RenderTextControl.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "Settings.h"
+#include "SystemTime.h"
+#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 "visible_units.h"
+
+#if FRAME_LOADS_USER_STYLESHEET
+#include "UserStyleSheetLoader.h"
+#endif
+
+#if ENABLE(SVG)
+#include "SVGDocument.h"
+#include "SVGDocumentExtensions.h"
+#include "SVGNames.h"
+#include "XLinkNames.h"
+#endif
+
+using namespace std;
+
+using KJS::JSLock;
+
+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)
+ LOG(WebCoreFrameLeaks, "LEAK: %d Frame\n", count);
+ }
+};
+int FrameCounter::count = 0;
+static FrameCounter frameCounter;
+#endif
+
+static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
+{
+ if (!ownerElement)
+ return 0;
+ return ownerElement->document()->frame();
+}
+
+Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
+ : RefCounted<Frame>(0)
+ , d(new FramePrivate(page, parentFromOwnerElement(ownerElement), this, ownerElement, frameLoaderClient))
+{
+ AtomicString::init();
+ EventNames::init();
+ HTMLNames::init();
+ QualifiedName::init();
+ MediaFeatureNames::init();
+
+#if ENABLE(SVG)
+ SVGNames::init();
+ XLinkNames::init();
+#endif
+
+ XMLNames::init();
+
+ if (!ownerElement)
+ page->setMainFrame(this);
+ else {
+ // FIXME: It's bad to have a ref() here but not in the !ownerElement case.
+ // We need to straighten this out.
+ ref();
+ page->incrementFrameCount();
+ ownerElement->m_contentFrame = this;
+ }
+
+#ifndef NDEBUG
+ ++FrameCounter::count;
+#endif
+}
+
+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
+
+ ASSERT(!d->m_lifeSupportTimer.isActive());
+
+#ifndef NDEBUG
+ --FrameCounter::count;
+#endif
+
+ if (d->m_jscript && d->m_jscript->haveGlobalObject())
+ static_cast<KJS::Window*>(d->m_jscript->globalObject())->disconnectFrame();
+
+ disconnectOwnerElement();
+
+ if (d->m_domWindow)
+ d->m_domWindow->disconnectFrame();
+
+ if (d->m_view) {
+ d->m_view->hide();
+ d->m_view->clearFrame();
+ }
+
+ ASSERT(!d->m_lifeSupportTimer.isActive());
+
+#if FRAME_LOADS_USER_STYLESHEET
+ delete d->m_userStyleSheetLoader;
+#endif
+
+ delete d;
+ d = 0;
+}
+
+void Frame::init()
+{
+ d->m_loader->init();
+}
+
+FrameLoader* Frame::loader() const
+{
+ return d->m_loader;
+}
+
+FrameView* Frame::view() const
+{
+ return d->m_view.get();
+}
+
+void Frame::setView(FrameView* view)
+{
+ // 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.
+ if (!view && d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
+ // FIXME: We don't call willRemove here. Why is that OK?
+ d->m_doc->detach();
+ if (d->m_view)
+ d->m_view->unscheduleRelayout();
+ }
+ eventHandler()->clear();
+
+ d->m_view = view;
+
+ // Only one form submission is allowed per view of a part.
+ // Since this part may be getting reused as a result of being
+ // pulled from the back/forward cache, reset this flag.
+ loader()->resetMultipleFormSubmissionProtection();
+}
+
+KJSProxy *Frame::scriptProxy()
+{
+ if (!d->m_jscript)
+ d->m_jscript = new KJSProxy(this);
+ return d->m_jscript;
+}
+
+Document *Frame::document() const
+{
+ if (d)
+ return d->m_doc.get();
+ return 0;
+}
+
+void Frame::setDocument(PassRefPtr<Document> newDoc)
+{
+ if (d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
+ // FIXME: We don't call willRemove here. Why is that OK?
+ d->m_doc->detach();
+ }
+
+ d->m_doc = newDoc;
+ if (d->m_doc && selectionController()->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();
+}
+
+Settings* Frame::settings() const
+{
+ return d->m_page ? d->m_page->settings() : 0;
+}
+
+String Frame::selectedText() const
+{
+ return plainText(selectionController()->toRange().get());
+}
+
+IntRect Frame::firstRectForRange(Range* range) const
+{
+ int extraWidthToEndOfLine = 0;
+ 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);
+
+ if (startCaretRect.y() == endCaretRect.y()) {
+ // start and end are on the same line
+ return IntRect(min(startCaretRect.x(), endCaretRect.x()),
+ startCaretRect.y(),
+ abs(endCaretRect.x() - startCaretRect.x()),
+ max(startCaretRect.height(), endCaretRect.height()));
+ }
+
+ // start and end aren't on the same line, so go from start to the end of its line
+ return IntRect(startCaretRect.x(),
+ startCaretRect.y(),
+ startCaretRect.width() + extraWidthToEndOfLine,
+ startCaretRect.height());
+}
+
+SelectionController* Frame::selectionController() const
+{
+ return &d->m_selectionController;
+}
+
+Editor* Frame::editor() const
+{
+ return &d->m_editor;
+}
+
+TextGranularity Frame::selectionGranularity() const
+{
+ return d->m_selectionGranularity;
+}
+
+void Frame::setSelectionGranularity(TextGranularity granularity) const
+{
+ d->m_selectionGranularity = granularity;
+}
+
+SelectionController* Frame::dragCaretController() const
+{
+ return d->m_page->dragCaretController();
+}
+
+
+AnimationController* Frame::animationController() const
+{
+ return &d->m_animationController;
+}
+
+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");
+ String pattern("(");
+ unsigned int numLabels = labels.size();
+ unsigned int i;
+ for (i = 0; i < numLabels; i++) {
+ String label = labels[i];
+
+ bool startsWithWordChar = false;
+ bool endsWithWordChar = false;
+ if (label.length() != 0) {
+ startsWithWordChar = wordRegExp.search(label.substring(0, 1)) >= 0;
+ endsWithWordChar = wordRegExp.search(label.substring(label.length() - 1, 1)) >= 0;
+ }
+
+ if (i != 0)
+ pattern.append("|");
+ // 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) {
+ pattern.append("\\b");
+ }
+ pattern.append(label);
+ if (endsWithWordChar) {
+ pattern.append("\\b");
+ }
+ }
+ pattern.append(")");
+ return new RegularExpression(pattern, false);
+}
+
+String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
+{
+ RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
+
+ if (cellRenderer && cellRenderer->isTableCell()) {
+ RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
+
+ if (cellAboveRenderer) {
+ HTMLTableCellElement* aboveCell =
+ static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
+
+ if (aboveCell) {
+ // search within the above cell we found for a match
+ 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
+ String nodeString = n->nodeValue();
+ int pos = regExp->searchRev(nodeString);
+ if (pos >= 0)
+ return nodeString.substring(pos, regExp->matchedLength());
+ }
+ }
+ }
+ }
+ }
+ // Any reason in practice to search all cells in that are above cell?
+ return String();
+}
+
+String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
+{
+ OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
+ // We stop searching after we've seen this many chars
+ const unsigned int charsSearchedThreshold = 500;
+ // This is the absolute max we search. We allow a little more slop than
+ // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
+ const unsigned int maxCharsSearched = 600;
+ // If the starting element is within a table, the cell that contains it
+ HTMLTableCellElement* startingTableCell = 0;
+ bool searchedCellAbove = false;
+
+ // walk backwards in the node tree, until another element, or form, or end of tree
+ int unsigned lengthSearched = 0;
+ Node* n;
+ for (n = element->traversePreviousNode();
+ n && lengthSearched < charsSearchedThreshold;
+ n = n->traversePreviousNode())
+ {
+ if (n->hasTagName(formTag)
+ || (n->isHTMLElement()
+ && static_cast<HTMLElement*>(n)->isGenericFormElement()))
+ {
+ // We hit another form element or the start of the form - bail out
+ break;
+ } else if (n->hasTagName(tdTag) && !startingTableCell) {
+ startingTableCell = static_cast<HTMLTableCellElement*>(n);
+ } else if (n->hasTagName(trTag) && startingTableCell) {
+ String result = searchForLabelsAboveCell(regExp.get(), startingTableCell);
+ if (!result.isEmpty())
+ return result;
+ searchedCellAbove = true;
+ } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
+ // For each text chunk, run the regexp
+ 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.substring(pos, regExp->matchedLength());
+ lengthSearched += nodeString.length();
+ }
+ }
+
+ // If we started in a cell, but bailed because we found the start of the form or the
+ // previous element, we still might need to search the row above us for a label.
+ if (startingTableCell && !searchedCellAbove) {
+ return searchForLabelsAboveCell(regExp.get(), startingTableCell);
+ }
+ return String();
+}
+
+String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
+{
+ String name = element->getAttribute(nameAttr);
+ // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
+ replace(name, RegularExpression("\\d"), " ");
+ name.replace('_', ' ');
+
+ OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
+ // Use the largest match we can find in the whole name string
+ int pos;
+ int length;
+ int bestPos = -1;
+ int bestLength = -1;
+ int start = 0;
+ do {
+ pos = regExp->search(name, start);
+ if (pos != -1) {
+ length = regExp->matchedLength();
+ if (length >= bestLength) {
+ bestPos = pos;
+ bestLength = length;
+ }
+ start = pos+1;
+ }
+ } while (pos != -1);
+
+ if (bestPos != -1)
+ return name.substring(bestPos, bestLength);
+ return String();
+}
+
+const Selection& Frame::mark() const
+{
+ return d->m_mark;
+}
+
+void Frame::setMark(const Selection& s)
+{
+ ASSERT(!s.base().node() || s.base().node()->document() == document());
+ ASSERT(!s.extent().node() || s.extent().node()->document() == document());
+ ASSERT(!s.start().node() || s.start().node()->document() == document());
+ ASSERT(!s.end().node() || s.end().node()->document() == document());
+
+ d->m_mark = s;
+}
+
+void Frame::notifyRendererOfSelectionChange(bool userTriggered)
+{
+ RenderObject* renderer = 0;
+ if (selectionController()->rootEditableElement())
+ renderer = selectionController()->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()))
+ static_cast<RenderTextControl*>(renderer)->selectionChanged(userTriggered);
+}
+
+void Frame::invalidateSelection()
+{
+ selectionController()->setNeedsLayout();
+ selectionLayoutChanged();
+}
+
+void Frame::setCaretVisible(bool flag)
+{
+ if (d->m_caretVisible == flag)
+ return;
+ clearCaretRectIfNeeded();
+ d->m_caretVisible = flag;
+ selectionLayoutChanged();
+}
+
+void Frame::clearCaretRectIfNeeded()
+{
+ if (d->m_caretPaint) {
+ d->m_caretPaint = false;
+ selectionController()->invalidateCaretRect();
+ }
+}
+
+// Helper function that tells whether a particular node is an element that has an entire
+// Frame and FrameView, a <frame>, <iframe>, or <object>.
+static bool isFrameElement(const Node *n)
+{
+ if (!n)
+ return false;
+ RenderObject *renderer = n->renderer();
+ if (!renderer || !renderer->isWidget())
+ return false;
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ return widget && widget->isFrameView();
+}
+
+void Frame::setFocusedNodeIfNeeded()
+{
+ if (!document() || selectionController()->isNone() || !selectionController()->isFocusedAndActive())
+ return;
+
+ Node* target = selectionController()->rootEditableElement();
+ if (target) {
+ RenderObject* renderer = target->renderer();
+
+ // Walk up the render tree to search for a node to focus.
+ // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields.
+ while (renderer) {
+ // We don't want to set focus on a subframe when selecting in a parent frame,
+ // so add the !isFrameElement check here. There's probably a better way to make this
+ // work in the long term, but this is the safest fix at this time.
+ if (target && target->isMouseFocusable() && !isFrameElement(target)) {
+ page()->focusController()->setFocusedNode(target, this);
+ return;
+ }
+ renderer = renderer->parent();
+ if (renderer)
+ target = renderer->element();
+ }
+ document()->setFocusedNode(0);
+ }
+}
+
+void Frame::selectionLayoutChanged()
+{
+ bool caretRectChanged = selectionController()->recomputeCaretRect();
+
+ bool shouldBlink = d->m_caretVisible
+ && selectionController()->isCaret() && selectionController()->isContentEditable();
+
+ // If the caret moved, stop the blink timer so we can restart with a
+ // black caret in the new location.
+ if (caretRectChanged || !shouldBlink)
+ d->m_caretBlinkTimer.stop();
+
+ // Start blinking with a black caret. Be sure not to restart if we're
+ // already blinking in the right location.
+ if (shouldBlink && !d->m_caretBlinkTimer.isActive()) {
+ d->m_caretBlinkTimer.startRepeating(theme()->caretBlinkFrequency());
+ if (!d->m_caretPaint) {
+ d->m_caretPaint = true;
+ selectionController()->invalidateCaretRect();
+ }
+ }
+
+ if (!renderer())
+ return;
+ RenderView* canvas = static_cast<RenderView*>(renderer());
+
+ Selection selection = selectionController()->selection();
+
+ if (!selection.isRange())
+ canvas->clearSelection();
+ else {
+ // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
+ // 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();
+ if (startPos.downstream().isCandidate())
+ startPos = startPos.downstream();
+ Position endPos = selection.visibleEnd().deepEquivalent();
+ if (endPos.upstream().isCandidate())
+ endPos = endPos.upstream();
+
+ // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
+ // because we don't yet notify the SelectionController of text removal.
+ if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
+ RenderObject *startRenderer = startPos.node()->renderer();
+ RenderObject *endRenderer = endPos.node()->renderer();
+ canvas->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
+ }
+ }
+}
+
+void Frame::caretBlinkTimerFired(Timer<Frame>*)
+{
+ ASSERT(d->m_caretVisible);
+ ASSERT(selectionController()->isCaret());
+ bool caretPaint = d->m_caretPaint;
+ if (selectionController()->isCaretBlinkingSuspended() && caretPaint)
+ return;
+ d->m_caretPaint = !caretPaint;
+ selectionController()->invalidateCaretRect();
+}
+
+void Frame::paintCaret(GraphicsContext* p, const IntRect& rect) const
+{
+ if (d->m_caretPaint && d->m_caretVisible)
+ selectionController()->paintCaret(p, rect);
+}
+
+void Frame::paintDragCaret(GraphicsContext* p, const IntRect& rect) const
+{
+ SelectionController* dragCaretController = d->m_page->dragCaretController();
+ ASSERT(dragCaretController->selection().isCaret());
+ if (dragCaretController->selection().start().node()->document()->frame() == this)
+ dragCaretController->paintCaret(p, rect);
+}
+
+int Frame::zoomFactor() const
+{
+ return d->m_zoomFactor;
+}
+
+void Frame::setZoomFactor(int percent)
+{
+ if (d->m_zoomFactor == percent)
+ return;
+
+#if ENABLE(SVG)
+ if (d->m_doc && d->m_doc->isSVGDocument()) {
+ if (!static_cast<SVGDocument*>(d->m_doc.get())->zoomAndPanEnabled())
+ return;
+ d->m_zoomFactor = percent;
+ 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);
+
+ if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->needsLayout())
+ view()->layout();
+}
+
+void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
+{
+ if (!d->m_doc)
+ return;
+
+ d->m_doc->setPrinting(printing);
+ view()->setMediaType(printing ? "print" : "screen");
+ d->m_doc->updateStyleSelector();
+ forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
+
+ for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
+ child->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize);
+}
+
+void Frame::setJSStatusBarText(const String& text)
+{
+ d->m_kjsStatusBarText = text;
+ if (d->m_page)
+ d->m_page->chrome()->setStatusbarText(this, d->m_kjsStatusBarText);
+}
+
+void Frame::setJSDefaultStatusBarText(const String& text)
+{
+ d->m_kjsDefaultStatusBarText = text;
+ if (d->m_page)
+ d->m_page->chrome()->setStatusbarText(this, d->m_kjsDefaultStatusBarText);
+}
+
+String Frame::jsStatusBarText() const
+{
+ return d->m_kjsStatusBarText;
+}
+
+String Frame::jsDefaultStatusBarText() const
+{
+ return d->m_kjsDefaultStatusBarText;
+}
+
+void Frame::setNeedsReapplyStyles()
+{
+ if (d->m_needsReapplyStyles)
+ return;
+
+ d->m_needsReapplyStyles = true;
+
+ // Invalidate the FrameView so that FrameView::layout will get called,
+ // which calls reapplyStyles.
+ view()->invalidate();
+}
+
+bool Frame::needsReapplyStyles() const
+{
+ return d->m_needsReapplyStyles;
+}
+
+void Frame::reapplyStyles()
+{
+ d->m_needsReapplyStyles = false;
+
+ // FIXME: This call doesn't really make sense in a method called
+ // "reapplyStyles". We should probably eventually move it into its own
+ // method.
+ if (d->m_doc)
+ d->m_doc->docLoader()->setAutoLoadImages(d->m_page && d->m_page->settings()->loadsImagesAutomatically());
+
+#if FRAME_LOADS_USER_STYLESHEET
+ const KURL userStyleSheetLocation = d->m_page ? d->m_page->settings()->userStyleSheetLocation() : KURL();
+ if (!userStyleSheetLocation.isEmpty())
+ setUserStyleSheetLocation(userStyleSheetLocation);
+ else
+ setUserStyleSheet(String());
+#endif
+
+ // FIXME: It's not entirely clear why the following is needed.
+ // The document automatically does this as required when you set the style sheet.
+ // But we had problems when this code was removed. Details are in
+ // <http://bugs.webkit.org/show_bug.cgi?id=8079>.
+ if (d->m_doc)
+ d->m_doc->updateStyleSelector();
+}
+
+bool Frame::shouldChangeSelection(const Selection& newSelection) const
+{
+ return shouldChangeSelection(selectionController()->selection(), newSelection, newSelection.affinity(), false);
+}
+
+bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
+{
+ return editor()->client()->shouldChangeSelectedRange(oldSelection.toRange().get(), newSelection.toRange().get(),
+ affinity, stillSelecting);
+}
+
+bool Frame::shouldDeleteSelection(const Selection& selection) const
+{
+ return editor()->client()->shouldDeleteRange(selection.toRange().get());
+}
+
+bool Frame::isContentEditable() const
+{
+ if (d->m_editor.clientIsEditable())
+ return true;
+ if (!d->m_doc)
+ return false;
+ return d->m_doc->inDesignMode();
+}
+
+#if !PLATFORM(MAC)
+
+void Frame::setUseSecureKeyboardEntry(bool)
+{
+}
+
+#endif
+
+void Frame::updateSecureKeyboardEntryIfActive()
+{
+ if (selectionController()->isFocusedAndActive())
+ setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
+}
+
+CSSMutableStyleDeclaration *Frame::typingStyle() const
+{
+ return d->m_typingStyle.get();
+}
+
+void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
+{
+ d->m_typingStyle = style;
+}
+
+void Frame::clearTypingStyle()
+{
+ d->m_typingStyle = 0;
+}
+
+void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction)
+{
+ if (!style || style->length() == 0) {
+ clearTypingStyle();
+ return;
+ }
+
+ // Calculate the current typing style.
+ RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
+ if (typingStyle()) {
+ typingStyle()->merge(mutableStyle.get());
+ mutableStyle = typingStyle();
+ }
+
+ Node *node = selectionController()->selection().visibleStart().deepEquivalent().node();
+ CSSComputedStyleDeclaration computedStyle(node);
+ computedStyle.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));
+
+ // Set the remaining style as the typing style.
+ d->m_typingStyle = mutableStyle.release();
+}
+
+String Frame::selectionStartStylePropertyValue(int stylePropertyID) const
+{
+ Node *nodeToRemove;
+ RefPtr<CSSStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
+ if (!selectionStyle)
+ return String();
+
+ String value = selectionStyle->getPropertyValue(stylePropertyID);
+
+ if (nodeToRemove) {
+ ExceptionCode ec = 0;
+ nodeToRemove->remove(ec);
+ ASSERT(ec == 0);
+ }
+
+ return value;
+}
+
+CSSComputedStyleDeclaration *Frame::selectionComputedStyle(Node *&nodeToRemove) const
+{
+ nodeToRemove = 0;
+
+ if (!document())
+ return 0;
+
+ if (selectionController()->isNone())
+ return 0;
+
+ RefPtr<Range> range(selectionController()->toRange());
+ Position pos = range->editingStartPosition();
+
+ Element *elem = pos.element();
+ if (!elem)
+ return 0;
+
+ RefPtr<Element> styleElement = elem;
+ ExceptionCode ec = 0;
+
+ if (d->m_typingStyle) {
+ styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
+ ASSERT(ec == 0);
+
+ styleElement->setAttribute(styleAttr, d->m_typingStyle->cssText().impl(), ec);
+ ASSERT(ec == 0);
+
+ styleElement->appendChild(document()->createEditingTextNode(""), ec);
+ ASSERT(ec == 0);
+
+ if (elem->renderer() && elem->renderer()->canHaveChildren()) {
+ elem->appendChild(styleElement, ec);
+ } else {
+ Node *parent = elem->parent();
+ Node *next = elem->nextSibling();
+
+ if (next) {
+ parent->insertBefore(styleElement, next, ec);
+ } else {
+ parent->appendChild(styleElement, ec);
+ }
+ }
+ ASSERT(ec == 0);
+
+ nodeToRemove = styleElement.get();
+ }
+
+ return new CSSComputedStyleDeclaration(styleElement);
+}
+
+void Frame::textFieldDidBeginEditing(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textFieldDidBeginEditing(e);
+}
+
+void Frame::textFieldDidEndEditing(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textFieldDidEndEditing(e);
+}
+
+void Frame::textDidChangeInTextField(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textDidChangeInTextField(e);
+}
+
+bool Frame::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
+{
+ if (editor()->client())
+ return editor()->client()->doTextFieldCommandFromEvent(e, ke);
+
+ return false;
+}
+
+void Frame::textWillBeDeletedInTextField(Element* input)
+{
+ if (editor()->client())
+ editor()->client()->textWillBeDeletedInTextField(input);
+}
+
+void Frame::textDidChangeInTextArea(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textDidChangeInTextArea(e);
+}
+
+void Frame::applyEditingStyleToBodyElement() const
+{
+ if (!d->m_doc)
+ return;
+
+ RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
+ unsigned len = list->length();
+ for (unsigned i = 0; i < len; i++) {
+ applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
+ }
+}
+
+void Frame::removeEditingStyleFromBodyElement() const
+{
+ if (!d->m_doc)
+ return;
+
+ RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
+ unsigned len = list->length();
+ for (unsigned i = 0; i < len; i++) {
+ removeEditingStyleFromElement(static_cast<Element*>(list->item(i)));
+ }
+}
+
+void Frame::applyEditingStyleToElement(Element* element) const
+{
+ if (!element)
+ return;
+
+ CSSStyleDeclaration* style = element->style();
+ ASSERT(style);
+
+ ExceptionCode ec = 0;
+ style->setProperty(CSS_PROP_WORD_WRAP, "break-word", false, ec);
+ ASSERT(ec == 0);
+ style->setProperty(CSS_PROP__WEBKIT_NBSP_MODE, "space", false, ec);
+ ASSERT(ec == 0);
+ style->setProperty(CSS_PROP__WEBKIT_LINE_BREAK, "after-white-space", false, ec);
+ ASSERT(ec == 0);
+}
+
+void Frame::removeEditingStyleFromElement(Element*) const
+{
+}
+
+#ifndef NDEBUG
+static HashSet<Frame*>& keepAliveSet()
+{
+ static HashSet<Frame*> staticKeepAliveSet;
+ return staticKeepAliveSet;
+}
+#endif
+
+void Frame::keepAlive()
+{
+ if (d->m_lifeSupportTimer.isActive())
+ return;
+#ifndef NDEBUG
+ keepAliveSet().add(this);
+#endif
+ ref();
+ d->m_lifeSupportTimer.startOneShot(0);
+}
+
+#ifndef NDEBUG
+void Frame::cancelAllKeepAlive()
+{
+ HashSet<Frame*>::iterator end = keepAliveSet().end();
+ for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) {
+ Frame* frame = *it;
+ frame->d->m_lifeSupportTimer.stop();
+ frame->deref();
+ }
+ keepAliveSet().clear();
+}
+#endif
+
+void Frame::lifeSupportTimerFired(Timer<Frame>*)
+{
+#ifndef NDEBUG
+ keepAliveSet().remove(this);
+#endif
+ deref();
+}
+
+KJS::Bindings::RootObject* Frame::bindingRootObject()
+{
+ if (!scriptProxy()->isEnabled())
+ return 0;
+
+ if (!d->m_bindingRootObject) {
+ JSLock lock;
+ d->m_bindingRootObject = KJS::Bindings::RootObject::create(0, scriptProxy()->globalObject());
+ }
+ return d->m_bindingRootObject.get();
+}
+
+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()
+{
+ if (d->m_jscript)
+ d->m_jscript->clear();
+}
+
+void Frame::clearDOMWindow()
+{
+ if (d->m_domWindow)
+ d->m_domWindow->clear();
+}
+
+void Frame::cleanupScriptObjectsForPlugin(void* nativeHandle)
+{
+ RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle);
+
+ if (it == d->m_rootObjects.end())
+ return;
+
+ it->second->invalidate();
+ d->m_rootObjects.remove(it);
+}
+
+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();
+}
+
+RenderObject *Frame::renderer() const
+{
+ Document *doc = document();
+ return doc ? doc->renderer() : 0;
+}
+
+HTMLFrameOwnerElement* Frame::ownerElement() const
+{
+ return d->m_ownerElement;
+}
+
+RenderPart* Frame::ownerRenderer()
+{
+ HTMLFrameOwnerElement* ownerElement = d->m_ownerElement;
+ if (!ownerElement)
+ return 0;
+ return static_cast<RenderPart*>(ownerElement->renderer());
+}
+
+// returns FloatRect because going through IntRect would truncate any floats
+FloatRect Frame::selectionRect(bool clipToVisibleContent) const
+{
+ RenderView *root = static_cast<RenderView*>(renderer());
+ if (!root)
+ return IntRect();
+
+ IntRect selectionRect = root->selectionRect(clipToVisibleContent);
+ return clipToVisibleContent ? intersection(selectionRect, d->m_view->visibleContentRect()) : selectionRect;
+}
+
+void Frame::selectionTextRects(Vector<FloatRect>& rects, bool clipToVisibleContent) const
+{
+ RenderView *root = static_cast<RenderView*>(renderer());
+ if (!root)
+ return;
+
+ RefPtr<Range> selectedRange = selectionController()->toRange();
+
+ Vector<IntRect> intRects;
+ selectedRange->addLineBoxRects(intRects, true);
+
+ unsigned size = intRects.size();
+ FloatRect visibleContentRect = d->m_view->visibleContentRect();
+ for (unsigned i = 0; i < size; ++i)
+ if (clipToVisibleContent)
+ rects.append(intersection(intRects[i], visibleContentRect));
+ else
+ rects.append(intRects[i]);
+}
+
+
+bool Frame::isFrameSet() const
+{
+ Document* document = d->m_doc.get();
+ if (!document || !document->isHTMLDocument())
+ return false;
+ Node *body = static_cast<HTMLDocument*>(document)->body();
+ return body && body->renderer() && body->hasTagName(framesetTag);
+}
+
+// Scans logically forward from "start", including any child frames
+static HTMLFormElement *scanForForm(Node *start)
+{
+ Node *n;
+ for (n = start; n; n = n->traverseNextNode()) {
+ if (n->hasTagName(formTag))
+ return static_cast<HTMLFormElement*>(n);
+ else if (n->isHTMLElement() && static_cast<HTMLElement*>(n)->isGenericFormElement())
+ return static_cast<HTMLGenericFormElement*>(n)->form();
+ else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) {
+ Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument();
+ if (HTMLFormElement *frameResult = scanForForm(childDoc))
+ return frameResult;
+ }
+ }
+ return 0;
+}
+
+// We look for either the form containing the current focus, or for one immediately after it
+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();
+
+ // try walking up the node tree to find a form element
+ Node *n;
+ for (n = start; n; n = n->parentNode()) {
+ if (n->hasTagName(formTag))
+ return static_cast<HTMLFormElement*>(n);
+ else if (n->isHTMLElement()
+ && static_cast<HTMLElement*>(n)->isGenericFormElement())
+ return static_cast<HTMLGenericFormElement*>(n)->form();
+ }
+
+ // try walking forward in the node tree to find a form element
+ return start ? scanForForm(start) : 0;
+}
+
+// FIXME: should this go in SelectionController?
+void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const
+{
+ IntRect rect;
+
+ switch (selectionController()->state()) {
+ case Selection::NONE:
+ return;
+
+ case Selection::CARET:
+ rect = selectionController()->caretRect();
+ break;
+
+ case Selection::RANGE:
+ rect = enclosingIntRect(selectionRect(false));
+ break;
+ }
+
+ Position start = selectionController()->start();
+
+ ASSERT(start.node());
+ if (start.node() && start.node()->renderer()) {
+ // FIXME: This code only handles scrolling the startContainer's layer, but
+ // 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);
+ }
+}
+
+void Frame::revealCaret(const RenderLayer::ScrollAlignment& alignment) const
+{
+ if (selectionController()->isNone())
+ return;
+
+ Position extent = selectionController()->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);
+ }
+}
+
+// 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());
+ if (root) {
+ // Use a context with painting disabled.
+ GraphicsContext context((PlatformGraphicsContext*)0);
+ root->setTruncatedAt((int)floorf(oldBottom));
+ IntRect dirtyRect(0, (int)floorf(oldTop), root->docWidth(), (int)ceilf(oldBottom - oldTop));
+ root->layer()->paint(&context, dirtyRect);
+ *newBottom = root->bestTruncatedAt();
+ if (*newBottom == 0)
+ *newBottom = oldBottom;
+ } else
+ *newBottom = oldBottom;
+}
+
+Frame* Frame::frameForWidget(const Widget* widget)
+{
+ ASSERT_ARG(widget, widget);
+
+ if (RenderWidget* renderer = RenderWidget::find(widget))
+ if (Node* node = renderer->node())
+ return node->document()->frame();
+
+ // Assume all widgets are either a FrameView or owned by a RenderWidget.
+ // FIXME: That assumption is not right for scroll bars!
+ ASSERT(widget->isFrameView());
+ return static_cast<const FrameView*>(widget)->frame();
+}
+
+void Frame::forceLayout(bool allowSubtree)
+{
+ FrameView *v = d->m_view.get();
+ if (v) {
+ v->layout(allowSubtree);
+ // We cannot unschedule a pending relayout, since the force can be called with
+ // a tiny rectangle from a drawRect update. By unscheduling we in effect
+ // "validate" and stop the necessary full repaint from occurring. Basically any basic
+ // append/remove DHTML is broken by this call. For now, I have removed the optimization
+ // until we have a better invalidation stategy. -dwh
+ //v->unscheduleRelayout();
+ }
+}
+
+void Frame::forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth, bool adjustViewSize)
+{
+ // Dumping externalRepresentation(m_frame->renderer()).ascii() is a good trick to see
+ // the state of things before and after the layout
+ RenderView *root = static_cast<RenderView*>(document()->renderer());
+ if (root) {
+ // This magic is basically copied from khtmlview::print
+ int pageW = (int)ceilf(minPageWidth);
+ root->setWidth(pageW);
+ root->setNeedsLayoutAndPrefWidthsRecalc();
+ forceLayout();
+
+ // If we don't fit in the minimum page width, we'll lay out again. If we don't fit in the
+ // maximum page width, we will lay out to the maximum page width and clip extra content.
+ // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
+ // implementation should not do this!
+ int rightmostPos = root->rightmostPosition();
+ if (rightmostPos > minPageWidth) {
+ pageW = min(rightmostPos, (int)ceilf(maxPageWidth));
+ root->setWidth(pageW);
+ root->setNeedsLayoutAndPrefWidthsRecalc();
+ forceLayout();
+ }
+ }
+
+ if (adjustViewSize && view())
+ view()->adjustViewSize();
+}
+
+void Frame::sendResizeEvent()
+{
+ if (Document* doc = document())
+ doc->dispatchWindowEvent(EventNames::resizeEvent, false, false);
+}
+
+void Frame::sendScrollEvent()
+{
+ FrameView* v = d->m_view.get();
+ if (!v)
+ return;
+ v->setWasScrolledByUser(true);
+ Document* doc = document();
+ if (!doc)
+ return;
+ doc->dispatchHTMLEvent(scrollEvent, true, false);
+}
+
+void Frame::clearTimers(FrameView *view)
+{
+ 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();
+ }
+ }
+}
+
+void Frame::clearTimers()
+{
+ clearTimers(d->m_view.get());
+}
+
+RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const
+{
+ nodeToRemove = 0;
+
+ if (!document())
+ return 0;
+ if (selectionController()->isNone())
+ return 0;
+
+ Position pos = selectionController()->selection().visibleStart().deepEquivalent();
+ if (!pos.isCandidate())
+ return 0;
+ Node *node = pos.node();
+ if (!node)
+ return 0;
+
+ if (!d->m_typingStyle)
+ return node->renderer()->style();
+
+ ExceptionCode ec = 0;
+ RefPtr<Element> styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
+ ASSERT(ec == 0);
+
+ String styleText = d->m_typingStyle->cssText() + " display: inline";
+ styleElement->setAttribute(styleAttr, styleText.impl(), ec);
+ ASSERT(ec == 0);
+
+ styleElement->appendChild(document()->createEditingTextNode(""), ec);
+ ASSERT(ec == 0);
+
+ node->parentNode()->appendChild(styleElement, ec);
+ ASSERT(ec == 0);
+
+ nodeToRemove = styleElement.get();
+ return styleElement->renderer() ? styleElement->renderer()->style() : 0;
+}
+
+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())
+ return;
+
+ Node* node = doc->documentElement();
+ while (node && !node->hasTagName(bodyTag))
+ node = node->traverseNextNode();
+ if (node)
+ selectionController()->setSelection(Selection(Position(node, 0), DOWNSTREAM));
+}
+
+bool Frame::inViewSourceMode() const
+{
+ return d->m_inViewSourceMode;
+}
+
+void Frame::setInViewSourceMode(bool mode) const
+{
+ d->m_inViewSourceMode = mode;
+}
+
+UChar Frame::backslashAsCurrencySymbol() const
+{
+ Document *doc = document();
+ if (!doc)
+ return '\\';
+ TextResourceDecoder *decoder = doc->decoder();
+ if (!decoder)
+ return '\\';
+
+ 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;
+
+ // 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);
+
+ if (selectionIsInMainContent) {
+ if (forward)
+ setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
+ else
+ setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
+ }
+ 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()) {
+ searchRange = rangeOfContents(document());
+ if (forward)
+ setStart(searchRange.get(), selection.visibleEnd());
+ else
+ setEnd(searchRange.get(), selection.visibleStart());
+ resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+ }
+
+ int exception = 0;
+
+ // 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) {
+ searchRange = rangeOfContents(document());
+ resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+ // We used to return false here if we ended up with the same range that we started with
+ // (e.g., the selection was already the only instance of this text). But we decided that
+ // this should be a success case instead, so we'll just fall through in that case.
+ }
+
+ if (resultRange->collapsed(exception))
+ return false;
+
+ selectionController()->setSelection(Selection(resultRange.get(), DOWNSTREAM));
+ revealSelection();
+ return true;
+}
+
+unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit)
+{
+ if (target.isEmpty() || !document())
+ return 0;
+
+ RefPtr<Range> searchRange(rangeOfContents(document()));
+
+ int exception = 0;
+ unsigned matchCount = 0;
+ do {
+ RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag));
+ 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;
+
+ ++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);
+ } 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()) {
+ 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);
+ }
+
+ return matchCount;
+}
+
+bool Frame::markedTextMatchesAreHighlighted() const
+{
+ return d->m_highlightTextMatches;
+}
+
+void Frame::setMarkedTextMatchesAreHighlighted(bool flag)
+{
+ if (flag == d->m_highlightTextMatches || !document())
+ return;
+
+ d->m_highlightTextMatches = flag;
+ document()->repaintMarkers(DocumentMarker::TextMatch);
+}
+
+FrameTree* Frame::tree() const
+{
+ return &d->m_treeNode;
+}
+
+DOMWindow* Frame::domWindow() const
+{
+ if (!d->m_domWindow)
+ d->m_domWindow = DOMWindow::create(const_cast<Frame*>(this));
+
+ return d->m_domWindow.get();
+}
+
+Page* Frame::page() const
+{
+ return d->m_page;
+}
+
+EventHandler* Frame::eventHandler() const
+{
+ return &d->m_eventHandler;
+}
+
+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);
+
+ // This will stop any JS timers
+ if (d->m_jscript && d->m_jscript->haveGlobalObject())
+ if (KJS::Window* w = KJS::Window::retrieveWindow(this))
+ w->disconnectFrame();
+
+ clearScriptObjects();
+
+ d->m_page = 0;
+}
+
+void Frame::disconnectOwnerElement()
+{
+ if (d->m_ownerElement) {
+ d->m_ownerElement->m_contentFrame = 0;
+ if (d->m_page)
+ d->m_page->decrementFrameCount();
+ }
+ d->m_ownerElement = 0;
+}
+
+String Frame::documentTypeString() const
+{
+ if (Document *doc = document())
+ if (DocumentType *doctype = doc->doctype())
+ return doctype->toString();
+
+ return String();
+}
+
+bool Frame::prohibitsScrolling() const
+{
+ return d->m_prohibitsScrolling;
+}
+
+void Frame::setProhibitsScrolling(bool prohibit)
+{
+ d->m_prohibitsScrolling = prohibit;
+}
+
+void Frame::focusWindow()
+{
+ if (!page())
+ return;
+
+ // If we're a top level window, bring the window to the front.
+ if (!tree()->parent())
+ page()->chrome()->focus();
+
+ eventHandler()->focusDocumentView();
+}
+
+void Frame::unfocusWindow()
+{
+ if (!page())
+ return;
+
+ // If we're a top level window, deactivate the window.
+ if (!tree()->parent())
+ page()->chrome()->unfocus();
+}
+
+bool Frame::shouldClose()
+{
+ Chrome* chrome = page() ? page()->chrome() : 0;
+ if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel())
+ return true;
+
+ RefPtr<Document> doc = document();
+ if (!doc)
+ return true;
+ HTMLElement* body = doc->body();
+ if (!body)
+ return true;
+
+ RefPtr<BeforeUnloadEvent> beforeUnloadEvent = new BeforeUnloadEvent;
+ beforeUnloadEvent->setTarget(doc);
+ doc->handleWindowEvent(beforeUnloadEvent.get(), false);
+
+ if (!beforeUnloadEvent->defaultPrevented() && doc)
+ doc->defaultEventHandler(beforeUnloadEvent.get());
+ if (beforeUnloadEvent->result().isNull())
+ return true;
+
+ String text = beforeUnloadEvent->result();
+ text.replace('\\', backslashAsCurrencySymbol());
+
+ return chrome->runBeforeUnloadConfirmPanel(text, this);
+}
+
+void Frame::scheduleClose()
+{
+ if (!shouldClose())
+ return;
+
+ Chrome* chrome = page() ? page()->chrome() : 0;
+ if (chrome)
+ chrome->closeWindowSoon();
+}
+
+void Frame::respondToChangedSelection(const Selection& oldSelection, bool closeTyping)
+{
+ if (document()) {
+ bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled();
+ bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled();
+ if (isContinuousSpellCheckingEnabled) {
+ Selection newAdjacentWords;
+ Selection newSelectedSentence;
+ if (selectionController()->selection().isContentEditable()) {
+ VisiblePosition newStart(selectionController()->selection().visibleStart());
+ newAdjacentWords = Selection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
+ if (isContinuousGrammarCheckingEnabled)
+ newSelectedSentence = Selection(startOfSentence(newStart), endOfSentence(newStart));
+ }
+
+ // When typing we check spelling elsewhere, so don't redo it here.
+ // If this is a change in selection resulting from a delete operation,
+ // oldSelection may no longer be in the document.
+ if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
+ VisiblePosition oldStart(oldSelection.visibleStart());
+ Selection oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
+ if (oldAdjacentWords != newAdjacentWords) {
+ editor()->markMisspellings(oldAdjacentWords);
+ if (isContinuousGrammarCheckingEnabled) {
+ Selection oldSelectedSentence = Selection(startOfSentence(oldStart), endOfSentence(oldStart));
+ if (oldSelectedSentence != newSelectedSentence)
+ editor()->markBadGrammar(oldSelectedSentence);
+ }
+ }
+ }
+
+ // This only erases markers that are in the first unit (word or sentence) of the selection.
+ // Perhaps peculiar, but it matches AppKit.
+ if (RefPtr<Range> wordRange = newAdjacentWords.toRange())
+ document()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
+ if (RefPtr<Range> sentenceRange = newSelectedSentence.toRange())
+ document()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
+ }
+
+ // When continuous spell checking is off, existing markers disappear after the selection changes.
+ if (!isContinuousSpellCheckingEnabled)
+ document()->removeMarkers(DocumentMarker::Spelling);
+ if (!isContinuousGrammarCheckingEnabled)
+ document()->removeMarkers(DocumentMarker::Grammar);
+ }
+
+ editor()->respondToChangedSelection(oldSelection);
+}
+
+VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint)
+{
+ HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, 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;
+}
+
+Document* Frame::documentAtPoint(const IntPoint& point)
+{
+ if (!view())
+ return 0;
+
+ IntPoint pt = view()->windowToContents(point);
+ HitTestResult result = HitTestResult(pt);
+
+ if (renderer())
+ result = eventHandler()->hitTestResultAtPoint(pt, false);
+ return result.innerNode() ? result.innerNode()->document() : 0;
+}
+
+FramePrivate::FramePrivate(Page* page, Frame* parent, Frame* thisFrame, HTMLFrameOwnerElement* ownerElement,
+ FrameLoaderClient* frameLoaderClient)
+ : m_page(page)
+ , m_treeNode(thisFrame, parent)
+ , m_ownerElement(ownerElement)
+ , m_jscript(0)
+ , m_zoomFactor(parent ? parent->d->m_zoomFactor : 100)
+ , m_selectionGranularity(CharacterGranularity)
+ , m_selectionController(thisFrame)
+ , m_caretBlinkTimer(thisFrame, &Frame::caretBlinkTimerFired)
+ , m_editor(thisFrame)
+ , m_eventHandler(thisFrame)
+ , m_animationController(thisFrame)
+ , 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)
+#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;
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/Frame.h b/WebCore/page/Frame.h
new file mode 100644
index 0000000..24c9903
--- /dev/null
+++ b/WebCore/page/Frame.h
@@ -0,0 +1,391 @@
+// -*- c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ * 1999-2001 Lars Knoll <knoll@kde.org>
+ * 1999-2001 Antti Koivisto <koivisto@kde.org>
+ * 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
+ *
+ * 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 Frame_h
+#define Frame_h
+
+#include "Color.h"
+#include "EditAction.h"
+#include "DragImage.h"
+#include "RenderLayer.h"
+#include "TextGranularity.h"
+#include "VisiblePosition.h"
+#include <wtf/unicode/Unicode.h>
+#include <wtf/Forward.h>
+#include <wtf/Vector.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
+class NSArray;
+class NSDictionary;
+class NSMenu;
+class NSMutableDictionary;
+class NSString;
+class WebCoreFrameBridge;
+class WebScriptObject;
+typedef int NSWritingDirection;
+#endif
+#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 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();
+
+#if PLATFORM(MAC)
+ void setBridge(WebCoreFrameBridge*);
+ WebCoreFrameBridge* bridge() const;
+#endif
+
+ Page* page() const;
+ HTMLFrameOwnerElement* ownerElement() const;
+
+ void pageDestroyed();
+ void disconnectOwnerElement();
+
+ Document* document() const;
+ FrameView* view() const;
+
+ DOMWindow* domWindow() const;
+ Editor* editor() const;
+ EventHandler* eventHandler() const;
+ FrameLoader* loader() const;
+ SelectionController* selectionController() const;
+ FrameTree* tree() const;
+ AnimationController* animationController() const;
+
+ // 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
+
+ friend class FramePrivate;
+
+private:
+ FramePrivate* d;
+
+// === undecided, would like to consider moving to another class
+
+public:
+ static Frame* frameForWidget(const Widget*);
+
+ Settings* settings() const; // can be NULL
+
+#if FRAME_LOADS_USER_STYLESHEET
+ void setUserStyleSheetLocation(const KURL&);
+ void setUserStyleSheet(const String& styleSheetData);
+#endif
+
+ void setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize);
+
+ bool inViewSourceMode() const;
+ void setInViewSourceMode(bool = true) const;
+
+ void keepAlive(); // Used to keep the frame alive when running a script that might destroy it.
+#ifndef NDEBUG
+ 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*);
+
+ // Convenience, to avoid repeating the code to dig down to get this.
+ UChar backslashAsCurrencySymbol() const;
+
+ void setNeedsReapplyStyles();
+ bool needsReapplyStyles() const;
+ void reapplyStyles();
+
+ String documentTypeString() const;
+
+ void dashboardRegionsChanged();
+
+ void clearScriptProxy();
+ void clearDOMWindow();
+
+ void clearScriptObjects();
+ void cleanupScriptObjectsForPlugin(void*);
+
+private:
+ void clearPlatformScriptObjects();
+
+ void lifeSupportTimerFired(Timer<Frame>*);
+
+// === to be moved into Document
+
+public:
+ bool isFrameSet() const;
+
+// === to be moved into EventHandler
+
+public:
+ void sendResizeEvent();
+ void sendScrollEvent();
+
+// === 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
+
+ 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
+
+// === to be moved into Chrome
+
+public:
+ void focusWindow();
+ void unfocusWindow();
+ bool shouldClose();
+ void scheduleClose();
+
+ void setJSStatusBarText(const String&);
+ void setJSDefaultStatusBarText(const String&);
+ String jsStatusBarText() const;
+ String jsDefaultStatusBarText() const;
+
+// === to be moved into Editor
+
+public:
+ String selectedText() const;
+ bool findString(const String&, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection);
+
+ const Selection& mark() const; // Mark, to be used as emacs uses it.
+ void setMark(const Selection&);
+
+ void computeAndSetTypingStyle(CSSStyleDeclaration* , EditAction = EditActionUnspecified);
+ String selectionStartStylePropertyValue(int stylePropertyID) const;
+ void applyEditingStyleToBodyElement() const;
+ void removeEditingStyleFromBodyElement() const;
+ void applyEditingStyleToElement(Element*) const;
+ void removeEditingStyleFromElement(Element*) const;
+
+ IntRect firstRectForRange(Range*) const;
+
+ void respondToChangedSelection(const Selection& oldSelection, bool closeTyping);
+ bool shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity, bool stillSelecting) const;
+
+ RenderStyle* styleForSelectionStart(Node*& nodeToRemove) const;
+
+ unsigned markAllMatchesForText(const String&, bool caseFlag, unsigned limit);
+ bool markedTextMatchesAreHighlighted() const;
+ void setMarkedTextMatchesAreHighlighted(bool flag);
+
+ CSSComputedStyleDeclaration* selectionComputedStyle(Node*& nodeToRemove) const;
+
+ void textFieldDidBeginEditing(Element*);
+ void textFieldDidEndEditing(Element*);
+ void textDidChangeInTextField(Element*);
+ bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*);
+ void textWillBeDeletedInTextField(Element* input);
+ void textDidChangeInTextArea(Element*);
+
+ DragImageRef dragImageForSelection();
+
+// === to be moved into SelectionController
+
+public:
+ TextGranularity selectionGranularity() const;
+ void setSelectionGranularity(TextGranularity) const;
+
+ bool shouldChangeSelection(const Selection&) const;
+ bool shouldDeleteSelection(const Selection&) const;
+ void clearCaretRectIfNeeded();
+ void setFocusedNodeIfNeeded();
+ void selectionLayoutChanged();
+ void notifyRendererOfSelectionChange(bool userTriggered);
+
+ void invalidateSelection();
+
+ void setCaretVisible(bool = true);
+ void paintCaret(GraphicsContext*, const IntRect&) const;
+ void paintDragCaret(GraphicsContext*, const IntRect&) const;
+
+ bool isContentEditable() const; // if true, everything in frame is editable
+
+ void updateSecureKeyboardEntryIfActive();
+
+ CSSMutableStyleDeclaration* typingStyle() const;
+ void setTypingStyle(CSSMutableStyleDeclaration*);
+ void clearTypingStyle();
+
+ FloatRect selectionRect(bool clipToVisibleContent = true) const;
+ void selectionTextRects(Vector<FloatRect>&, bool clipToVisibleContent = true) const;
+
+ HTMLFormElement* currentForm() const;
+
+ void revealSelection(const RenderLayer::ScrollAlignment& = RenderLayer::gAlignCenterIfNeeded) const;
+ void revealCaret(const RenderLayer::ScrollAlignment& = RenderLayer::gAlignCenterIfNeeded) const;
+ void setSelectionFromNone();
+
+ void setUseSecureKeyboardEntry(bool);
+
+private:
+ void caretBlinkTimerFired(Timer<Frame>*);
+
+public:
+ SelectionController* dragCaretController() const;
+
+ String searchForLabelsAboveCell(RegularExpression*, HTMLTableCellElement*);
+ String searchForLabelsBeforeElement(const Vector<String>& labels, Element*);
+ String matchLabelsAgainstElement(const Vector<String>& labels, Element*);
+
+ VisiblePosition visiblePositionForPoint(const IntPoint& framePoint);
+ Document* documentAtPoint(const IntPoint& windowPoint);
+
+#if PLATFORM(MAC)
+
+// === undecided, would like to consider moving to another class
+
+public:
+ NSString* searchForNSLabelsAboveCell(RegularExpression*, HTMLTableCellElement*);
+ NSString* searchForLabelsBeforeElement(NSArray* labels, Element*);
+ NSString* matchLabelsAgainstElement(NSArray* labels, Element*);
+
+ NSMutableDictionary* dashboardRegionsDictionary();
+
+ void willPopupMenu(NSMenu*);
+
+ NSImage* selectionImage(bool forceBlackText = false) const;
+ NSImage* snapshotDragImage(Node*, NSRect* imageRect, NSRect* elementRect) 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
+
+};
+
+} // namespace WebCore
+
+#endif // Frame_h
diff --git a/WebCore/page/FrameLoadRequest.h b/WebCore/page/FrameLoadRequest.h
new file mode 100644
index 0000000..c916a05
--- /dev/null
+++ b/WebCore/page/FrameLoadRequest.h
@@ -0,0 +1,74 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, 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 FrameLoadRequest_h
+#define FrameLoadRequest_h
+
+#include "ResourceRequest.h"
+
+namespace WebCore {
+
+ struct FrameLoadRequest {
+ public:
+ FrameLoadRequest()
+ : m_lockHistory(false)
+ {
+ }
+
+ FrameLoadRequest(const ResourceRequest& resourceRequest)
+ : m_resourceRequest(resourceRequest)
+ , m_lockHistory(false)
+ {
+ }
+
+ FrameLoadRequest(const ResourceRequest& resourceRequest, const String& frameName)
+ : m_resourceRequest(resourceRequest)
+ , m_frameName(frameName)
+ , m_lockHistory(false)
+ {
+ }
+
+ bool isEmpty() const { return m_resourceRequest.isEmpty(); }
+
+ ResourceRequest& resourceRequest() { return m_resourceRequest; }
+ const ResourceRequest& resourceRequest() const { return m_resourceRequest; }
+
+ const String& frameName() const { return m_frameName; }
+ void setFrameName(const String& frameName) { m_frameName = frameName; }
+
+ bool lockHistory() const { return m_lockHistory; }
+ void setLockHistory(bool lock) { m_lockHistory = lock; }
+
+ private:
+ ResourceRequest m_resourceRequest;
+ String m_frameName;
+ bool m_lockHistory;
+ };
+
+}
+
+#endif // FrameLoadRequest_h
+
diff --git a/WebCore/page/FramePrivate.h b/WebCore/page/FramePrivate.h
new file mode 100644
index 0000000..c780db2
--- /dev/null
+++ b/WebCore/page/FramePrivate.h
@@ -0,0 +1,134 @@
+/* Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ * 1999-2001 Lars Knoll <knoll@kde.org>
+ * 1999-2001 Antti Koivisto <koivisto@kde.org>
+ * 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
+ *
+ * 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 FramePrivate_h
+#define FramePrivate_h
+
+#include "AnimationController.h"
+#include "Editor.h"
+#include "EventHandler.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
+
+#if PLATFORM(WIN)
+#include "FrameWin.h"
+#endif
+
+namespace WebCore {
+
+#if FRAME_LOADS_USER_STYLESHEET
+ class UserStyleSheetLoader;
+#endif
+
+ typedef HashMap<void*, RefPtr<KJS::Bindings::RootObject> > RootObjectMap;
+
+ class FramePrivate {
+ public:
+ FramePrivate(Page*, Frame* parent, Frame* thisFrame, HTMLFrameOwnerElement*, FrameLoaderClient*);
+ ~FramePrivate();
+
+ Page* m_page;
+ FrameTree m_treeNode;
+ RefPtr<DOMWindow> m_domWindow;
+
+ HTMLFrameOwnerElement* m_ownerElement;
+ RefPtr<FrameView> m_view;
+ RefPtr<Document> m_doc;
+
+ KJSProxy* m_jscript;
+
+ String m_kjsStatusBarText;
+ String m_kjsDefaultStatusBarText;
+
+ int m_zoomFactor;
+
+ TextGranularity m_selectionGranularity;
+
+ SelectionController m_selectionController;
+ Selection m_mark;
+ Timer<Frame> m_caretBlinkTimer;
+ Editor m_editor;
+ 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_highlightTextMatches;
+
+ bool m_inViewSourceMode;
+
+ unsigned frameCount;
+
+ bool m_prohibitsScrolling;
+
+ bool m_needsReapplyStyles;
+
+ // 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
+ };
+}
+
+#endif
diff --git a/WebCore/page/FrameTree.cpp b/WebCore/page/FrameTree.cpp
new file mode 100644
index 0000000..0831be8
--- /dev/null
+++ b/WebCore/page/FrameTree.cpp
@@ -0,0 +1,296 @@
+// -*- c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2006 Apple Computer, 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 "FrameTree.h"
+
+#include "Frame.h"
+#include "Page.h"
+#include <stdarg.h>
+#include <wtf/Platform.h>
+#include <wtf/StringExtras.h>
+#include <wtf/Vector.h>
+
+using std::swap;
+
+namespace WebCore {
+
+FrameTree::~FrameTree()
+{
+ for (Frame* child = firstChild(); child; child = child->tree()->nextSibling())
+ child->setView(0);
+}
+
+void FrameTree::setName(const AtomicString& name)
+{
+ if (!parent()) {
+ m_name = name;
+ return;
+ }
+ m_name = AtomicString(); // Remove our old frame name so it's not considered in uniqueChildName.
+ m_name = parent()->tree()->uniqueChildName(name);
+}
+
+void FrameTree::appendChild(PassRefPtr<Frame> child)
+{
+ ASSERT(child->page() == m_thisFrame->page());
+ child->tree()->m_parent = m_thisFrame;
+
+ Frame* oldLast = m_lastChild;
+ m_lastChild = child.get();
+
+ if (oldLast) {
+ child->tree()->m_previousSibling = oldLast;
+ oldLast->tree()->m_nextSibling = child;
+ } else
+ m_firstChild = child;
+
+ m_childCount++;
+
+ ASSERT(!m_lastChild->tree()->m_nextSibling);
+}
+
+void FrameTree::removeChild(Frame* child)
+{
+ child->tree()->m_parent = 0;
+ child->setView(0);
+ if (child->ownerElement())
+ child->page()->decrementFrameCount();
+ child->pageDestroyed();
+
+ // Slightly tricky way to prevent deleting the child until we are done with it, w/o
+ // extra refs. These swaps leave the child in a circular list by itself. Clearing its
+ // previous and next will then finally deref it.
+
+ RefPtr<Frame>& newLocationForNext = m_firstChild == child ? m_firstChild : child->tree()->m_previousSibling->tree()->m_nextSibling;
+ Frame*& newLocationForPrevious = m_lastChild == child ? m_lastChild : child->tree()->m_nextSibling->tree()->m_previousSibling;
+ swap(newLocationForNext, child->tree()->m_nextSibling);
+ // For some inexplicable reason, the following line does not compile without the explicit std:: namepsace
+ std::swap(newLocationForPrevious, child->tree()->m_previousSibling);
+
+ child->tree()->m_previousSibling = 0;
+ child->tree()->m_nextSibling = 0;
+
+ m_childCount--;
+}
+
+AtomicString FrameTree::uniqueChildName(const AtomicString& requestedName) const
+{
+ if (!requestedName.isEmpty() && !child(requestedName) && requestedName != "_blank")
+ return requestedName;
+
+ // Create a repeatable name for a child about to be added to us. The name must be
+ // unique within the frame tree. The string we generate includes a "path" of names
+ // from the root frame down to us. For this path to be unique, each set of siblings must
+ // contribute a unique name to the path, which can't collide with any HTML-assigned names.
+ // We generate this path component by index in the child list along with an unlikely
+ // frame name that can't be set in HTML because it collides with comment syntax.
+
+ const char framePathPrefix[] = "<!--framePath ";
+ const int framePathPrefixLength = 14;
+ const int framePathSuffixLength = 3;
+
+ // Find the nearest parent that has a frame with a path in it.
+ Vector<Frame*, 16> chain;
+ Frame* frame;
+ for (frame = m_thisFrame; frame; frame = frame->tree()->parent()) {
+ if (frame->tree()->name().startsWith(framePathPrefix))
+ break;
+ chain.append(frame);
+ }
+ String name;
+ name += framePathPrefix;
+ if (frame)
+ 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];
+ name += "/";
+ name += frame->tree()->name();
+ }
+
+ // Suffix buffer has more than enough space for:
+ // 10 characters before the number
+ // a number (3 digits for the highest this gets in practice, 20 digits for the largest 64-bit integer)
+ // 6 characters after the number
+ // trailing null byte
+ // But we still use snprintf just to be extra-safe.
+ char suffix[40];
+ snprintf(suffix, sizeof(suffix), "/<!--frame%u-->-->", childCount());
+
+ name += suffix;
+
+ return AtomicString(name);
+}
+
+Frame* FrameTree::child(unsigned index) const
+{
+ Frame* result = firstChild();
+ for (unsigned i = 0; result && i != index; ++i)
+ result = result->tree()->nextSibling();
+ return result;
+}
+
+Frame* FrameTree::child(const AtomicString& name) const
+{
+ for (Frame* child = firstChild(); child; child = child->tree()->nextSibling())
+ if (child->tree()->name() == name)
+ return child;
+ return 0;
+}
+
+Frame* FrameTree::find(const AtomicString& name) const
+{
+ if (name == "_self" || name == "_current" || name.isEmpty())
+ return m_thisFrame;
+
+ if (name == "_top")
+ return m_thisFrame->page()->mainFrame();
+
+ if (name == "_parent")
+ return parent() ? parent() : m_thisFrame;
+
+ // Since "_blank" should never be any frame's name, the following just amounts to an optimization.
+ if (name == "_blank")
+ return 0;
+
+ // Search subtree starting with this frame first.
+ for (Frame* frame = m_thisFrame; frame; frame = frame->tree()->traverseNext(m_thisFrame))
+ if (frame->tree()->name() == name)
+ return frame;
+
+ // Search the entire tree for this page next.
+ Page* page = m_thisFrame->page();
+ 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;
+ }
+ }
+
+ return 0;
+}
+
+bool FrameTree::isDescendantOf(const Frame* ancestor) const
+{
+ if (!ancestor)
+ return false;
+
+ if (m_thisFrame->page() != ancestor->page())
+ return false;
+
+ for (Frame* frame = m_thisFrame; frame; frame = frame->tree()->parent())
+ if (frame == ancestor)
+ return true;
+ return false;
+}
+
+Frame* FrameTree::traverseNext(const Frame* stayWithin) const
+{
+ Frame* child = firstChild();
+ if (child) {
+ ASSERT(!stayWithin || child->tree()->isDescendantOf(stayWithin));
+ return child;
+ }
+
+ if (m_thisFrame == stayWithin)
+ return 0;
+
+ Frame* sibling = nextSibling();
+ if (sibling) {
+ ASSERT(!stayWithin || sibling->tree()->isDescendantOf(stayWithin));
+ return sibling;
+ }
+
+ Frame* frame = m_thisFrame;
+ while (!sibling && (!stayWithin || frame->tree()->parent() != stayWithin)) {
+ frame = frame->tree()->parent();
+ if (!frame)
+ return 0;
+ sibling = frame->tree()->nextSibling();
+ }
+
+ if (frame) {
+ ASSERT(!stayWithin || !sibling || sibling->tree()->isDescendantOf(stayWithin));
+ return sibling;
+ }
+
+ return 0;
+}
+
+Frame* FrameTree::traverseNextWithWrap(bool wrap) const
+{
+ if (Frame* result = traverseNext())
+ return result;
+
+ if (wrap)
+ return m_thisFrame->page()->mainFrame();
+
+ return 0;
+}
+
+Frame* FrameTree::traversePreviousWithWrap(bool wrap) const
+{
+ // FIXME: besides the wrap feature, this is just the traversePreviousNode algorithm
+
+ if (Frame* prevSibling = previousSibling())
+ return prevSibling->tree()->deepLastChild();
+ if (Frame* parentFrame = parent())
+ return parentFrame;
+
+ // no siblings, no parent, self==top
+ if (wrap)
+ return deepLastChild();
+
+ // top view is always the last one in this ordering, so prev is nil without wrap
+ return 0;
+}
+
+Frame* FrameTree::deepLastChild() const
+{
+ Frame* result = m_thisFrame;
+ for (Frame* last = lastChild(); last; last = last->tree()->lastChild())
+ result = last;
+
+ return result;
+}
+
+Frame* FrameTree::top() const
+{
+ if (Page* page = m_thisFrame->page())
+ return page->mainFrame();
+
+ Frame* frame = m_thisFrame;
+ while (Frame* parent = frame->tree()->parent())
+ frame = parent;
+ return frame;
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/FrameTree.h b/WebCore/page/FrameTree.h
new file mode 100644
index 0000000..3f3e20a
--- /dev/null
+++ b/WebCore/page/FrameTree.h
@@ -0,0 +1,87 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2006 Apple Computer, 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 FrameTree_h
+#define FrameTree_h
+
+#include "AtomicString.h"
+
+namespace WebCore {
+
+ class Frame;
+
+ class FrameTree : Noncopyable {
+ public:
+ FrameTree(Frame* thisFrame, Frame* parentFrame)
+ : m_thisFrame(thisFrame)
+ , m_parent(parentFrame)
+ , m_previousSibling(0)
+ , m_lastChild(0)
+ , m_childCount(0)
+ {
+ }
+ ~FrameTree();
+
+ const AtomicString& name() const { return m_name; }
+ void setName(const AtomicString&);
+ Frame* parent() const { return m_parent; }
+ void setParent(Frame* parent) { m_parent = parent; }
+
+ Frame* nextSibling() const { return m_nextSibling.get(); }
+ Frame* previousSibling() const { return m_previousSibling; }
+ Frame* firstChild() const { return m_firstChild.get(); }
+ Frame* lastChild() const { return m_lastChild; }
+ unsigned childCount() const { return m_childCount; }
+
+ bool isDescendantOf(const Frame* ancestor) const;
+ Frame* traverseNext(const Frame* stayWithin = 0) const;
+ Frame* traverseNextWithWrap(bool) const;
+ Frame* traversePreviousWithWrap(bool) const;
+
+ void appendChild(PassRefPtr<Frame>);
+ void removeChild(Frame*);
+
+ Frame* child(unsigned index) const;
+ Frame* child(const AtomicString& name) const;
+ Frame* find(const AtomicString& name) const;
+
+ AtomicString uniqueChildName(const AtomicString& requestedName) const;
+
+ Frame* top() const;
+
+ private:
+ Frame* deepLastChild() const;
+
+ Frame* m_thisFrame;
+
+ Frame* m_parent;
+ AtomicString m_name;
+
+ // FIXME: use ListRefPtr?
+ RefPtr<Frame> m_nextSibling;
+ Frame* m_previousSibling;
+ RefPtr<Frame> m_firstChild;
+ Frame* m_lastChild;
+ unsigned m_childCount;
+ };
+
+} // namespace WebCore
+
+#endif // FrameTree_h
diff --git a/WebCore/page/FrameView.cpp b/WebCore/page/FrameView.cpp
new file mode 100644
index 0000000..d56175a
--- /dev/null
+++ b/WebCore/page/FrameView.cpp
@@ -0,0 +1,1092 @@
+/*
+ * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ * 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.
+ * (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ *
+ * 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 "FrameView.h"
+
+#include "AXObjectCache.h"
+#include "CSSStyleSelector.h"
+#include "EventHandler.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "GraphicsContext.h"
+#include "HTMLDocument.h"
+#include "HTMLFrameSetElement.h"
+#include "HTMLNames.h"
+#include "OverflowEvent.h"
+#include "RenderPart.h"
+#include "RenderPartObject.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+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_mediaType("screen")
+ , m_enqueueEvents(0)
+ , m_overflowStatusDirty(true)
+ , m_viewportRenderer(0)
+ , m_wasScrolledByUser(false)
+ , m_inProgrammaticScroll(false)
+ {
+ isTransparent = false;
+ baseBackgroundColor = Color::white;
+ vmode = hmode = ScrollbarAuto;
+ 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_wasScrolledByUser = false;
+ lastLayoutSize = IntSize();
+ }
+
+ bool doFullRepaint;
+
+ ScrollbarMode vmode;
+ ScrollbarMode hmode;
+ bool useSlowRepaints;
+ unsigned m_slowRepaintObjectCount;
+
+ int borderX, 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;
+
+ String m_mediaType;
+
+ unsigned m_enqueueEvents;
+ Vector<ScheduledEvent*> m_scheduledEvents;
+
+ bool m_overflowStatusDirty;
+ bool horizontalOverflow;
+ bool m_verticalOverflow;
+ RenderObject* m_viewportRenderer;
+
+ bool m_wasScrolledByUser;
+ bool m_inProgrammaticScroll;
+};
+
+FrameView::FrameView(Frame* frame)
+ : m_refCount(1)
+ , m_frame(frame)
+ , d(new FrameViewPrivate(this))
+{
+ init();
+ 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()));
+ show();
+}
+#endif
+
+FrameView::~FrameView()
+{
+ if (d->postLayoutTasksTimer.isActive()) {
+ d->postLayoutTasksTimer.stop();
+ d->m_scheduledEvents.clear();
+ d->m_enqueueEvents = 0;
+ }
+
+ resetScrollbars();
+
+ 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());
+ RenderPart* renderer = m_frame->ownerRenderer();
+ if (renderer && renderer->widget() == this)
+ renderer->setWidget(0);
+ }
+
+ delete d;
+ d = 0;
+}
+
+bool FrameView::isFrameView() const
+{
+ return true;
+}
+
+void FrameView::clearFrame()
+{
+ m_frame = 0;
+}
+
+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);
+}
+
+void FrameView::init()
+{
+ m_margins = IntSize(-1, -1); // undefined
+ m_size = IntSize();
+}
+
+void FrameView::clear()
+{
+ setStaticBackground(false);
+
+ d->reset();
+
+ if (m_frame)
+ if (RenderPart* renderer = m_frame->ownerRenderer())
+ renderer->viewCleared();
+
+ suppressScrollbars(true);
+}
+
+bool FrameView::didFirstLayout() const
+{
+ return !d->firstLayout;
+}
+
+void FrameView::initScrollbars()
+{
+ if (!d->needToInitScrollbars)
+ return;
+ d->needToInitScrollbars = false;
+ setScrollbarsMode(hScrollbarMode());
+}
+
+void FrameView::setMarginWidth(int w)
+{
+ // make it update the rendering area when set
+ m_margins.setWidth(w);
+}
+
+void FrameView::setMarginHeight(int h)
+{
+ // make it update the rendering area when set
+ m_margins.setHeight(h);
+}
+
+void FrameView::adjustViewSize()
+{
+ ASSERT(m_frame->view() == this);
+ RenderView* root = static_cast<RenderView*>(m_frame->renderer());
+ if (!root)
+ return;
+ resizeContents(root->overflowWidth(), root->overflowHeight());
+}
+
+void FrameView::applyOverflowToViewport(RenderObject* o, ScrollbarMode& hMode, ScrollbarMode& vMode)
+{
+ // Handle the overflow:hidden/scroll case for the body/html elements. WinIE treats
+ // overflow:hidden and overflow:scroll on <body> as applying to the document's
+ // scrollbars. The CSS2.1 draft states that HTML UAs should use the <html> or <body> element and XML/XHTML UAs should
+ // use the root element.
+ switch (o->style()->overflowX()) {
+ case OHIDDEN:
+ hMode = ScrollbarAlwaysOff;
+ break;
+ case OSCROLL:
+ hMode = ScrollbarAlwaysOn;
+ break;
+ case OAUTO:
+ hMode = ScrollbarAuto;
+ break;
+ default:
+ // Don't set it at all.
+ ;
+ }
+
+ switch (o->style()->overflowY()) {
+ case OHIDDEN:
+ vMode = ScrollbarAlwaysOff;
+ break;
+ case OSCROLL:
+ vMode = ScrollbarAlwaysOn;
+ break;
+ case OAUTO:
+ vMode = ScrollbarAuto;
+ break;
+ default:
+ // Don't set it at all.
+ ;
+ }
+
+ d->m_viewportRenderer = o;
+}
+
+int FrameView::layoutCount() const
+{
+ return d->layoutCount;
+}
+
+bool FrameView::needsFullRepaint() const
+{
+ return d->doFullRepaint;
+}
+
+void FrameView::addRepaintInfo(RenderObject* o, const IntRect& r)
+{
+ d->repaintRects.append(RenderObject::RepaintInfo(o, r));
+}
+
+RenderObject* FrameView::layoutRoot(bool onlyDuringLayout) const
+{
+ return onlyDuringLayout && layoutPending() ? 0 : d->layoutRoot;
+}
+
+void FrameView::layout(bool allowSubtree)
+{
+ if (d->midLayout)
+ return;
+
+ d->layoutTimer.stop();
+ d->delayedLayout = false;
+
+ // Protect the view from being deleted during layout (in recalcStyle)
+ RefPtr<FrameView> protector(this);
+
+ if (!m_frame) {
+ // FIXME: Do we need to set m_size.width here?
+ // FIXME: Should we set m_size.height here too?
+ m_size.setWidth(visibleWidth());
+ return;
+ }
+
+ // we shouldn't enter layout() while painting
+ ASSERT(!m_frame->isPainting());
+ if (m_frame->isPainting())
+ return;
+
+ if (!allowSubtree && d->layoutRoot) {
+ d->layoutRoot->markContainingBlocksForLayout(false);
+ d->layoutRoot = 0;
+ }
+
+ ASSERT(m_frame->view() == this);
+ // This early return should be removed when rdar://5598072 is resolved. In the meantime, there is a
+ // gigantic CrashTracer because of this issue, and the early return will hopefully cause graceful
+ // failure instead.
+ if (m_frame->view() != this)
+ return;
+
+ Document* document = m_frame->document();
+ if (!document) {
+ // FIXME: Should we set m_size.height here too?
+ m_size.setWidth(visibleWidth());
+ return;
+ }
+
+ d->layoutSchedulingEnabled = false;
+
+ if (!d->nestedLayoutCount && d->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();
+ 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())
+ m_frame->reapplyStyles();
+ else if (document->hasChangedChild())
+ document->recalcStyle();
+
+ bool subtree = d->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();
+ if (!root) {
+ // FIXME: Do we need to set m_size here?
+ d->layoutSchedulingEnabled = true;
+ return;
+ }
+
+ d->nestedLayoutCount++;
+
+ ScrollbarMode hMode = d->hmode;
+ ScrollbarMode vMode = d->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)) {
+ 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
+ }
+ }
+ } else if (rootRenderer)
+ applyOverflowToViewport(rootRenderer, hMode, vMode); // XML/XHTML UAs use the root element.
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (d->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());
+
+ 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());
+
+ // Set the initial vMode to AlwaysOn if we're auto.
+ if (vMode == ScrollbarAuto)
+ ScrollView::setVScrollbarMode(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);
+ }
+
+ suppressScrollbars(false, true);
+ }
+
+ IntSize oldSize = m_size;
+
+ m_size = IntSize(visibleWidth(), visibleHeight());
+
+ if (oldSize != m_size)
+ d->doFullRepaint = true;
+ }
+
+ RenderLayer* layer = root->enclosingLayer();
+
+ pauseScheduledEvents();
+
+ if (subtree)
+ root->view()->pushLayoutState(root);
+ d->midLayout = true;
+ root->layout();
+ d->midLayout = false;
+ if (subtree)
+ root->view()->popLayoutState();
+ d->layoutRoot = 0;
+
+ m_frame->invalidateSelection();
+
+ d->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();
+
+ d->layoutCount++;
+
+#if PLATFORM(MAC)
+ if (AXObjectCache::accessibilityEnabled())
+ root->document()->axObjectCache()->postNotificationToElement(root, "AXLayoutComplete");
+#endif
+ updateDashboardRegions();
+
+ if (didFirstLayout)
+ m_frame->loader()->didFirstLayout();
+
+ ASSERT(!root->needsLayout());
+
+ setStaticBackground(useSlowRepaints());
+
+ if (document->hasListenerType(Document::OVERFLOWCHANGED_LISTENER))
+ updateOverflowStatus(visibleWidth() < contentsWidth(),
+ visibleHeight() < contentsHeight());
+
+ if (!d->postLayoutTasksTimer.isActive()) {
+ // Calls resumeScheduledEvents()
+ performPostLayoutTasks();
+
+ if (needsLayout()) {
+ // 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);
+ pauseScheduledEvents();
+ layout();
+ }
+ } else {
+ resumeScheduledEvents();
+ ASSERT(d->m_enqueueEvents);
+ }
+
+ d->nestedLayoutCount--;
+}
+
+void FrameView::addWidgetToUpdate(RenderPartObject* object)
+{
+ if (!m_widgetUpdateSet)
+ m_widgetUpdateSet.set(new HashSet<RenderPartObject*>);
+
+ m_widgetUpdateSet->add(object);
+}
+
+void FrameView::removeWidgetToUpdate(RenderPartObject* object)
+{
+ if (!m_widgetUpdateSet)
+ return;
+
+ 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;
+}
+
+String FrameView::mediaType() const
+{
+ // See if we have an override type.
+ String overrideType = m_frame->loader()->client()->overrideMediaType();
+ if (!overrideType.isNull())
+ return overrideType;
+ return d->m_mediaType;
+}
+
+bool FrameView::useSlowRepaints() const
+{
+ return d->useSlowRepaints || d->m_slowRepaintObjectCount > 0;
+}
+
+void FrameView::setUseSlowRepaints()
+{
+ d->useSlowRepaints = true;
+ setStaticBackground(true);
+}
+
+void FrameView::addSlowRepaintObject()
+{
+ if (!d->m_slowRepaintObjectCount)
+ setStaticBackground(true);
+ d->m_slowRepaintObjectCount++;
+}
+
+void FrameView::removeSlowRepaintObject()
+{
+ ASSERT(d->m_slowRepaintObjectCount > 0);
+ d->m_slowRepaintObjectCount--;
+ if (!d->m_slowRepaintObjectCount)
+ setStaticBackground(d->useSlowRepaints);
+}
+
+void FrameView::setScrollbarsMode(ScrollbarMode mode)
+{
+ d->vmode = mode;
+ d->hmode = mode;
+
+ ScrollView::setScrollbarsMode(mode);
+}
+
+void FrameView::setVScrollbarMode(ScrollbarMode mode)
+{
+ d->vmode = mode;
+ ScrollView::setVScrollbarMode(mode);
+}
+
+void FrameView::setHScrollbarMode(ScrollbarMode mode)
+{
+ d->hmode = mode;
+ ScrollView::setHScrollbarMode(mode);
+}
+
+void FrameView::restoreScrollbar()
+{
+ suppressScrollbars(false);
+}
+
+void FrameView::scrollRectIntoViewRecursively(const IntRect& r)
+{
+ if (frame()->prohibitsScrolling())
+ return;
+ bool wasInProgrammaticScroll = d->m_inProgrammaticScroll;
+ d->m_inProgrammaticScroll = true;
+ ScrollView::scrollRectIntoViewRecursively(r);
+ d->m_inProgrammaticScroll = wasInProgrammaticScroll;
+}
+
+void FrameView::setContentsPos(int x, int y)
+{
+ if (frame()->prohibitsScrolling())
+ return;
+ bool wasInProgrammaticScroll = d->m_inProgrammaticScroll;
+ d->m_inProgrammaticScroll = true;
+ ScrollView::setContentsPos(x, y);
+ d->m_inProgrammaticScroll = wasInProgrammaticScroll;
+}
+
+void FrameView::repaintRectangle(const IntRect& r, bool immediate)
+{
+ updateContents(r, immediate);
+}
+
+void FrameView::layoutTimerFired(Timer<FrameView>*)
+{
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (m_frame->document() && !m_frame->document()->ownerElement())
+ printf("Layout timer fired at %d\n", m_frame->document()->elapsedTime());
+#endif
+ layout();
+}
+
+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->layoutSchedulingEnabled)
+ return;
+
+ if (!m_frame->document() || !m_frame->document()->shouldScheduleLayout())
+ return;
+
+ int delay = m_frame->document()->minimumLayoutDelay();
+ if (d->layoutTimer.isActive() && d->delayedLayout && !delay)
+ unscheduleRelayout();
+ if (d->layoutTimer.isActive())
+ return;
+
+ d->delayedLayout = delay != 0;
+
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (!m_frame->document()->ownerElement())
+ printf("Scheduling layout for %d\n", delay);
+#endif
+
+ d->layoutTimer.startOneShot(delay * 0.001);
+}
+
+static bool isObjectAncestorContainerOf(RenderObject* ancestor, RenderObject* descendant)
+{
+ for (RenderObject* r = descendant; r; r = r->container()) {
+ if (r == ancestor)
+ return true;
+ }
+ return false;
+}
+
+void FrameView::scheduleRelayoutOfSubtree(RenderObject* o)
+{
+ 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);
+ return;
+ }
+
+ if (layoutPending()) {
+ if (d->layoutRoot != o) {
+ if (isObjectAncestorContainerOf(d->layoutRoot, o)) {
+ // 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;
+ } else {
+ // Just do a full relayout
+ if (d->layoutRoot)
+ d->layoutRoot->markContainingBlocksForLayout(false);
+ d->layoutRoot = 0;
+ o->markContainingBlocksForLayout(false);
+ }
+ }
+ } else {
+ int delay = m_frame->document()->minimumLayoutDelay();
+ d->layoutRoot = o;
+ d->delayedLayout = delay != 0;
+ d->layoutTimer.startOneShot(delay * 0.001);
+ }
+}
+
+bool FrameView::layoutPending() const
+{
+ return d->layoutTimer.isActive();
+}
+
+bool FrameView::needsLayout() const
+{
+ // It is possible that our document will not have a body yet. If this is the case,
+ // 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());
+ 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();
+}
+
+void FrameView::setNeedsLayout()
+{
+ if (m_frame->renderer())
+ m_frame->renderer()->setNeedsLayout(true);
+}
+
+void FrameView::unscheduleRelayout()
+{
+ if (!d->layoutTimer.isActive())
+ return;
+
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+ if (m_frame->document() && !m_frame->document()->ownerElement())
+ printf("Layout timer unscheduled at %d\n", m_frame->document()->elapsedTime());
+#endif
+
+ d->layoutTimer.stop();
+ d->delayedLayout = false;
+}
+
+bool FrameView::isTransparent() const
+{
+ return d->isTransparent;
+}
+
+void FrameView::setTransparent(bool isTransparent)
+{
+ d->isTransparent = isTransparent;
+}
+
+Color FrameView::baseBackgroundColor() const
+{
+ return d->baseBackgroundColor;
+}
+
+void FrameView::setBaseBackgroundColor(Color bc)
+{
+ if (!bc.isValid())
+ bc = Color::white;
+ d->baseBackgroundColor = bc;
+}
+
+void FrameView::scheduleEvent(PassRefPtr<Event> event, PassRefPtr<EventTargetNode> eventTarget, bool tempEvent)
+{
+ if (!d->m_enqueueEvents) {
+ ExceptionCode ec = 0;
+ eventTarget->dispatchEvent(event, ec, tempEvent);
+ return;
+ }
+
+ ScheduledEvent* scheduledEvent = new ScheduledEvent;
+ scheduledEvent->m_event = event;
+ scheduledEvent->m_eventTarget = eventTarget;
+ scheduledEvent->m_tempEvent = tempEvent;
+ d->m_scheduledEvents.append(scheduledEvent);
+}
+
+void FrameView::pauseScheduledEvents()
+{
+ ASSERT(d->m_scheduledEvents.isEmpty() || d->m_enqueueEvents);
+ d->m_enqueueEvents++;
+}
+
+void FrameView::resumeScheduledEvents()
+{
+ d->m_enqueueEvents--;
+ if (!d->m_enqueueEvents)
+ dispatchScheduledEvents();
+ ASSERT(d->m_scheduledEvents.isEmpty() || d->m_enqueueEvents);
+}
+
+void FrameView::performPostLayoutTasks()
+{
+ RenderView* root = static_cast<RenderView*>(m_frame->document()->renderer());
+
+ root->updateWidgetPositions();
+ if (m_widgetUpdateSet && d->nestedLayoutCount <= 1) {
+ Vector<RenderPartObject*> objectVector;
+ copyToVector(*m_widgetUpdateSet, objectVector);
+ size_t size = objectVector.size();
+ for (size_t i = 0; i < size; ++i) {
+ RenderPartObject* object = objectVector[i];
+ object->updateWidget(false);
+
+ // updateWidget() can destroy the RenderPartObject, so we need to make sure it's
+ // alive by checking if it's still in m_widgetUpdateSet.
+ if (m_widgetUpdateSet->contains(object))
+ object->updateWidgetPosition();
+ }
+ m_widgetUpdateSet->clear();
+ }
+
+ resumeScheduledEvents();
+
+ if (!root->printing()) {
+ IntSize currentSize = IntSize(width(), height());
+ bool resized = !d->firstLayout && currentSize != d->lastLayoutSize;
+ d->lastLayoutSize = currentSize;
+ if (resized)
+ m_frame->sendResizeEvent();
+ }
+}
+
+void FrameView::postLayoutTimerFired(Timer<FrameView>*)
+{
+ performPostLayoutTasks();
+}
+
+void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow)
+{
+ if (!d->m_viewportRenderer)
+ return;
+
+ if (d->m_overflowStatusDirty) {
+ d->horizontalOverflow = horizontalOverflow;
+ d->m_verticalOverflow = verticalOverflow;
+ d->m_overflowStatusDirty = false;
+ return;
+ }
+
+ bool horizontalOverflowChanged = (d->horizontalOverflow != horizontalOverflow);
+ bool verticalOverflowChanged = (d->m_verticalOverflow != verticalOverflow);
+
+ if (horizontalOverflowChanged || verticalOverflowChanged) {
+ d->horizontalOverflow = horizontalOverflow;
+ d->m_verticalOverflow = verticalOverflow;
+
+ scheduleEvent(new OverflowEvent(horizontalOverflowChanged, horizontalOverflow,
+ verticalOverflowChanged, verticalOverflow),
+ EventTargetNodeCast(d->m_viewportRenderer->element()), true);
+ }
+
+}
+
+void FrameView::dispatchScheduledEvents()
+{
+ if (d->m_scheduledEvents.isEmpty())
+ return;
+
+ Vector<ScheduledEvent*> scheduledEventsCopy = d->m_scheduledEvents;
+ d->m_scheduledEvents.clear();
+
+ Vector<ScheduledEvent*>::iterator end = scheduledEventsCopy.end();
+ for (Vector<ScheduledEvent*>::iterator it = scheduledEventsCopy.begin(); it != end; ++it) {
+ ScheduledEvent* scheduledEvent = *it;
+
+ ExceptionCode ec = 0;
+
+ // 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);
+
+ 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);
+
+ if (!m_frame || !m_frame->document() || !m_frame->document()->ownerElement())
+ return clipRect;
+
+ // Take our owner element and get the clip rect from the enclosing layer.
+ Element* elt = m_frame->document()->ownerElement();
+ RenderLayer* layer = elt->renderer()->enclosingLayer();
+ // FIXME: layer should never be null, but sometimes seems to be anyway.
+ if (!layer)
+ return clipRect;
+ FrameView* parentView = elt->document()->view();
+ clipRect.intersect(parentView->windowClipRectForLayer(layer, true));
+ return clipRect;
+}
+
+IntRect FrameView::windowClipRectForLayer(const RenderLayer* layer, bool clipToLayerContents) const
+{
+ // If we have no layer, just return our window clip rect.
+ if (!layer)
+ return windowClipRect();
+
+ // Apply the clip from the layer.
+ IntRect clipRect;
+ if (clipToLayerContents)
+ clipRect = layer->childrenClipRect();
+ else
+ clipRect = layer->selfClipRect();
+ clipRect = contentsToWindow(clipRect);
+ return intersection(clipRect, windowClipRect());
+}
+
+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();
+ }
+}
+
+void FrameView::updateControlTints()
+{
+ // This is called when control tints are changed from aqua/graphite to clear and vice versa.
+ // We do a "fake" paint, and when the theme gets a paint call, it can then do an invalidate.
+ // This is only done if the theme supports control tinting. It's up to the theme and platform
+ // to define when controls get the tint and to call this function when that changes.
+
+ // Optimize the common case where we bring a window to the front while it's still empty.
+ if (!m_frame || m_frame->loader()->url().isEmpty())
+ return;
+
+ if (theme()->supportsControlTints() && m_frame->renderer()) {
+ 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
+ }
+}
+
+bool FrameView::wasScrolledByUser() const
+{
+ return d->m_wasScrolledByUser;
+}
+
+void FrameView::setWasScrolledByUser(bool wasScrolledByUser)
+{
+ if (d->m_inProgrammaticScroll)
+ return;
+ d->m_wasScrolledByUser = wasScrolledByUser;
+}
+
+#if PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(QT)
+void FrameView::layoutIfNeededRecursive()
+{
+ // We have to crawl our entire tree looking for any FrameViews that need
+ // layout and make sure they are up to date.
+ // Mac actually tests for intersection with the dirty region and tries not to
+ // update layout for frames that are outside the dirty region. Not only does this seem
+ // pointless (since those frames will have set a zero timer to layout anyway), but
+ // it is also incorrect, since if two frames overlap, the first could be excluded from the dirty
+ // region but then become included later by the second frame adding rects to the dirty region
+ // when it lays out.
+
+ if (needsLayout())
+ layout();
+
+ HashSet<Widget*>* viewChildren = children();
+ HashSet<Widget*>::iterator end = viewChildren->end();
+ for (HashSet<Widget*>::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
new file mode 100644
index 0000000..7577b10
--- /dev/null
+++ b/WebCore/page/FrameView.h
@@ -0,0 +1,173 @@
+/*
+ Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ (C) 1998 Waldo Bastian (bastian@kde.org)
+ (C) 1998, 1999 Torben Weis (weis@kde.org)
+ (C) 1999 Lars Knoll (knoll@kde.org)
+ (C) 1999 Antti Koivisto (koivisto@kde.org)
+ Copyright (C) 2004, 2005, 2006, 2007 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
+ 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 FrameView_h
+#define FrameView_h
+
+#include "ScrollView.h"
+#include "IntSize.h"
+#include <wtf/Forward.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class Color;
+class Event;
+class EventTargetNode;
+class Frame;
+class FrameViewPrivate;
+class IntRect;
+class PlatformMouseEvent;
+class Node;
+class RenderLayer;
+class RenderObject;
+class RenderPartObject;
+class String;
+
+template <typename T> class Timer;
+
+class FrameView : public ScrollView {
+public:
+ FrameView(Frame*);
+
+ // On the Mac, FrameViews always get their size from the underlying NSView,
+ // so passing in a size is nonsensical.
+#if !PLATFORM(MAC)
+ FrameView(Frame*, const IntSize& initialSize);
+#endif
+
+ virtual ~FrameView();
+
+ Frame* frame() const { return m_frame.get(); }
+ void clearFrame();
+
+ void ref() { ++m_refCount; }
+ void deref() { if (!--m_refCount) delete this; }
+ bool hasOneRef() { return m_refCount == 1; }
+
+ int marginWidth() const { return m_margins.width(); } // -1 means default
+ int marginHeight() const { return m_margins.height(); } // -1 means default
+ void setMarginWidth(int);
+ void setMarginHeight(int);
+
+ virtual void setVScrollbarMode(ScrollbarMode);
+ virtual void setHScrollbarMode(ScrollbarMode);
+ virtual void setScrollbarsMode(ScrollbarMode);
+
+ void layout(bool allowSubtree = true);
+ bool didFirstLayout() const;
+ void layoutTimerFired(Timer<FrameView>*);
+ void scheduleRelayout();
+ void scheduleRelayoutOfSubtree(RenderObject*);
+ void unscheduleRelayout();
+ bool layoutPending() const;
+
+ RenderObject* layoutRoot(bool onlyDuringLayout = false) const;
+ int layoutCount() const;
+
+ // These two helper functions just pass through to the RenderView.
+ bool needsLayout() const;
+ void setNeedsLayout();
+
+ bool needsFullRepaint() const;
+ void repaintRectangle(const IntRect&, bool immediate);
+ void addRepaintInfo(RenderObject*, const IntRect&);
+
+ void resetScrollbars();
+
+ void clear();
+
+ bool isTransparent() const;
+ void setTransparent(bool isTransparent);
+
+ Color baseBackgroundColor() const;
+ void setBaseBackgroundColor(Color);
+
+ void adjustViewSize();
+ void initScrollbars();
+
+ virtual IntRect windowClipRect() const;
+ IntRect windowClipRect(bool clipToContents) const;
+ IntRect windowClipRectForLayer(const RenderLayer*, bool clipToLayerContents) const;
+
+ virtual void scrollRectIntoViewRecursively(const IntRect&);
+ virtual void setContentsPos(int x, int y);
+
+ String mediaType() const;
+ void setMediaType(const String&);
+
+ void setUseSlowRepaints();
+
+ void addSlowRepaintObject();
+ void removeSlowRepaintObject();
+
+ void updateDashboardRegions();
+ void updateControlTints();
+
+ void restoreScrollbar();
+
+ void scheduleEvent(PassRefPtr<Event>, PassRefPtr<EventTargetNode>, bool tempEvent);
+ void pauseScheduledEvents();
+ void resumeScheduledEvents();
+ void postLayoutTimerFired(Timer<FrameView>*);
+
+ bool wasScrolledByUser() const;
+ void setWasScrolledByUser(bool);
+
+ 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)
+ void layoutIfNeededRecursive();
+#endif
+
+private:
+ void init();
+
+ virtual bool isFrameView() const;
+
+ bool scrollTo(const IntRect&);
+
+ bool useSlowRepaints() const;
+
+ void applyOverflowToViewport(RenderObject*, ScrollbarMode& hMode, ScrollbarMode& vMode);
+
+ void updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow);
+
+ void dispatchScheduledEvents();
+ void performPostLayoutTasks();
+
+ unsigned m_refCount;
+ IntSize m_size;
+ IntSize m_margins;
+ OwnPtr<HashSet<RenderPartObject*> > m_widgetUpdateSet;
+ RefPtr<Frame> m_frame;
+ FrameViewPrivate* d;
+};
+
+}
+
+#endif
diff --git a/WebCore/page/GlobalHistory.h b/WebCore/page/GlobalHistory.h
new file mode 100644
index 0000000..4857f6f
--- /dev/null
+++ b/WebCore/page/GlobalHistory.h
@@ -0,0 +1,37 @@
+/*
+ * 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 GlobalHistory_h
+#define GlobalHistory_h
+
+#include <wtf/unicode/Unicode.h>
+
+namespace WebCore {
+
+ bool historyContains(const UChar* characters, unsigned length);
+
+} // namespace WebCore
+
+#endif // GlobalHistory_h
diff --git a/WebCore/page/History.cpp b/WebCore/page/History.cpp
new file mode 100644
index 0000000..2527132
--- /dev/null
+++ b/WebCore/page/History.cpp
@@ -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.
+ *
+ * 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 "History.h"
+
+#include "Frame.h"
+#include "FrameLoader.h"
+
+namespace WebCore {
+
+History::History(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+Frame* History::frame() const
+{
+ return m_frame;
+}
+
+void History::disconnectFrame()
+{
+ m_frame = 0;
+}
+
+unsigned History::length() const
+{
+ if (!m_frame)
+ return 0;
+ return m_frame->loader()->getHistoryLength();
+}
+
+void History::back()
+{
+ if (!m_frame)
+ return;
+ m_frame->loader()->scheduleHistoryNavigation(-1);
+}
+
+void History::forward()
+{
+ if (!m_frame)
+ return;
+ m_frame->loader()->scheduleHistoryNavigation(1);
+}
+
+void History::go(int distance)
+{
+ if (!m_frame)
+ return;
+ m_frame->loader()->scheduleHistoryNavigation(distance);
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/History.h b/WebCore/page/History.h
new file mode 100644
index 0000000..f0df2de
--- /dev/null
+++ b/WebCore/page/History.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ * 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 History_h
+#define History_h
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class Frame;
+
+ class History : public RefCounted<History> {
+ public:
+ static PassRefPtr<History> create(Frame* frame) { return adoptRef(new History(frame)); }
+
+ Frame* frame() const;
+ void disconnectFrame();
+
+ unsigned length() const;
+ void back();
+ void forward();
+ void go(int distance);
+
+ private:
+ History(Frame*);
+
+ Frame* m_frame;
+ };
+
+} // namespace WebCore
+
+#endif // History_h
diff --git a/WebCore/page/History.idl b/WebCore/page/History.idl
new file mode 100644
index 0000000..e86cf92
--- /dev/null
+++ b/WebCore/page/History.idl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+module window {
+
+ interface [
+ CustomGetOwnPropertySlot,
+ CustomPutFunction,
+ CustomDeleteProperty,
+ CustomGetPropertyNames
+ ] History {
+ readonly attribute unsigned long length;
+
+ void back();
+ void forward();
+ void go(in long distance);
+ };
+
+}
diff --git a/WebCore/page/InspectorClient.h b/WebCore/page/InspectorClient.h
new file mode 100644
index 0000000..ec1ee92
--- /dev/null
+++ b/WebCore/page/InspectorClient.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ *
+ * 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 InspectorClient_h
+#define InspectorClient_h
+
+namespace WebCore {
+
+class Node;
+class Page;
+class String;
+
+class InspectorClient {
+public:
+ virtual ~InspectorClient() { }
+
+ virtual void inspectorDestroyed() = 0;
+
+ virtual Page* createPage() = 0;
+
+ virtual String localizedStringsURL() = 0;
+
+ 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;
+
+ virtual void inspectedURLChanged(const String& newURL) = 0;
+};
+
+} // namespace WebCore
+
+#endif // !defined(InspectorClient_h)
diff --git a/WebCore/page/InspectorController.cpp b/WebCore/page/InspectorController.cpp
new file mode 100644
index 0000000..ba08f01
--- /dev/null
+++ b/WebCore/page/InspectorController.cpp
@@ -0,0 +1,1653 @@
+/*
+ * 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
+ };
+
+ static PassRefPtr<InspectorResource> create(long long identifier, DocumentLoader* documentLoader, Frame* frame)
+ {
+ return adoptRef(new InspectorResource(identifier, documentLoader, frame));
+ }
+
+ ~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;
+
+private:
+ 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)
+ {
+ }
+};
+
+#pragma mark -
+#pragma mark InspectorDatabaseResource Struct
+
+#if ENABLE(DATABASE)
+struct InspectorDatabaseResource : public RefCounted<InspectorDatabaseResource> {
+ static PassRefPtr<InspectorDatabaseResource> create(Database* database, const String& domain, const String& name, const String& version)
+ {
+ return adoptRef(new InspectorDatabaseResource(database, domain, name, version));
+ }
+
+ 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;
+
+private:
+ InspectorDatabaseResource(Database* database, const String& domain, const String& name, const String& version)
+ : database(database)
+ , domain(domain)
+ , name(name)
+ , version(version)
+ , scriptContext(0)
+ , scriptObject(0)
+ {
+ }
+};
+#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;
+
+ RefPtr<InspectorResource> resource = InspectorResource::create(m_nextIdentifier--, loader, loader->frame());
+ resource->finished = true;
+
+ updateResourceRequest(resource.get(), request);
+ updateResourceResponse(resource.get(), 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.get());
+
+ if (windowVisible())
+ addAndUpdateScriptResource(resource.get());
+}
+
+void InspectorController::identifierForInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request)
+{
+ if (!enabled())
+ return;
+
+ RefPtr<InspectorResource> resource = InspectorResource::create(identifier, loader, loader->frame());
+
+ updateResourceRequest(resource.get(), request);
+
+ if (loader->frame() == m_inspectedPage->mainFrame() && request.url() == loader->requestURL())
+ m_mainResource = resource;
+
+ addResource(resource.get());
+
+ if (windowVisible() && loader->isLoadingFromCachedPage() && resource == m_mainResource)
+ addAndUpdateScriptResource(resource.get());
+}
+
+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;
+
+ RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
+
+ m_databaseResources.add(resource);
+
+ if (windowVisible())
+ addDatabaseScriptResource(resource.get());
+}
+#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
new file mode 100644
index 0000000..46a5df3
--- /dev/null
+++ b/WebCore/page/InspectorController.h
@@ -0,0 +1,177 @@
+/*
+ * 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/MouseEventWithHitTestResults.cpp b/WebCore/page/MouseEventWithHitTestResults.cpp
new file mode 100644
index 0000000..d7596ce
--- /dev/null
+++ b/WebCore/page/MouseEventWithHitTestResults.cpp
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2006 Apple Computer, 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 "MouseEventWithHitTestResults.h"
+
+#include "Element.h"
+#include "Node.h"
+
+// Would TargetedMouseEvent be a better name?
+
+namespace WebCore {
+
+static inline Element* targetElement(Node* node)
+{
+ if (!node)
+ return 0;
+ Node* parent = node->parent();
+ if (!parent || !parent->isElementNode())
+ return 0;
+ return static_cast<Element*>(parent);
+}
+
+MouseEventWithHitTestResults::MouseEventWithHitTestResults(const PlatformMouseEvent& event, const HitTestResult& hitTestResult)
+ : m_event(event)
+ , m_hitTestResult(hitTestResult)
+{
+}
+
+Node* MouseEventWithHitTestResults::targetNode() const
+{
+ Node* node = m_hitTestResult.innerNode();
+ if (node && node->inDocument())
+ return node;
+
+ Element* element = targetElement(node);
+ if (element && element->inDocument())
+ return element;
+
+ return node;
+}
+
+const IntPoint MouseEventWithHitTestResults::localPoint() const
+{
+ return m_hitTestResult.localPoint();
+}
+
+PlatformScrollbar* MouseEventWithHitTestResults::scrollbar() const
+{
+ return m_hitTestResult.scrollbar();
+}
+
+bool MouseEventWithHitTestResults::isOverLink() const
+{
+ return m_hitTestResult.URLElement() && m_hitTestResult.URLElement()->isLink();
+}
+
+}
diff --git a/WebCore/page/MouseEventWithHitTestResults.h b/WebCore/page/MouseEventWithHitTestResults.h
new file mode 100644
index 0000000..8392702
--- /dev/null
+++ b/WebCore/page/MouseEventWithHitTestResults.h
@@ -0,0 +1,50 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2006 Apple Computer, 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 MouseEventWithHitTestResults_h
+#define MouseEventWithHitTestResults_h
+
+#include "HitTestResult.h"
+#include "PlatformMouseEvent.h"
+
+namespace WebCore {
+
+class PlatformScrollbar;
+
+// FIXME: Why doesn't this class just cache a HitTestResult instead of copying all of HitTestResult's fields over?
+class MouseEventWithHitTestResults {
+public:
+ MouseEventWithHitTestResults(const PlatformMouseEvent&, const HitTestResult&);
+
+ const PlatformMouseEvent& event() const { return m_event; }
+ const HitTestResult& hitTestResult() const { return m_hitTestResult; }
+ Node* targetNode() const;
+ const IntPoint localPoint() const;
+ PlatformScrollbar* scrollbar() const;
+ bool isOverLink() const;
+
+private:
+ PlatformMouseEvent m_event;
+ HitTestResult m_hitTestResult;
+};
+
+}
+
+#endif
diff --git a/WebCore/page/Page.cpp b/WebCore/page/Page.cpp
new file mode 100644
index 0000000..f60d05c
--- /dev/null
+++ b/WebCore/page/Page.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2006, 2007 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
+ * 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 "Page.h"
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "ContextMenuClient.h"
+#include "ContextMenuController.h"
+#include "EditorClient.h"
+#include "DragController.h"
+#include "FileSystem.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "HistoryItem.h"
+#include "InspectorController.h"
+#include "Logging.h"
+#include "ProgressTracker.h"
+#include "RenderWidget.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "StringHash.h"
+#include "TextResourceDecoder.h"
+#include "Widget.h"
+#include <kjs/collector.h>
+#include <kjs/JSLock.h>
+#include <wtf/HashMap.h>
+
+using namespace KJS;
+
+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;
+#endif
+
+Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, EditorClient* editorClient, DragClient* dragClient, InspectorClient* inspectorClient)
+ : m_chrome(new Chrome(this, chromeClient))
+ , m_dragCaretController(new SelectionController(0, true))
+ , m_dragController(new DragController(this, dragClient))
+ , m_focusController(new FocusController(this))
+ , m_contextMenuController(new ContextMenuController(this, contextMenuClient))
+ , m_inspectorController(new InspectorController(this, inspectorClient))
+ , m_settings(new Settings(this))
+ , m_progress(new ProgressTracker)
+ , m_backForwardList(BackForwardList::create(this))
+ , m_editorClient(editorClient)
+ , m_frameCount(0)
+ , m_tabKeyCyclesThroughElements(true)
+ , m_defersLoading(false)
+ , m_inLowQualityInterpolationMode(false)
+ , m_parentInspectorController(0)
+ , m_didLoadUserStyleSheet(false)
+ , m_userStyleSheetModificationTime(0)
+{
+ if (!allPages) {
+ allPages = new HashSet<Page*>;
+ setFocusRingColorChangeFunction(setNeedsReapplyStyles);
+ }
+
+ ASSERT(!allPages->contains(this));
+ allPages->add(this);
+
+#ifndef NDEBUG
+ ++PageCounter::count;
+#endif
+}
+
+Page::~Page()
+{
+ m_mainFrame->setView(0);
+ setGroupName(String());
+ allPages->remove(this);
+
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->pageDestroyed();
+ m_editorClient->pageDestroyed();
+ m_inspectorController->pageDestroyed();
+
+ m_backForwardList->close();
+
+#ifndef NDEBUG
+ --PageCounter::count;
+
+ // 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
+ // is executing.
+ Frame::cancelAllKeepAlive();
+#endif
+}
+
+void Page::setMainFrame(PassRefPtr<Frame> mainFrame)
+{
+ ASSERT(!m_mainFrame); // Should only be called during initialization
+ m_mainFrame = mainFrame;
+}
+
+BackForwardList* Page::backForwardList()
+{
+ return m_backForwardList.get();
+}
+
+bool Page::goBack()
+{
+ HistoryItem* item = m_backForwardList->backItem();
+
+ if (item) {
+ goToItem(item, FrameLoadTypeBack);
+ return true;
+ }
+ return false;
+}
+
+bool Page::goForward()
+{
+ HistoryItem* item = m_backForwardList->forwardItem();
+
+ if (item) {
+ goToItem(item, FrameLoadTypeForward);
+ return true;
+ }
+ return false;
+}
+
+void Page::goToItem(HistoryItem* item, FrameLoadType type)
+{
+ // Abort any current load if we're going to a history item
+ m_mainFrame->loader()->stopAllLoaders();
+ m_mainFrame->loader()->goToItem(item, 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;
+ }
+ }
+ }
+ 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);
+ }
+}
+
+const HashSet<Page*>* Page::frameNamespace() const
+{
+ return (frameNamespaces && !m_groupName.isEmpty()) ? frameNamespaces->get(m_groupName) : 0;
+}
+
+const HashSet<Page*>* Page::frameNamespace(const String& groupName)
+{
+ return (frameNamespaces && !groupName.isEmpty()) ? frameNamespaces->get(groupName) : 0;
+}
+
+void Page::setNeedsReapplyStyles()
+{
+ if (!allPages)
+ return;
+ 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())
+ frame->setNeedsReapplyStyles();
+}
+
+static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
+{
+ return forward
+ ? curr->tree()->traverseNextWithWrap(wrapFlag)
+ : curr->tree()->traversePreviousWithWrap(wrapFlag);
+}
+
+bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap)
+{
+ if (target.isEmpty() || !mainFrame())
+ return false;
+
+ Frame* frame = focusController()->focusedOrMainFrame();
+ Frame* startFrame = frame;
+ do {
+ if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) {
+ if (frame != startFrame)
+ startFrame->selectionController()->clear();
+ focusController()->setFocusedFrame(frame);
+ return true;
+ }
+ frame = incrementFrame(frame, direction == FindDirectionForward, shouldWrap);
+ } while (frame && frame != startFrame);
+
+ // 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()) {
+ bool found = startFrame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true);
+ focusController()->setFocusedFrame(frame);
+ return found;
+ }
+
+ return false;
+}
+
+unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit)
+{
+ if (target.isEmpty() || !mainFrame())
+ return 0;
+
+ unsigned matches = 0;
+
+ Frame* frame = mainFrame();
+ do {
+ frame->setMarkedTextMatchesAreHighlighted(shouldHighlight);
+ matches += frame->markAllMatchesForText(target, caseSensitivity == TextCaseSensitive, (limit == 0) ? 0 : (limit - matches));
+ frame = incrementFrame(frame, true, false);
+ } while (frame);
+
+ return matches;
+}
+
+void Page::unmarkAllTextMatches()
+{
+ if (!mainFrame())
+ return;
+
+ Frame* frame = mainFrame();
+ do {
+ if (Document* document = frame->document())
+ document->removeMarkers(DocumentMarker::TextMatch);
+ frame = incrementFrame(frame, true, false);
+ } while (frame);
+}
+
+const Selection& Page::selection() const
+{
+ return focusController()->focusedOrMainFrame()->selectionController()->selection();
+}
+
+void Page::setDefersLoading(bool defers)
+{
+ if (defers == m_defersLoading)
+ return;
+
+ m_defersLoading = defers;
+ for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->loader()->setDefersLoading(defers);
+}
+
+void Page::clearUndoRedoOperations()
+{
+ m_editorClient->clearUndoRedoOperations();
+}
+
+bool Page::inLowQualityImageInterpolationMode() const
+{
+ return m_inLowQualityInterpolationMode;
+}
+
+void Page::setInLowQualityImageInterpolationMode(bool mode)
+{
+ m_inLowQualityInterpolationMode = mode;
+}
+
+void Page::userStyleSheetLocationChanged()
+{
+#if !FRAME_LOADS_USER_STYLESHEET
+ // FIXME: We should provide a way to load other types of URLs than just
+ // file: (e.g., http:, data:).
+ if (m_settings->userStyleSheetLocation().isLocalFile())
+ m_userStyleSheetPath = m_settings->userStyleSheetLocation().fileSystemPath();
+ else
+ m_userStyleSheetPath = String();
+
+ m_didLoadUserStyleSheet = false;
+ m_userStyleSheet = String();
+ m_userStyleSheetModificationTime = 0;
+#endif
+}
+
+const String& Page::userStyleSheet() const
+{
+ if (m_userStyleSheetPath.isEmpty()) {
+ ASSERT(m_userStyleSheet.isEmpty());
+ return m_userStyleSheet;
+ }
+
+ time_t modTime;
+ if (!getFileModificationTime(m_userStyleSheetPath, modTime)) {
+ // The stylesheet either doesn't exist, was just deleted, or is
+ // otherwise unreadable. If we've read the stylesheet before, we should
+ // throw away that data now as it no longer represents what's on disk.
+ m_userStyleSheet = String();
+ return m_userStyleSheet;
+ }
+
+ // If the stylesheet hasn't changed since the last time we read it, we can
+ // just return the old data.
+ if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime)
+ return m_userStyleSheet;
+
+ m_didLoadUserStyleSheet = true;
+ m_userStyleSheet = String();
+ m_userStyleSheetModificationTime = modTime;
+
+ // FIXME: It would be better to load this asynchronously to avoid blocking
+ // the process, but we will first need to create an asynchronous loading
+ // mechanism that is not tied to a particular Frame. We will also have to
+ // determine what our behavior should be before the stylesheet is loaded
+ // and what should happen when it finishes loading, especially with respect
+ // to when the load event fires, when Document::close is called, and when
+ // layout/paint are allowed to happen.
+ RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath);
+ if (!data)
+ return m_userStyleSheet;
+
+ m_userStyleSheet = TextResourceDecoder("text/css").decode(data->data(), data->size());
+
+ return m_userStyleSheet;
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/Page.h b/WebCore/page/Page.h
new file mode 100644
index 0000000..b685e3f
--- /dev/null
+++ b/WebCore/page/Page.h
@@ -0,0 +1,173 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2006 Apple Computer, 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 Page_h
+#define Page_h
+
+#include "BackForwardList.h"
+#include "Chrome.h"
+#include "ContextMenuController.h"
+#include "FrameLoaderTypes.h"
+#include "PlatformString.h"
+#include <wtf/HashSet.h>
+#include <wtf/OwnPtr.h>
+
+#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS))
+typedef struct HINSTANCE__* HINSTANCE;
+#endif
+
+typedef enum TextCaseSensitivity {
+ TextCaseSensitive,
+ TextCaseInsensitive
+};
+
+typedef enum FindDirection {
+ FindDirectionForward,
+ FindDirectionBackward
+};
+
+namespace WebCore {
+
+ class Chrome;
+ class ChromeClient;
+ class ContextMenuClient;
+ class ContextMenuController;
+ class DragClient;
+ class DragController;
+ class EditorClient;
+ class FocusController;
+ class Frame;
+ class InspectorClient;
+ class InspectorController;
+ class Node;
+ class ProgressTracker;
+ class Selection;
+ class SelectionController;
+ class Settings;
+
+ class Page : Noncopyable {
+ public:
+ static void setNeedsReapplyStyles();
+ static const HashSet<Page*>* frameNamespace(const String&);
+
+ Page(ChromeClient*, ContextMenuClient*, EditorClient*, DragClient*, InspectorClient*);
+ ~Page();
+
+ EditorClient* editorClient() const { return m_editorClient; }
+
+ void setMainFrame(PassRefPtr<Frame>);
+ Frame* mainFrame() const { return m_mainFrame.get(); }
+
+ BackForwardList* backForwardList();
+
+ // FIXME: The following three methods don't fall under the responsibilities of the Page object
+ // They seem to fit a hypothetical Page-controller object that would be akin to the
+ // Frame-FrameLoader relationship. They have to live here now, but should move somewhere that
+ // makes more sense when that class exists.
+ bool goBack();
+ bool goForward();
+ void goToItem(HistoryItem*, FrameLoadType);
+
+ void setGroupName(const String&);
+ String groupName() const { return m_groupName; }
+
+ const HashSet<Page*>* frameNamespace() const;
+
+ void incrementFrameCount() { ++m_frameCount; }
+ void decrementFrameCount() { --m_frameCount; }
+ int frameCount() const { return m_frameCount; }
+
+ Chrome* chrome() const { return m_chrome.get(); }
+ SelectionController* dragCaretController() const { return m_dragCaretController.get(); }
+ DragController* dragController() const { return m_dragController.get(); }
+ FocusController* focusController() const { return m_focusController.get(); }
+ ContextMenuController* contextMenuController() const { return m_contextMenuController.get(); }
+ InspectorController* inspectorController() const { return m_inspectorController.get(); }
+ Settings* settings() const { return m_settings.get(); }
+ ProgressTracker* progress() const { return m_progress.get(); }
+
+ void setParentInspectorController(InspectorController* controller) { m_parentInspectorController = controller; }
+ InspectorController* parentInspectorController() const { return m_parentInspectorController; }
+
+ void setTabKeyCyclesThroughElements(bool b) { m_tabKeyCyclesThroughElements = b; }
+ bool tabKeyCyclesThroughElements() const { return m_tabKeyCyclesThroughElements; }
+
+ bool findString(const String&, TextCaseSensitivity, FindDirection, bool shouldWrap);
+ unsigned int markAllMatchesForText(const String&, TextCaseSensitivity, bool shouldHighlight, unsigned);
+ void unmarkAllTextMatches();
+
+ const Selection& selection() const;
+
+ void setDefersLoading(bool);
+ bool defersLoading() const { return m_defersLoading; }
+
+ void clearUndoRedoOperations();
+
+ bool inLowQualityImageInterpolationMode() const;
+ void setInLowQualityImageInterpolationMode(bool = true);
+
+ void userStyleSheetLocationChanged();
+ const String& userStyleSheet() const;
+
+#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS))
+ // 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
+
+ private:
+ OwnPtr<Chrome> m_chrome;
+ OwnPtr<SelectionController> m_dragCaretController;
+ OwnPtr<DragController> m_dragController;
+ OwnPtr<FocusController> m_focusController;
+ OwnPtr<ContextMenuController> m_contextMenuController;
+ OwnPtr<InspectorController> m_inspectorController;
+ OwnPtr<Settings> m_settings;
+ OwnPtr<ProgressTracker> m_progress;
+
+ RefPtr<BackForwardList> m_backForwardList;
+ RefPtr<Frame> m_mainFrame;
+ RefPtr<Node> m_focusedNode;
+
+ EditorClient* m_editorClient;
+
+ int m_frameCount;
+ String m_groupName;
+
+ bool m_tabKeyCyclesThroughElements;
+ bool m_defersLoading;
+
+ bool m_inLowQualityInterpolationMode;
+
+ InspectorController* m_parentInspectorController;
+
+ String m_userStyleSheetPath;
+ mutable String m_userStyleSheet;
+ mutable bool m_didLoadUserStyleSheet;
+ mutable time_t m_userStyleSheetModificationTime;
+
+#if PLATFORM(WIN) || (PLATFORM(WX) && defined(__WXMSW__))
+ static HINSTANCE s_instanceHandle;
+#endif
+ };
+
+} // namespace WebCore
+
+#endif // Page_h
diff --git a/WebCore/page/Plugin.h b/WebCore/page/Plugin.h
new file mode 100644
index 0000000..1e53184
--- /dev/null
+++ b/WebCore/page/Plugin.h
@@ -0,0 +1,42 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2006 Apple Computer, 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 Plugin_h
+#define Plugin_h
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class Widget;
+
+ class Plugin : public RefCounted<Plugin> {
+ public:
+ static PassRefPtr<Plugin> create(Widget* view) { return adoptRef(new Plugin(view)); }
+ Widget* view() const { return m_view; }
+
+ private:
+ Plugin(Widget* view) : m_view(view) { }
+ Widget* m_view;
+ };
+
+} // namespace WebCore
+
+#endif // Plugin_h
diff --git a/WebCore/page/Screen.cpp b/WebCore/page/Screen.cpp
new file mode 100644
index 0000000..396a6f4
--- /dev/null
+++ b/WebCore/page/Screen.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 "Screen.h"
+
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "PlatformScreen.h"
+#include "Widget.h"
+
+namespace WebCore {
+
+Screen::Screen(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+void Screen::disconnectFrame()
+{
+ m_frame = 0;
+}
+
+unsigned Screen::height() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenRect(m_frame->view()).height());
+}
+
+unsigned Screen::width() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenRect(m_frame->view()).width());
+}
+
+unsigned Screen::colorDepth() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenDepth(m_frame->view()));
+}
+
+unsigned Screen::pixelDepth() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenDepth(m_frame->view()));
+}
+
+unsigned Screen::availLeft() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenAvailableRect(m_frame->view()).x());
+}
+
+unsigned Screen::availTop() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenAvailableRect(m_frame->view()).y());
+}
+
+unsigned Screen::availHeight() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenAvailableRect(m_frame->view()).height());
+}
+
+unsigned Screen::availWidth() const
+{
+ if (!m_frame)
+ return 0;
+ return static_cast<unsigned>(screenAvailableRect(m_frame->view()).width());
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/Screen.h b/WebCore/page/Screen.h
new file mode 100644
index 0000000..288b1ac
--- /dev/null
+++ b/WebCore/page/Screen.h
@@ -0,0 +1,62 @@
+/*
+ * 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 Screen_h
+#define Screen_h
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class Frame;
+
+ class Screen : public RefCounted<Screen> {
+ public:
+ static PassRefPtr<Screen> create(Frame *frame) { return adoptRef(new Screen(frame)); }
+ void disconnectFrame();
+
+ unsigned height() const;
+ unsigned width() const;
+ unsigned colorDepth() const;
+ unsigned pixelDepth() const;
+ unsigned availLeft() const;
+ unsigned availTop() const;
+ unsigned availHeight() const;
+ unsigned availWidth() const;
+
+ private:
+ Screen(Frame*);
+
+ Frame* m_frame;
+ };
+
+} // namespace WebCore
+
+#endif // Screen_h
diff --git a/WebCore/page/Screen.idl b/WebCore/page/Screen.idl
new file mode 100644
index 0000000..ca7d20d
--- /dev/null
+++ b/WebCore/page/Screen.idl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+
+module window {
+
+ interface Screen {
+ readonly attribute unsigned long height;
+ readonly attribute unsigned long width;
+ readonly attribute unsigned long colorDepth;
+ readonly attribute unsigned long pixelDepth;
+ readonly attribute unsigned long availLeft;
+ readonly attribute unsigned long availTop;
+ readonly attribute unsigned long availHeight;
+ readonly attribute unsigned long availWidth;
+ };
+
+}
diff --git a/WebCore/page/Settings.cpp b/WebCore/page/Settings.cpp
new file mode 100644
index 0000000..5253117
--- /dev/null
+++ b/WebCore/page/Settings.cpp
@@ -0,0 +1,326 @@
+/*
+ * 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 "Settings.h"
+
+#include "Frame.h"
+#include "FrameTree.h"
+#include "Page.h"
+#include "PageCache.h"
+#include "HistoryItem.h"
+
+#if ENABLE(DATABASE)
+#include "DatabaseTracker.h"
+#endif
+
+namespace WebCore {
+
+static void setNeedsReapplyStylesInAllFrames(Page* page)
+{
+ for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext())
+ frame->setNeedsReapplyStyles();
+}
+
+Settings::Settings(Page* page)
+ : m_page(page)
+ , m_editableLinkBehavior(EditableLinkDefaultBehavior)
+ , m_minimumFontSize(0)
+ , m_minimumLogicalFontSize(0)
+ , m_defaultFontSize(0)
+ , m_defaultFixedFontSize(0)
+ , m_isJavaEnabled(false)
+ , m_loadsImagesAutomatically(false)
+ , m_privateBrowsingEnabled(false)
+ , m_arePluginsEnabled(false)
+ , m_isJavaScriptEnabled(false)
+ , m_javaScriptCanOpenWindowsAutomatically(false)
+ , m_shouldPrintBackgrounds(false)
+ , m_textAreasAreResizable(false)
+ , m_usesDashboardBackwardCompatibilityMode(false)
+ , m_needsAdobeFrameReloadingQuirk(false)
+ , m_needsKeyboardEventDisambiguationQuirks(false)
+ , m_isDOMPasteAllowed(false)
+ , m_shrinksStandaloneImagesToFit(true)
+ , m_usesPageCache(false)
+ , m_showsURLsInToolTips(false)
+ , m_forceFTPDirectoryListings(false)
+ , m_developerExtrasEnabled(false)
+ , m_authorAndUserStylesEnabled(true)
+ , m_needsSiteSpecificQuirks(false)
+ , m_fontRenderingMode(0)
+{
+ // A Frame may not have been created yet, so we initialize the AtomicString
+ // hash before trying to use it.
+ AtomicString::init();
+}
+
+void Settings::setStandardFontFamily(const AtomicString& standardFontFamily)
+{
+ if (standardFontFamily == m_standardFontFamily)
+ return;
+
+ m_standardFontFamily = standardFontFamily;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setFixedFontFamily(const AtomicString& fixedFontFamily)
+{
+ if (m_fixedFontFamily == fixedFontFamily)
+ return;
+
+ m_fixedFontFamily = fixedFontFamily;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setSerifFontFamily(const AtomicString& serifFontFamily)
+{
+ if (m_serifFontFamily == serifFontFamily)
+ return;
+
+ m_serifFontFamily = serifFontFamily;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setSansSerifFontFamily(const AtomicString& sansSerifFontFamily)
+{
+ if (m_sansSerifFontFamily == sansSerifFontFamily)
+ return;
+
+ m_sansSerifFontFamily = sansSerifFontFamily;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setCursiveFontFamily(const AtomicString& cursiveFontFamily)
+{
+ if (m_cursiveFontFamily == cursiveFontFamily)
+ return;
+
+ m_cursiveFontFamily = cursiveFontFamily;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setFantasyFontFamily(const AtomicString& fantasyFontFamily)
+{
+ if (m_fantasyFontFamily == fantasyFontFamily)
+ return;
+
+ m_fantasyFontFamily = fantasyFontFamily;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setMinimumFontSize(int minimumFontSize)
+{
+ if (m_minimumFontSize == minimumFontSize)
+ return;
+
+ m_minimumFontSize = minimumFontSize;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setMinimumLogicalFontSize(int minimumLogicalFontSize)
+{
+ if (m_minimumLogicalFontSize == minimumLogicalFontSize)
+ return;
+
+ m_minimumLogicalFontSize = minimumLogicalFontSize;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setDefaultFontSize(int defaultFontSize)
+{
+ if (m_defaultFontSize == defaultFontSize)
+ return;
+
+ m_defaultFontSize = defaultFontSize;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setDefaultFixedFontSize(int defaultFontSize)
+{
+ if (m_defaultFixedFontSize == defaultFontSize)
+ return;
+
+ m_defaultFixedFontSize = defaultFontSize;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setLoadsImagesAutomatically(bool loadsImagesAutomatically)
+{
+ m_loadsImagesAutomatically = loadsImagesAutomatically;
+}
+
+void Settings::setJavaScriptEnabled(bool isJavaScriptEnabled)
+{
+ m_isJavaScriptEnabled = isJavaScriptEnabled;
+}
+
+void Settings::setJavaEnabled(bool isJavaEnabled)
+{
+ m_isJavaEnabled = isJavaEnabled;
+}
+
+void Settings::setPluginsEnabled(bool arePluginsEnabled)
+{
+ m_arePluginsEnabled = arePluginsEnabled;
+}
+
+void Settings::setPrivateBrowsingEnabled(bool privateBrowsingEnabled)
+{
+ m_privateBrowsingEnabled = privateBrowsingEnabled;
+}
+
+void Settings::setJavaScriptCanOpenWindowsAutomatically(bool javaScriptCanOpenWindowsAutomatically)
+{
+ m_javaScriptCanOpenWindowsAutomatically = javaScriptCanOpenWindowsAutomatically;
+}
+
+void Settings::setDefaultTextEncodingName(const String& defaultTextEncodingName)
+{
+ m_defaultTextEncodingName = defaultTextEncodingName;
+}
+
+void Settings::setUserStyleSheetLocation(const KURL& userStyleSheetLocation)
+{
+ if (m_userStyleSheetLocation == userStyleSheetLocation)
+ return;
+
+ m_userStyleSheetLocation = userStyleSheetLocation;
+
+ m_page->userStyleSheetLocationChanged();
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setShouldPrintBackgrounds(bool shouldPrintBackgrounds)
+{
+ m_shouldPrintBackgrounds = shouldPrintBackgrounds;
+}
+
+void Settings::setTextAreasAreResizable(bool textAreasAreResizable)
+{
+ if (m_textAreasAreResizable == textAreasAreResizable)
+ return;
+
+ m_textAreasAreResizable = textAreasAreResizable;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setEditableLinkBehavior(EditableLinkBehavior editableLinkBehavior)
+{
+ m_editableLinkBehavior = editableLinkBehavior;
+}
+
+void Settings::setUsesDashboardBackwardCompatibilityMode(bool usesDashboardBackwardCompatibilityMode)
+{
+ m_usesDashboardBackwardCompatibilityMode = usesDashboardBackwardCompatibilityMode;
+}
+
+// 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.
+void Settings::setNeedsAdobeFrameReloadingQuirk(bool shouldNotReloadIFramesForUnchangedSRC)
+{
+ m_needsAdobeFrameReloadingQuirk = shouldNotReloadIFramesForUnchangedSRC;
+}
+
+// This is a quirk we are pro-actively applying to old applications. It changes keyboard event dispatching,
+// making keyIdentifier available on keypress events, making charCode available on keydown/keyup events,
+// and getting keypress dispatched in more cases.
+void Settings::setNeedsKeyboardEventDisambiguationQuirks(bool needsQuirks)
+{
+ m_needsKeyboardEventDisambiguationQuirks = needsQuirks;
+}
+
+void Settings::setDOMPasteAllowed(bool DOMPasteAllowed)
+{
+ m_isDOMPasteAllowed = DOMPasteAllowed;
+}
+
+void Settings::setUsesPageCache(bool usesPageCache)
+{
+ if (m_usesPageCache == usesPageCache)
+ return;
+
+ m_usesPageCache = usesPageCache;
+ if (!m_usesPageCache) {
+ HistoryItemVector& historyItems = m_page->backForwardList()->entries();
+ for (unsigned i = 0; i < historyItems.size(); i++)
+ pageCache()->remove(historyItems[i].get());
+ pageCache()->releaseAutoreleasedPagesNow();
+ }
+}
+
+void Settings::setShrinksStandaloneImagesToFit(bool shrinksStandaloneImagesToFit)
+{
+ m_shrinksStandaloneImagesToFit = shrinksStandaloneImagesToFit;
+}
+
+void Settings::setShowsURLsInToolTips(bool showsURLsInToolTips)
+{
+ m_showsURLsInToolTips = showsURLsInToolTips;
+}
+
+void Settings::setFTPDirectoryTemplatePath(const String& path)
+{
+ m_ftpDirectoryTemplatePath = path;
+}
+
+void Settings::setForceFTPDirectoryListings(bool force)
+{
+ m_forceFTPDirectoryListings = force;
+}
+
+void Settings::setDeveloperExtrasEnabled(bool developerExtrasEnabled)
+{
+ m_developerExtrasEnabled = developerExtrasEnabled;
+}
+
+void Settings::setAuthorAndUserStylesEnabled(bool authorAndUserStylesEnabled)
+{
+ if (m_authorAndUserStylesEnabled == authorAndUserStylesEnabled)
+ return;
+
+ m_authorAndUserStylesEnabled = authorAndUserStylesEnabled;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+void Settings::setFontRenderingMode(FontRenderingMode mode)
+{
+ if (fontRenderingMode() == mode)
+ return;
+ m_fontRenderingMode = mode;
+ setNeedsReapplyStylesInAllFrames(m_page);
+}
+
+FontRenderingMode Settings::fontRenderingMode() const
+{
+ return static_cast<FontRenderingMode>(m_fontRenderingMode);
+}
+
+void Settings::setNeedsSiteSpecificQuirks(bool needsQuirks)
+{
+ m_needsSiteSpecificQuirks = needsQuirks;
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/Settings.h b/WebCore/page/Settings.h
new file mode 100644
index 0000000..704b486
--- /dev/null
+++ b/WebCore/page/Settings.h
@@ -0,0 +1,193 @@
+/*
+ * 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
+ * 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 Settings_h
+#define Settings_h
+
+#include "AtomicString.h"
+#include "FontDescription.h"
+#include "KURL.h"
+
+namespace WebCore {
+
+ class Page;
+
+ enum EditableLinkBehavior {
+ EditableLinkDefaultBehavior = 0,
+ EditableLinkAlwaysLive,
+ EditableLinkOnlyLiveWithShiftKey,
+ EditableLinkLiveWhenNotFocused,
+ EditableLinkNeverLive
+ };
+
+ class Settings {
+ public:
+ Settings(Page*);
+
+ void setStandardFontFamily(const AtomicString&);
+ const AtomicString& standardFontFamily() const { return m_standardFontFamily; }
+
+ void setFixedFontFamily(const AtomicString&);
+ const AtomicString& fixedFontFamily() const { return m_fixedFontFamily; }
+
+ void setSerifFontFamily(const AtomicString&);
+ const AtomicString& serifFontFamily() const { return m_serifFontFamily; }
+
+ void setSansSerifFontFamily(const AtomicString&);
+ const AtomicString& sansSerifFontFamily() const { return m_sansSerifFontFamily; }
+
+ void setCursiveFontFamily(const AtomicString&);
+ const AtomicString& cursiveFontFamily() const { return m_cursiveFontFamily; }
+
+ void setFantasyFontFamily(const AtomicString&);
+ const AtomicString& fantasyFontFamily() const { return m_fantasyFontFamily; }
+
+ void setMinimumFontSize(int);
+ int minimumFontSize() const { return m_minimumFontSize; }
+
+ void setMinimumLogicalFontSize(int);
+ int minimumLogicalFontSize() const { return m_minimumLogicalFontSize; }
+
+ void setDefaultFontSize(int);
+ int defaultFontSize() const { return m_defaultFontSize; }
+
+ void setDefaultFixedFontSize(int);
+ int defaultFixedFontSize() const { return m_defaultFixedFontSize; }
+
+ void setLoadsImagesAutomatically(bool);
+ bool loadsImagesAutomatically() const { return m_loadsImagesAutomatically; }
+
+ void setJavaScriptEnabled(bool);
+ bool isJavaScriptEnabled() const { return m_isJavaScriptEnabled; }
+
+ void setJavaScriptCanOpenWindowsAutomatically(bool);
+ bool JavaScriptCanOpenWindowsAutomatically() const { return m_javaScriptCanOpenWindowsAutomatically; }
+
+ void setJavaEnabled(bool);
+ bool isJavaEnabled() const { return m_isJavaEnabled; }
+
+ void setPluginsEnabled(bool);
+ bool arePluginsEnabled() const { return m_arePluginsEnabled; }
+
+ void setPrivateBrowsingEnabled(bool);
+ bool privateBrowsingEnabled() const { return m_privateBrowsingEnabled; }
+
+ void setDefaultTextEncodingName(const String&);
+ const String& defaultTextEncodingName() const { return m_defaultTextEncodingName; }
+
+ void setUserStyleSheetLocation(const KURL&);
+ const KURL& userStyleSheetLocation() const { return m_userStyleSheetLocation; }
+
+ void setShouldPrintBackgrounds(bool);
+ bool shouldPrintBackgrounds() const { return m_shouldPrintBackgrounds; }
+
+ void setTextAreasAreResizable(bool);
+ bool textAreasAreResizable() const { return m_textAreasAreResizable; }
+
+ void setEditableLinkBehavior(EditableLinkBehavior);
+ EditableLinkBehavior editableLinkBehavior() const { return m_editableLinkBehavior; }
+
+ void setUsesDashboardBackwardCompatibilityMode(bool);
+ bool usesDashboardBackwardCompatibilityMode() const { return m_usesDashboardBackwardCompatibilityMode; }
+
+ void setNeedsAdobeFrameReloadingQuirk(bool);
+ bool needsAcrobatFrameReloadingQuirk() const { return m_needsAdobeFrameReloadingQuirk; }
+
+ void setNeedsKeyboardEventDisambiguationQuirks(bool);
+ bool needsKeyboardEventDisambiguationQuirks() const { return m_needsKeyboardEventDisambiguationQuirks; }
+
+ void setDOMPasteAllowed(bool);
+ bool isDOMPasteAllowed() const { return m_isDOMPasteAllowed; }
+
+ void setUsesPageCache(bool);
+ bool usesPageCache() const { return m_usesPageCache; }
+
+ void setShrinksStandaloneImagesToFit(bool);
+ bool shrinksStandaloneImagesToFit() const { return m_shrinksStandaloneImagesToFit; }
+
+ void setShowsURLsInToolTips(bool);
+ bool showsURLsInToolTips() const { return m_showsURLsInToolTips; }
+
+ void setFTPDirectoryTemplatePath(const String&);
+ const String& ftpDirectoryTemplatePath() const { return m_ftpDirectoryTemplatePath; }
+
+ void setForceFTPDirectoryListings(bool);
+ bool forceFTPDirectoryListings() const { return m_forceFTPDirectoryListings; }
+
+ void setDeveloperExtrasEnabled(bool);
+ bool developerExtrasEnabled() const { return m_developerExtrasEnabled; }
+
+ void setAuthorAndUserStylesEnabled(bool);
+ bool authorAndUserStylesEnabled() const { return m_authorAndUserStylesEnabled; }
+
+ void setFontRenderingMode(FontRenderingMode mode);
+ FontRenderingMode fontRenderingMode() const;
+
+ void setNeedsSiteSpecificQuirks(bool);
+ bool needsSiteSpecificQuirks() const { return m_needsSiteSpecificQuirks; }
+
+ private:
+ Page* m_page;
+
+ String m_defaultTextEncodingName;
+ String m_ftpDirectoryTemplatePath;
+ KURL m_userStyleSheetLocation;
+ AtomicString m_standardFontFamily;
+ AtomicString m_fixedFontFamily;
+ AtomicString m_serifFontFamily;
+ AtomicString m_sansSerifFontFamily;
+ AtomicString m_cursiveFontFamily;
+ AtomicString m_fantasyFontFamily;
+ EditableLinkBehavior m_editableLinkBehavior;
+ int m_minimumFontSize;
+ int m_minimumLogicalFontSize;
+ int m_defaultFontSize;
+ int m_defaultFixedFontSize;
+ bool m_isJavaEnabled : 1;
+ bool m_loadsImagesAutomatically : 1;
+ bool m_privateBrowsingEnabled : 1;
+ bool m_arePluginsEnabled : 1;
+ bool m_isJavaScriptEnabled : 1;
+ bool m_javaScriptCanOpenWindowsAutomatically : 1;
+ bool m_shouldPrintBackgrounds : 1;
+ bool m_textAreasAreResizable : 1;
+ bool m_usesDashboardBackwardCompatibilityMode : 1;
+ bool m_needsAdobeFrameReloadingQuirk : 1;
+ bool m_needsKeyboardEventDisambiguationQuirks : 1;
+ bool m_isDOMPasteAllowed : 1;
+ bool m_shrinksStandaloneImagesToFit : 1;
+ bool m_usesPageCache: 1;
+ bool m_showsURLsInToolTips : 1;
+ bool m_forceFTPDirectoryListings : 1;
+ bool m_developerExtrasEnabled : 1;
+ bool m_authorAndUserStylesEnabled : 1;
+ bool m_needsSiteSpecificQuirks : 1;
+ unsigned m_fontRenderingMode : 1;
+ };
+
+} // namespace WebCore
+
+#endif // Settings_h
diff --git a/WebCore/page/WindowFeatures.cpp b/WebCore/page/WindowFeatures.cpp
new file mode 100644
index 0000000..c499a4a
--- /dev/null
+++ b/WebCore/page/WindowFeatures.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2000 Harri Porten (porten@kde.org)
+ * Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reseved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
+ *
+ * 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 "WindowFeatures.h"
+
+#include "PlatformString.h"
+#include "StringHash.h"
+#include <wtf/Assertions.h>
+#include <wtf/HashMap.h>
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+// Though isspace() considers \t and \v to be whitespace, Win IE doesn't.
+static bool isSeparator(UChar c)
+{
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0';
+}
+
+WindowFeatures::WindowFeatures(const String& features)
+ : xSet(false)
+ , ySet(false)
+ , widthSet(false)
+ , heightSet(false)
+ , fullscreen(false)
+ , dialog(false)
+{
+ /*
+ The IE rule is: all features except for channelmode and fullscreen default to YES, but
+ if the user specifies a feature string, all features default to NO. (There is no public
+ standard that applies to this method.)
+
+ <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp>
+ We always allow a window to be resized, which is consistent with Firefox.
+ */
+
+ if (features.length() == 0) {
+ menuBarVisible = true;
+ statusBarVisible = true;
+ toolBarVisible = true;
+ locationBarVisible = true;
+ scrollbarsVisible = true;
+ resizable = true;
+ return;
+ }
+
+ menuBarVisible = false;
+ statusBarVisible = false;
+ toolBarVisible = false;
+ locationBarVisible = false;
+ scrollbarsVisible = false;
+ resizable = true;
+
+ // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior.
+ int keyBegin, keyEnd;
+ int valueBegin, valueEnd;
+
+ int i = 0;
+ int length = features.length();
+ String buffer = features.lower();
+ while (i < length) {
+ // skip to first non-separator, but don't skip past the end of the string
+ while (isSeparator(buffer[i])) {
+ if (i >= length)
+ break;
+ i++;
+ }
+ keyBegin = i;
+
+ // skip to first separator
+ while (!isSeparator(buffer[i]))
+ i++;
+ keyEnd = i;
+
+ // skip to first '=', but don't skip past a ',' or the end of the string
+ while (buffer[i] != '=') {
+ if (buffer[i] == ',' || i >= length)
+ break;
+ i++;
+ }
+
+ // skip to first non-separator, but don't skip past a ',' or the end of the string
+ while (isSeparator(buffer[i])) {
+ if (buffer[i] == ',' || i >= length)
+ break;
+ i++;
+ }
+ valueBegin = i;
+
+ // skip to first separator
+ while (!isSeparator(buffer[i]))
+ i++;
+ valueEnd = i;
+
+ ASSERT(i <= length);
+
+ String keyString(buffer.substring(keyBegin, keyEnd - keyBegin));
+ String valueString(buffer.substring(valueBegin, valueEnd - valueBegin));
+ setWindowFeature(keyString, valueString);
+ }
+}
+
+void WindowFeatures::setWindowFeature(const String& keyString, const String& valueString)
+{
+ int value;
+
+ // Listing a key with no value is shorthand for key=yes
+ if (valueString.length() == 0 || valueString == "yes")
+ value = 1;
+ else
+ value = valueString.toInt();
+
+ // We ignore a keyString of "resizable", which is consistent with Firefox.
+ if (keyString == "left" || keyString == "screenx") {
+ xSet = true;
+ x = value;
+ } else if (keyString == "top" || keyString == "screeny") {
+ ySet = true;
+ y = value;
+ } else if (keyString == "width" || keyString == "innerwidth") {
+ widthSet = true;
+ width = value;
+ } else if (keyString == "height" || keyString == "innerheight") {
+ heightSet = true;
+ height = value;
+ } else if (keyString == "menubar")
+ menuBarVisible = value;
+ else if (keyString == "toolbar")
+ toolBarVisible = value;
+ else if (keyString == "location")
+ locationBarVisible = value;
+ else if (keyString == "status")
+ statusBarVisible = value;
+ else if (keyString == "fullscreen")
+ fullscreen = value;
+ else if (keyString == "scrollbars")
+ scrollbarsVisible = value;
+}
+
+bool WindowFeatures::boolFeature(const HashMap<String, String>& features, const char* key, bool defaultValue)
+{
+ HashMap<String, String>::const_iterator it = features.find(key);
+ if (it == features.end())
+ return defaultValue;
+ const String& value = it->second;
+ return value.isNull() || value == "1" || value == "yes" || value == "on";
+}
+
+float WindowFeatures::floatFeature(const HashMap<String, String>& features, const char* key, float min, float max, float defaultValue)
+{
+ HashMap<String, String>::const_iterator it = features.find(key);
+ if (it == features.end())
+ return defaultValue;
+ // FIXME: Can't distinguish "0q" from string with no digits in it -- both return d == 0 and ok == false.
+ // Would be good to tell them apart somehow since string with no digits should be default value and
+ // "0q" should be minimum value.
+ bool ok;
+ double d = it->second.toDouble(&ok);
+ if ((d == 0 && !ok) || isnan(d))
+ return defaultValue;
+ if (d < min || max <= min)
+ return min;
+ if (d > max)
+ return max;
+ return static_cast<int>(d);
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/WindowFeatures.h b/WebCore/page/WindowFeatures.h
new file mode 100644
index 0000000..a12cf05
--- /dev/null
+++ b/WebCore/page/WindowFeatures.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2003, 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 WindowFeatures_h
+#define WindowFeatures_h
+
+#include "PlatformString.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+ struct WindowFeatures {
+ WindowFeatures()
+ : xSet(false)
+ , ySet(false)
+ , widthSet(false)
+ , heightSet(false)
+ , menuBarVisible(true)
+ , statusBarVisible(true)
+ , toolBarVisible(true)
+ , locationBarVisible(true)
+ , scrollbarsVisible(true)
+ , resizable(true)
+ , fullscreen(false)
+ , dialog(false)
+ {
+ }
+
+ WindowFeatures(const String& features);
+
+ void setWindowFeature(const String& keyString, const String& valueString);
+
+ static bool boolFeature(const HashMap<String, String>& features, const char* key, bool defaultValue = false);
+ static float floatFeature(const HashMap<String, String>& features, const char* key, float min, float max, float defaultValue);
+
+ float x;
+ bool xSet;
+ float y;
+ bool ySet;
+ float width;
+ bool widthSet;
+ float height;
+ bool heightSet;
+
+ bool menuBarVisible;
+ bool statusBarVisible;
+ bool toolBarVisible;
+ bool locationBarVisible;
+ bool scrollbarsVisible;
+ bool resizable;
+
+ bool fullscreen;
+ bool dialog;
+ };
+
+} // namespace WebCore
+
+#endif // WindowFeatures_h
diff --git a/WebCore/page/gtk/DragControllerGtk.cpp b/WebCore/page/gtk/DragControllerGtk.cpp
new file mode 100644
index 0000000..62f4421
--- /dev/null
+++ b/WebCore/page/gtk/DragControllerGtk.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ *
+ * 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 "DragController.h"
+
+#include "DragData.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "Page.h"
+
+namespace WebCore {
+
+// FIXME: These values are straight out of DragControllerMac, so probably have
+// little correlation with Gdk standards...
+const int DragController::LinkDragBorderInset = 2;
+const int DragController::MaxOriginalImageArea = 1500 * 1500;
+const int DragController::DragIconRightInset = 7;
+const int DragController::DragIconBottomInset = 3;
+
+const float DragController::DragImageAlpha = 0.75f;
+
+bool DragController::isCopyKeyDown()
+{
+ return false;
+}
+
+DragOperation DragController::dragOperation(DragData* dragData)
+{
+ //FIXME: This logic is incomplete
+ if (dragData->containsURL())
+ return DragOperationCopy;
+
+ return DragOperationNone;
+}
+
+const IntSize& DragController::maxDragImageSize()
+{
+ static const IntSize maxDragImageSize(400, 400);
+
+ return maxDragImageSize;
+}
+
+}
diff --git a/WebCore/page/gtk/EventHandlerGtk.cpp b/WebCore/page/gtk/EventHandlerGtk.cpp
new file mode 100644
index 0000000..8215a4e
--- /dev/null
+++ b/WebCore/page/gtk/EventHandlerGtk.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2006 Zack Rusin <zack@kde.org>
+ *
+ * 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 "EventHandler.h"
+
+#include "ClipboardGtk.h"
+#include "EventNames.h"
+#include "FloatPoint.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "KeyboardEvent.h"
+#include "MouseEventWithHitTestResults.h"
+#include "NotImplemented.h"
+#include "Page.h"
+#include "PlatformScrollBar.h"
+#include "PlatformWheelEvent.h"
+#include "RenderWidget.h"
+
+namespace WebCore {
+
+using namespace EventNames;
+
+bool EventHandler::tabsToAllControls(KeyboardEvent* event) const
+{
+ // We always allow tabs to all controls
+ return true;
+}
+
+void EventHandler::focusDocumentView()
+{
+ if (Page* page = m_frame->page())
+ page->focusController()->setFocusedFrame(m_frame);
+}
+
+bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
+{
+ // Figure out which view to send the event to.
+ RenderObject* target = event.targetNode() ? event.targetNode()->renderer() : 0;
+ if (!target || !target->isWidget())
+ return false;
+
+ return passMouseDownEventToWidget(static_cast<RenderWidget*>(target)->widget());
+}
+
+bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
+{
+ return passMouseDownEventToWidget(renderWidget->widget());
+}
+
+bool EventHandler::passMouseDownEventToWidget(Widget* widget)
+{
+ notImplemented();
+ return false;
+}
+
+bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const
+{
+ //GTK+ activation is not necessarily tied to mouse events, so it may
+ //not make sense to implement this
+
+ notImplemented();
+ return false;
+}
+
+bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& event, Widget* widget)
+{
+ ASSERT(widget);
+ if (!widget->isFrameView())
+ return false;
+
+ return static_cast<FrameView*>(widget)->frame()->eventHandler()->handleWheelEvent(event);
+}
+
+Clipboard* EventHandler::createDraggingClipboard() const
+{
+ return new ClipboardGtk(ClipboardWritable, true);
+}
+
+bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ subframe->eventHandler()->handleMousePressEvent(mev.event());
+ return true;
+}
+
+bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
+{
+ subframe->eventHandler()->handleMouseMoveEvent(mev.event(), hoveredNode);
+ return true;
+}
+
+bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ subframe->eventHandler()->handleMouseReleaseEvent(mev.event());
+ 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
new file mode 100644
index 0000000..b2afcbd
--- /dev/null
+++ b/WebCore/page/gtk/FrameGtk.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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:
+ * 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 "Frame.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
new file mode 100644
index 0000000..57bf331
--- /dev/null
+++ b/WebCore/page/inspector/ConsolePanel.js
@@ -0,0 +1,459 @@
+/*
+ * 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
new file mode 100644
index 0000000..36e89da
--- /dev/null
+++ b/WebCore/page/inspector/Database.js
@@ -0,0 +1,129 @@
+/*
+ * 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
new file mode 100644
index 0000000..9101b9c
--- /dev/null
+++ b/WebCore/page/inspector/DatabasePanel.js
@@ -0,0 +1,462 @@
+/*
+ * 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
new file mode 100644
index 0000000..8528196
--- /dev/null
+++ b/WebCore/page/inspector/DocumentPanel.js
@@ -0,0 +1,836 @@
+/*
+ * 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\">&lt;/" + this.representedObject.nodeName.toLowerCase().escapeHTML() + "&gt;</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.listItemElement.scrollIntoViewIfNeeded(false);
+ },
+
+ 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
new file mode 100644
index 0000000..c1ffd2f
--- /dev/null
+++ b/WebCore/page/inspector/FontPanel.js
@@ -0,0 +1,103 @@
+/*
+ * 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
new file mode 100644
index 0000000..402b754
--- /dev/null
+++ b/WebCore/page/inspector/ImagePanel.js
@@ -0,0 +1,74 @@
+/*
+ * 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
new file mode 100644
index 0000000..7706f50
--- /dev/null
+++ b/WebCore/page/inspector/Images/alternateTableRows.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/attachedShadow.png b/WebCore/page/inspector/Images/attachedShadow.png
new file mode 100644
index 0000000..b0b687c
--- /dev/null
+++ b/WebCore/page/inspector/Images/attachedShadow.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/backNormal.png b/WebCore/page/inspector/Images/backNormal.png
new file mode 100644
index 0000000..cff21a5
--- /dev/null
+++ b/WebCore/page/inspector/Images/backNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/bottomShadow.png b/WebCore/page/inspector/Images/bottomShadow.png
new file mode 100644
index 0000000..61699c4
--- /dev/null
+++ b/WebCore/page/inspector/Images/bottomShadow.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/breadcrumbBackground.png b/WebCore/page/inspector/Images/breadcrumbBackground.png
new file mode 100644
index 0000000..516452b
--- /dev/null
+++ b/WebCore/page/inspector/Images/breadcrumbBackground.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/checker.png b/WebCore/page/inspector/Images/checker.png
new file mode 100644
index 0000000..8349908
--- /dev/null
+++ b/WebCore/page/inspector/Images/checker.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/console.png b/WebCore/page/inspector/Images/console.png
new file mode 100644
index 0000000..c85fed0
--- /dev/null
+++ b/WebCore/page/inspector/Images/console.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/darkShadow.png b/WebCore/page/inspector/Images/darkShadow.png
new file mode 100644
index 0000000..2761b7d
--- /dev/null
+++ b/WebCore/page/inspector/Images/darkShadow.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/database.png b/WebCore/page/inspector/Images/database.png
new file mode 100644
index 0000000..2c13789
--- /dev/null
+++ b/WebCore/page/inspector/Images/database.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseBrowserViewNormal.png b/WebCore/page/inspector/Images/databaseBrowserViewNormal.png
new file mode 100644
index 0000000..0d55522
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseBrowserViewNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseBrowserViewNormalSelected.png b/WebCore/page/inspector/Images/databaseBrowserViewNormalSelected.png
new file mode 100644
index 0000000..af32982
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseBrowserViewNormalSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseBrowserViewSmall.png b/WebCore/page/inspector/Images/databaseBrowserViewSmall.png
new file mode 100644
index 0000000..fa33eec
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseBrowserViewSmall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseBrowserViewSmallSelected.png b/WebCore/page/inspector/Images/databaseBrowserViewSmallSelected.png
new file mode 100644
index 0000000..6880954
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseBrowserViewSmallSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseQueryViewNormal.png b/WebCore/page/inspector/Images/databaseQueryViewNormal.png
new file mode 100644
index 0000000..326e8c3
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseQueryViewNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseQueryViewNormalSelected.png b/WebCore/page/inspector/Images/databaseQueryViewNormalSelected.png
new file mode 100644
index 0000000..8ffda5c
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseQueryViewNormalSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseQueryViewSmall.png b/WebCore/page/inspector/Images/databaseQueryViewSmall.png
new file mode 100644
index 0000000..d11a127
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseQueryViewSmall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/databaseQueryViewSmallSelected.png b/WebCore/page/inspector/Images/databaseQueryViewSmallSelected.png
new file mode 100644
index 0000000..35be952
--- /dev/null
+++ b/WebCore/page/inspector/Images/databaseQueryViewSmallSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/disclosureDownPressed.png b/WebCore/page/inspector/Images/disclosureDownPressed.png
new file mode 100644
index 0000000..32ac517
--- /dev/null
+++ b/WebCore/page/inspector/Images/disclosureDownPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/disclosureRightDown.png b/WebCore/page/inspector/Images/disclosureRightDown.png
new file mode 100644
index 0000000..104ea86
--- /dev/null
+++ b/WebCore/page/inspector/Images/disclosureRightDown.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/disclosureRightPressed.png b/WebCore/page/inspector/Images/disclosureRightPressed.png
new file mode 100644
index 0000000..41c9b20
--- /dev/null
+++ b/WebCore/page/inspector/Images/disclosureRightPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/document.png b/WebCore/page/inspector/Images/document.png
new file mode 100644
index 0000000..fa9c3f5
--- /dev/null
+++ b/WebCore/page/inspector/Images/document.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/domViewNormal.png b/WebCore/page/inspector/Images/domViewNormal.png
new file mode 100644
index 0000000..6cd4ac4
--- /dev/null
+++ b/WebCore/page/inspector/Images/domViewNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/domViewNormalSelected.png b/WebCore/page/inspector/Images/domViewNormalSelected.png
new file mode 100644
index 0000000..5831819
--- /dev/null
+++ b/WebCore/page/inspector/Images/domViewNormalSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/domViewSmall.png b/WebCore/page/inspector/Images/domViewSmall.png
new file mode 100644
index 0000000..e9fb0da
--- /dev/null
+++ b/WebCore/page/inspector/Images/domViewSmall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/domViewSmallSelected.png b/WebCore/page/inspector/Images/domViewSmallSelected.png
new file mode 100644
index 0000000..517479d
--- /dev/null
+++ b/WebCore/page/inspector/Images/domViewSmallSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/downTriangle.png b/WebCore/page/inspector/Images/downTriangle.png
new file mode 100644
index 0000000..18a2a89
--- /dev/null
+++ b/WebCore/page/inspector/Images/downTriangle.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/errorIcon.png b/WebCore/page/inspector/Images/errorIcon.png
new file mode 100644
index 0000000..d6ec461
--- /dev/null
+++ b/WebCore/page/inspector/Images/errorIcon.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/errorMediumIcon.png b/WebCore/page/inspector/Images/errorMediumIcon.png
new file mode 100644
index 0000000..6ca32bb
--- /dev/null
+++ b/WebCore/page/inspector/Images/errorMediumIcon.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/folder.png b/WebCore/page/inspector/Images/folder.png
new file mode 100644
index 0000000..9bd7d9e
--- /dev/null
+++ b/WebCore/page/inspector/Images/folder.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/forwardNormal.png b/WebCore/page/inspector/Images/forwardNormal.png
new file mode 100644
index 0000000..5cb2c1b
--- /dev/null
+++ b/WebCore/page/inspector/Images/forwardNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/glossyHeader.png b/WebCore/page/inspector/Images/glossyHeader.png
new file mode 100644
index 0000000..8c80b6b
--- /dev/null
+++ b/WebCore/page/inspector/Images/glossyHeader.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/glossyHeaderPressed.png b/WebCore/page/inspector/Images/glossyHeaderPressed.png
new file mode 100644
index 0000000..6b0dd60
--- /dev/null
+++ b/WebCore/page/inspector/Images/glossyHeaderPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/goArrow.png b/WebCore/page/inspector/Images/goArrow.png
new file mode 100644
index 0000000..f318a56
--- /dev/null
+++ b/WebCore/page/inspector/Images/goArrow.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/gradient.png b/WebCore/page/inspector/Images/gradient.png
new file mode 100644
index 0000000..aa8568b
--- /dev/null
+++ b/WebCore/page/inspector/Images/gradient.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/gradientHighlight.png b/WebCore/page/inspector/Images/gradientHighlight.png
new file mode 100644
index 0000000..93bc9c1
--- /dev/null
+++ b/WebCore/page/inspector/Images/gradientHighlight.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/gradientHighlightBottom.png b/WebCore/page/inspector/Images/gradientHighlightBottom.png
new file mode 100644
index 0000000..89b0b67
--- /dev/null
+++ b/WebCore/page/inspector/Images/gradientHighlightBottom.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/hideStatusWidget.png b/WebCore/page/inspector/Images/hideStatusWidget.png
new file mode 100644
index 0000000..52ecece
--- /dev/null
+++ b/WebCore/page/inspector/Images/hideStatusWidget.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/hideStatusWidgetPressed.png b/WebCore/page/inspector/Images/hideStatusWidgetPressed.png
new file mode 100644
index 0000000..f041662
--- /dev/null
+++ b/WebCore/page/inspector/Images/hideStatusWidgetPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/network.png b/WebCore/page/inspector/Images/network.png
new file mode 100644
index 0000000..bc215bc
--- /dev/null
+++ b/WebCore/page/inspector/Images/network.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/paneBottomGrow.png b/WebCore/page/inspector/Images/paneBottomGrow.png
new file mode 100644
index 0000000..d55b865
--- /dev/null
+++ b/WebCore/page/inspector/Images/paneBottomGrow.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/paneBottomGrowActive.png b/WebCore/page/inspector/Images/paneBottomGrowActive.png
new file mode 100644
index 0000000..ef3f259
--- /dev/null
+++ b/WebCore/page/inspector/Images/paneBottomGrowActive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/paneGrowHandleLine.png b/WebCore/page/inspector/Images/paneGrowHandleLine.png
new file mode 100644
index 0000000..4eaf61b
--- /dev/null
+++ b/WebCore/page/inspector/Images/paneGrowHandleLine.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/paneHeader.png b/WebCore/page/inspector/Images/paneHeader.png
new file mode 100644
index 0000000..cc66afa
--- /dev/null
+++ b/WebCore/page/inspector/Images/paneHeader.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/paneHeaderActive.png b/WebCore/page/inspector/Images/paneHeaderActive.png
new file mode 100644
index 0000000..08205fb
--- /dev/null
+++ b/WebCore/page/inspector/Images/paneHeaderActive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/plainDocument.png b/WebCore/page/inspector/Images/plainDocument.png
new file mode 100644
index 0000000..2e92b71
--- /dev/null
+++ b/WebCore/page/inspector/Images/plainDocument.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/popupArrows.png b/WebCore/page/inspector/Images/popupArrows.png
new file mode 100644
index 0000000..b980e46
--- /dev/null
+++ b/WebCore/page/inspector/Images/popupArrows.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/popupArrowsBlack.png b/WebCore/page/inspector/Images/popupArrowsBlack.png
new file mode 100644
index 0000000..9c9b061
--- /dev/null
+++ b/WebCore/page/inspector/Images/popupArrowsBlack.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/reload.png b/WebCore/page/inspector/Images/reload.png
new file mode 100644
index 0000000..9797d26
--- /dev/null
+++ b/WebCore/page/inspector/Images/reload.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/rightTriangle.png b/WebCore/page/inspector/Images/rightTriangle.png
new file mode 100644
index 0000000..9f519f2
--- /dev/null
+++ b/WebCore/page/inspector/Images/rightTriangle.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/segment.png b/WebCore/page/inspector/Images/segment.png
new file mode 100644
index 0000000..bcef26a
--- /dev/null
+++ b/WebCore/page/inspector/Images/segment.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/segmentEnd.png b/WebCore/page/inspector/Images/segmentEnd.png
new file mode 100644
index 0000000..374a53d
--- /dev/null
+++ b/WebCore/page/inspector/Images/segmentEnd.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/segmentHover.png b/WebCore/page/inspector/Images/segmentHover.png
new file mode 100644
index 0000000..f54c029
--- /dev/null
+++ b/WebCore/page/inspector/Images/segmentHover.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/segmentHoverEnd.png b/WebCore/page/inspector/Images/segmentHoverEnd.png
new file mode 100644
index 0000000..e377c2d
--- /dev/null
+++ b/WebCore/page/inspector/Images/segmentHoverEnd.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/segmentSelected.png b/WebCore/page/inspector/Images/segmentSelected.png
new file mode 100644
index 0000000..a7f3a8f
--- /dev/null
+++ b/WebCore/page/inspector/Images/segmentSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/segmentSelectedEnd.png b/WebCore/page/inspector/Images/segmentSelectedEnd.png
new file mode 100644
index 0000000..be2c61b
--- /dev/null
+++ b/WebCore/page/inspector/Images/segmentSelectedEnd.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/showStatusWidget.png b/WebCore/page/inspector/Images/showStatusWidget.png
new file mode 100644
index 0000000..051d883
--- /dev/null
+++ b/WebCore/page/inspector/Images/showStatusWidget.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/showStatusWidgetPressed.png b/WebCore/page/inspector/Images/showStatusWidgetPressed.png
new file mode 100644
index 0000000..5622c9c
--- /dev/null
+++ b/WebCore/page/inspector/Images/showStatusWidgetPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidbarItemBackground.png b/WebCore/page/inspector/Images/sidbarItemBackground.png
new file mode 100644
index 0000000..5b94167
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidbarItemBackground.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarActionWidget.png b/WebCore/page/inspector/Images/sidebarActionWidget.png
new file mode 100644
index 0000000..daa1aff
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarActionWidget.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarActionWidgetPressed.png b/WebCore/page/inspector/Images/sidebarActionWidgetPressed.png
new file mode 100644
index 0000000..794b187
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarActionWidgetPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarAttachWidget.png b/WebCore/page/inspector/Images/sidebarAttachWidget.png
new file mode 100644
index 0000000..5f13f36
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarAttachWidget.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarAttachWidgetPressed.png b/WebCore/page/inspector/Images/sidebarAttachWidgetPressed.png
new file mode 100644
index 0000000..f70dd32
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarAttachWidgetPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarDetachWidget.png b/WebCore/page/inspector/Images/sidebarDetachWidget.png
new file mode 100644
index 0000000..2443c21
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarDetachWidget.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarDetachWidgetPressed.png b/WebCore/page/inspector/Images/sidebarDetachWidgetPressed.png
new file mode 100644
index 0000000..e13f4c1
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarDetachWidgetPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarResizeWidget.png b/WebCore/page/inspector/Images/sidebarResizeWidget.png
new file mode 100644
index 0000000..7c1ff70
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarResizeWidget.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarSelection.png b/WebCore/page/inspector/Images/sidebarSelection.png
new file mode 100644
index 0000000..fccfa0f
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarSelection.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarSelectionBlurred.png b/WebCore/page/inspector/Images/sidebarSelectionBlurred.png
new file mode 100644
index 0000000..1e49cdf
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarSelectionBlurred.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarSelectionBlurredTall.png b/WebCore/page/inspector/Images/sidebarSelectionBlurredTall.png
new file mode 100644
index 0000000..33a7cfc
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarSelectionBlurredTall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarSelectionGray.png b/WebCore/page/inspector/Images/sidebarSelectionGray.png
new file mode 100644
index 0000000..f4faf2c
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarSelectionGray.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarSelectionGrayTall.png b/WebCore/page/inspector/Images/sidebarSelectionGrayTall.png
new file mode 100644
index 0000000..01379d2
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarSelectionGrayTall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarSelectionTall.png b/WebCore/page/inspector/Images/sidebarSelectionTall.png
new file mode 100644
index 0000000..0bb56da
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarSelectionTall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sidebarStatusAreaBackground.png b/WebCore/page/inspector/Images/sidebarStatusAreaBackground.png
new file mode 100644
index 0000000..0127d9d
--- /dev/null
+++ b/WebCore/page/inspector/Images/sidebarStatusAreaBackground.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sourceViewNormal.png b/WebCore/page/inspector/Images/sourceViewNormal.png
new file mode 100644
index 0000000..2b23460
--- /dev/null
+++ b/WebCore/page/inspector/Images/sourceViewNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sourceViewNormalSelected.png b/WebCore/page/inspector/Images/sourceViewNormalSelected.png
new file mode 100644
index 0000000..0c24b1c
--- /dev/null
+++ b/WebCore/page/inspector/Images/sourceViewNormalSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sourceViewSmall.png b/WebCore/page/inspector/Images/sourceViewSmall.png
new file mode 100644
index 0000000..82d06ef
--- /dev/null
+++ b/WebCore/page/inspector/Images/sourceViewSmall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/sourceViewSmallSelected.png b/WebCore/page/inspector/Images/sourceViewSmallSelected.png
new file mode 100644
index 0000000..fe0351e
--- /dev/null
+++ b/WebCore/page/inspector/Images/sourceViewSmallSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/splitviewDimple.png b/WebCore/page/inspector/Images/splitviewDimple.png
new file mode 100644
index 0000000..584ffd4
--- /dev/null
+++ b/WebCore/page/inspector/Images/splitviewDimple.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/splitviewDividerBackground.png b/WebCore/page/inspector/Images/splitviewDividerBackground.png
new file mode 100644
index 0000000..1120a7f
--- /dev/null
+++ b/WebCore/page/inspector/Images/splitviewDividerBackground.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/tab.png b/WebCore/page/inspector/Images/tab.png
new file mode 100644
index 0000000..2f5000a
--- /dev/null
+++ b/WebCore/page/inspector/Images/tab.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/tabSelected.png b/WebCore/page/inspector/Images/tabSelected.png
new file mode 100644
index 0000000..76c2f66
--- /dev/null
+++ b/WebCore/page/inspector/Images/tabSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/timelinePillBlue.png b/WebCore/page/inspector/Images/timelinePillBlue.png
new file mode 100644
index 0000000..c897faa
--- /dev/null
+++ b/WebCore/page/inspector/Images/timelinePillBlue.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/timelinePillGray.png b/WebCore/page/inspector/Images/timelinePillGray.png
new file mode 100644
index 0000000..2128896
--- /dev/null
+++ b/WebCore/page/inspector/Images/timelinePillGray.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/timelinePillGreen.png b/WebCore/page/inspector/Images/timelinePillGreen.png
new file mode 100644
index 0000000..9b66125
--- /dev/null
+++ b/WebCore/page/inspector/Images/timelinePillGreen.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/timelinePillOrange.png b/WebCore/page/inspector/Images/timelinePillOrange.png
new file mode 100644
index 0000000..dd944fb
--- /dev/null
+++ b/WebCore/page/inspector/Images/timelinePillOrange.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/timelinePillPurple.png b/WebCore/page/inspector/Images/timelinePillPurple.png
new file mode 100644
index 0000000..21b96f7
--- /dev/null
+++ b/WebCore/page/inspector/Images/timelinePillPurple.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/timelinePillRed.png b/WebCore/page/inspector/Images/timelinePillRed.png
new file mode 100644
index 0000000..f5e213b
--- /dev/null
+++ b/WebCore/page/inspector/Images/timelinePillRed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/timelinePillYellow.png b/WebCore/page/inspector/Images/timelinePillYellow.png
new file mode 100644
index 0000000..ae2a5a2
--- /dev/null
+++ b/WebCore/page/inspector/Images/timelinePillYellow.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/tipBalloon.png b/WebCore/page/inspector/Images/tipBalloon.png
new file mode 100644
index 0000000..4cdf738
--- /dev/null
+++ b/WebCore/page/inspector/Images/tipBalloon.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/tipBalloonBottom.png b/WebCore/page/inspector/Images/tipBalloonBottom.png
new file mode 100644
index 0000000..3317a5a
--- /dev/null
+++ b/WebCore/page/inspector/Images/tipBalloonBottom.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/tipIcon.png b/WebCore/page/inspector/Images/tipIcon.png
new file mode 100644
index 0000000..8ca6124
--- /dev/null
+++ b/WebCore/page/inspector/Images/tipIcon.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/tipIconPressed.png b/WebCore/page/inspector/Images/tipIconPressed.png
new file mode 100644
index 0000000..443e410
--- /dev/null
+++ b/WebCore/page/inspector/Images/tipIconPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toggleDown.png b/WebCore/page/inspector/Images/toggleDown.png
new file mode 100644
index 0000000..9e73bfb
--- /dev/null
+++ b/WebCore/page/inspector/Images/toggleDown.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toggleUp.png b/WebCore/page/inspector/Images/toggleUp.png
new file mode 100644
index 0000000..6b62aee
--- /dev/null
+++ b/WebCore/page/inspector/Images/toggleUp.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarBackground.png b/WebCore/page/inspector/Images/toolbarBackground.png
new file mode 100644
index 0000000..257d63d
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarBackground.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarBackgroundInactive.png b/WebCore/page/inspector/Images/toolbarBackgroundInactive.png
new file mode 100644
index 0000000..a62b8df
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarBackgroundInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonNormal.png b/WebCore/page/inspector/Images/toolbarButtonNormal.png
new file mode 100644
index 0000000..bd16c12
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalInactive.png b/WebCore/page/inspector/Images/toolbarButtonNormalInactive.png
new file mode 100644
index 0000000..3c9d5bc
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonNormalInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalPressed.png b/WebCore/page/inspector/Images/toolbarButtonNormalPressed.png
new file mode 100644
index 0000000..aca9b02
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonNormalPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalSelected.png b/WebCore/page/inspector/Images/toolbarButtonNormalSelected.png
new file mode 100644
index 0000000..41013b3
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonNormalSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonNormalSelectedInactive.png b/WebCore/page/inspector/Images/toolbarButtonNormalSelectedInactive.png
new file mode 100644
index 0000000..f6fdb08
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonNormalSelectedInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonSmall.png b/WebCore/page/inspector/Images/toolbarButtonSmall.png
new file mode 100644
index 0000000..04398fd
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonSmall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallInactive.png b/WebCore/page/inspector/Images/toolbarButtonSmallInactive.png
new file mode 100644
index 0000000..a6928be
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonSmallInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallPressed.png b/WebCore/page/inspector/Images/toolbarButtonSmallPressed.png
new file mode 100644
index 0000000..0d83a38
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonSmallPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallSelected.png b/WebCore/page/inspector/Images/toolbarButtonSmallSelected.png
new file mode 100644
index 0000000..d5433cb
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonSmallSelected.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarButtonSmallSelectedInactive.png b/WebCore/page/inspector/Images/toolbarButtonSmallSelectedInactive.png
new file mode 100644
index 0000000..41a3249
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarButtonSmallSelectedInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonNormal.png b/WebCore/page/inspector/Images/toolbarPopupButtonNormal.png
new file mode 100644
index 0000000..438b5ea
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarPopupButtonNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonNormalInactive.png b/WebCore/page/inspector/Images/toolbarPopupButtonNormalInactive.png
new file mode 100644
index 0000000..0e8f6a2
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarPopupButtonNormalInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonNormalPressed.png b/WebCore/page/inspector/Images/toolbarPopupButtonNormalPressed.png
new file mode 100644
index 0000000..23bcafd
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarPopupButtonNormalPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonSmall.png b/WebCore/page/inspector/Images/toolbarPopupButtonSmall.png
new file mode 100644
index 0000000..efa68a3
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarPopupButtonSmall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonSmallInactive.png b/WebCore/page/inspector/Images/toolbarPopupButtonSmallInactive.png
new file mode 100644
index 0000000..55e3c0c
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarPopupButtonSmallInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarPopupButtonSmallPressed.png b/WebCore/page/inspector/Images/toolbarPopupButtonSmallPressed.png
new file mode 100644
index 0000000..56e574c
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarPopupButtonSmallPressed.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormal.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormal.png
new file mode 100644
index 0000000..bf6de38
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormal.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormalInactive.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormalInactive.png
new file mode 100644
index 0000000..c61541c
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarSplitButtonDividerNormalInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmall.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmall.png
new file mode 100644
index 0000000..9f248f6
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmall.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmallInactive.png b/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmallInactive.png
new file mode 100644
index 0000000..7365c34
--- /dev/null
+++ b/WebCore/page/inspector/Images/toolbarSplitButtonDividerSmallInactive.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/treeDownTriangleBlack.png b/WebCore/page/inspector/Images/treeDownTriangleBlack.png
new file mode 100644
index 0000000..0821112
--- /dev/null
+++ b/WebCore/page/inspector/Images/treeDownTriangleBlack.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/treeDownTriangleWhite.png b/WebCore/page/inspector/Images/treeDownTriangleWhite.png
new file mode 100644
index 0000000..1667b51
--- /dev/null
+++ b/WebCore/page/inspector/Images/treeDownTriangleWhite.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/treeLeftTriangleBlack.png b/WebCore/page/inspector/Images/treeLeftTriangleBlack.png
new file mode 100644
index 0000000..81bc7e0
--- /dev/null
+++ b/WebCore/page/inspector/Images/treeLeftTriangleBlack.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/treeRightTriangleBlack.png b/WebCore/page/inspector/Images/treeRightTriangleBlack.png
new file mode 100644
index 0000000..90de820
--- /dev/null
+++ b/WebCore/page/inspector/Images/treeRightTriangleBlack.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/treeRightTriangleWhite.png b/WebCore/page/inspector/Images/treeRightTriangleWhite.png
new file mode 100644
index 0000000..2b6a82f
--- /dev/null
+++ b/WebCore/page/inspector/Images/treeRightTriangleWhite.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/warningIcon.png b/WebCore/page/inspector/Images/warningIcon.png
new file mode 100644
index 0000000..f37727e
--- /dev/null
+++ b/WebCore/page/inspector/Images/warningIcon.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/warningMediumIcon.png b/WebCore/page/inspector/Images/warningMediumIcon.png
new file mode 100644
index 0000000..291e111
--- /dev/null
+++ b/WebCore/page/inspector/Images/warningMediumIcon.png
Binary files differ
diff --git a/WebCore/page/inspector/Images/warningsErrors.png b/WebCore/page/inspector/Images/warningsErrors.png
new file mode 100644
index 0000000..878b593
--- /dev/null
+++ b/WebCore/page/inspector/Images/warningsErrors.png
Binary files differ
diff --git a/WebCore/page/inspector/MetricsSidebarPane.js b/WebCore/page/inspector/MetricsSidebarPane.js
new file mode 100644
index 0000000..e805560
--- /dev/null
+++ b/WebCore/page/inspector/MetricsSidebarPane.js
@@ -0,0 +1,140 @@
+/*
+ * 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
new file mode 100644
index 0000000..2f000ee
--- /dev/null
+++ b/WebCore/page/inspector/NetworkPanel.js
@@ -0,0 +1,1036 @@
+/*
+ * 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
new file mode 100644
index 0000000..7631fa3
--- /dev/null
+++ b/WebCore/page/inspector/Panel.js
@@ -0,0 +1,180 @@
+/*
+ * 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
new file mode 100644
index 0000000..b2a3473
--- /dev/null
+++ b/WebCore/page/inspector/PropertiesSection.js
@@ -0,0 +1,139 @@
+/*
+ * 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
new file mode 100644
index 0000000..0266d53
--- /dev/null
+++ b/WebCore/page/inspector/PropertiesSidebarPane.js
@@ -0,0 +1,139 @@
+/*
+ * 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\">&mdash;</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
new file mode 100644
index 0000000..ed35970
--- /dev/null
+++ b/WebCore/page/inspector/Resource.js
@@ -0,0 +1,689 @@
+/*
+ * 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.listItemElement.scrollIntoViewIfNeeded(false);
+ }
+}
+
+WebInspector.ResourceTreeElement.prototype.__proto__ = TreeElement.prototype;
diff --git a/WebCore/page/inspector/ResourceCategory.js b/WebCore/page/inspector/ResourceCategory.js
new file mode 100644
index 0000000..a2f2ba4
--- /dev/null
+++ b/WebCore/page/inspector/ResourceCategory.js
@@ -0,0 +1,105 @@
+/*
+ * 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/ResourcePanel.js b/WebCore/page/inspector/ResourcePanel.js
new file mode 100644
index 0000000..b165c2b
--- /dev/null
+++ b/WebCore/page/inspector/ResourcePanel.js
@@ -0,0 +1,50 @@
+/*
+ * 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.ResourcePanel = function(resource, views)
+{
+ WebInspector.Panel.call(this, views);
+ this.resource = resource;
+}
+
+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();
+ },
+
+ hide: function()
+ {
+ this.resource.listItem.deselect(true); // passing true prevents a cycle
+ WebInspector.Panel.prototype.hide.call(this);
+ }
+}
+
+WebInspector.ResourcePanel.prototype.__proto__ = WebInspector.Panel.prototype;
diff --git a/WebCore/page/inspector/SidebarPane.js b/WebCore/page/inspector/SidebarPane.js
new file mode 100644
index 0000000..53f9d6d
--- /dev/null
+++ b/WebCore/page/inspector/SidebarPane.js
@@ -0,0 +1,123 @@
+/*
+ * 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
new file mode 100644
index 0000000..32d7899
--- /dev/null
+++ b/WebCore/page/inspector/SourcePanel.js
@@ -0,0 +1,144 @@
+/*
+ * 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.scrollIntoViewIfNeeded(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
new file mode 100644
index 0000000..a701a35
--- /dev/null
+++ b/WebCore/page/inspector/StylesSidebarPane.js
@@ -0,0 +1,682 @@
+/*
+ * 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.styleRule.parentStyleSheet.href)
+ 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
new file mode 100644
index 0000000..0a5e164
--- /dev/null
+++ b/WebCore/page/inspector/WebKit.qrc
@@ -0,0 +1,137 @@
+<!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
new file mode 100644
index 0000000..2ba3208
--- /dev/null
+++ b/WebCore/page/inspector/inspector.css
@@ -0,0 +1,2169 @@
+/*
+ * 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
new file mode 100644
index 0000000..3ba0300
--- /dev/null
+++ b/WebCore/page/inspector/inspector.html
@@ -0,0 +1,75 @@
+<!--
+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
new file mode 100644
index 0000000..a9f5fcf
--- /dev/null
+++ b/WebCore/page/inspector/inspector.js
@@ -0,0 +1,1186 @@
+/*
+ * 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.scrollIntoViewIfNeeded(true);
+ element.listItemElement.scrollIntoViewIfNeeded(false);
+ }
+
+ var domResultSelected = function(element)
+ {
+ WebInspector.navigateToPanel(element.representedObject.panel, "dom");
+ element.representedObject.panel.focusedDOMNode = element.representedObject.node;
+ element.listItemElement.scrollIntoViewIfNeeded(false);
+ }
+
+ 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
new file mode 100644
index 0000000..228136a
--- /dev/null
+++ b/WebCore/page/inspector/treeoutline.js
@@ -0,0 +1,728 @@
+/*
+ * 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
new file mode 100644
index 0000000..47d721f
--- /dev/null
+++ b/WebCore/page/inspector/utilities.js
@@ -0,0 +1,793 @@
+/*
+ * 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);
+}
+
+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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
+}
+
+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 + "-->";
+
+ case Node.DOCUMENT_TYPE_NODE:
+ var docType = "<!DOCTYPE " + this.nodeName;
+ if (this.publicId) {
+ docType += " PUBLIC \"" + this.publicId + "\"";
+ if (this.systemId)
+ docType += " \"" + this.systemId + "\"";
+ } else if (this.systemId)
+ docType += " SYSTEM \"" + this.systemId + "\"";
+ if (this.internalSubset)
+ docType += " [" + this.internalSubset + "]";
+ return docType + ">";
+ }
+
+ 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 += "&#x2026;"; // 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\">&lt;" + 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&#8203;");
+
+ info.title += " <span class=\"webkit-html-attribute-name\">" + attr.name.escapeHTML() + "</span>=&#8203;";
+
+ 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 += "&gt;</span>&#8203;";
+
+ // 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() + "&#8203;<span class=\"webkit-html-tag\">&lt;/" + this.nodeName.toLowerCase().escapeHTML() + "&gt;</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\">&lt;!--" + this.nodeValue.escapeHTML() + "--&gt;</span>";
+ break;
+
+ case Node.DOCUMENT_TYPE_NODE:
+ info.title = "<span class=\"webkit-html-tag\">&lt;!DOCTYPE " + this.nodeName;
+ if (this.publicId) {
+ info.title += " PUBLIC \"" + this.publicId + "\"";
+ if (this.systemId)
+ info.title += " \"" + this.systemId + "\"";
+ } else if (this.systemId)
+ info.title += " SYSTEM \"" + this.systemId + "\"";
+ if (this.internalSubset)
+ info.title += " [" + this.internalSubset + "]";
+ info.title += "&gt;</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..61d0319
--- /dev/null
+++ b/WebCore/page/mac/AXObjectCacheMac.mm
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, 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 "Document.h"
+#import "FoundationExtras.h"
+#import "RenderObject.h"
+#import "WebCoreAXObject.h"
+#import "WebCoreViewFactory.h"
+
+// The simple Cocoa calls in this file don't throw exceptions.
+
+namespace WebCore {
+
+struct TextMarkerData {
+ AXID axID;
+ Node* node;
+ int offset;
+ EAffinity affinity;
+};
+
+bool AXObjectCache::gAccessibilityEnabled = false;
+
+AXObjectCache::~AXObjectCache()
+{
+ HashMap<RenderObject*, WebCoreAXObject*>::iterator end = m_objects.end();
+ for (HashMap<RenderObject*, WebCoreAXObject*>::iterator it = m_objects.begin(); it != end; ++it) {
+ WebCoreAXObject* obj = (*it).second;
+ [obj detach];
+ HardRelease(obj);
+ }
+}
+
+WebCoreAXObject* AXObjectCache::get(RenderObject* renderer)
+{
+ WebCoreAXObject* obj = m_objects.get(renderer);
+ if (obj)
+ return obj;
+
+ obj = [[WebCoreAXObject alloc] initWithRenderer:renderer];
+ HardRetainWithNSRelease(obj);
+ m_objects.set(renderer, obj);
+ return obj;
+}
+
+void AXObjectCache::remove(RenderObject* renderer)
+{
+ WebCoreAXObject* obj = m_objects.take(renderer);
+ if (!obj)
+ return;
+ [obj detach];
+ HardRelease(obj);
+
+ ASSERT(m_objects.size() >= m_idsInUse.size());
+}
+
+AXID AXObjectCache::getAXID(WebCoreAXObject* 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 || objID == AXIDHashTraits::deletedValue() || m_idsInUse.contains(objID));
+ m_idsInUse.add(objID);
+ lastUsedID = objID;
+ [obj setAXObjectID:objID];
+
+ return objID;
+}
+
+void AXObjectCache::removeAXID(WebCoreAXObject* obj)
+{
+ AXID objID = [obj axObjectID];
+ if (objID == 0)
+ return;
+ ASSERT(objID != AXIDHashTraits::deletedValue());
+ ASSERT(m_idsInUse.contains(objID));
+ [obj setAXObjectID:0];
+ m_idsInUse.remove(objID);
+}
+
+WebCoreTextMarker* AXObjectCache::textMarkerForVisiblePosition(const VisiblePosition& visiblePos)
+{
+ Position deepPos = visiblePos.deepEquivalent();
+ Node* domNode = deepPos.node();
+ ASSERT(domNode);
+ if (!domNode)
+ 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
+ WebCoreAXObject* obj = get(renderer);
+
+ // create a text marker, adding an ID for the WebCoreAXObject if needed
+ TextMarkerData textMarkerData;
+ textMarkerData.axID = getAXID(obj);
+ textMarkerData.node = domNode;
+ textMarkerData.offset = deepPos.offset();
+ textMarkerData.affinity = visiblePos.affinity();
+ return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)];
+}
+
+VisiblePosition AXObjectCache::visiblePositionForTextMarker(WebCoreTextMarker* textMarker)
+{
+ TextMarkerData textMarkerData;
+
+ if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)])
+ return VisiblePosition();
+
+ // return empty position if the text marker is no longer valid
+ if (!m_idsInUse.contains(textMarkerData.axID))
+ return VisiblePosition();
+
+ // generate a VisiblePosition from the data we stored earlier
+ VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
+
+ // make sure the node and offset still match (catches stale markers). affinity is not critical for this.
+ Position deepPos = visiblePos.deepEquivalent();
+ if (deepPos.node() != textMarkerData.node || deepPos.offset() != textMarkerData.offset)
+ return VisiblePosition();
+
+ return visiblePos;
+}
+
+void AXObjectCache::childrenChanged(RenderObject* renderer)
+{
+ WebCoreAXObject* obj = m_objects.get(renderer);
+ if (obj)
+ [obj childrenChanged];
+}
+
+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
+ WebCoreAXObject* obj = [get(renderer) observableObject];
+ if (obj)
+ NSAccessibilityPostNotification(obj, message);
+ else
+ NSAccessibilityPostNotification(get(renderer->document()->renderer()), message);
+}
+
+void AXObjectCache::postNotificationToElement(RenderObject* renderer, const String& message)
+{
+ // send the notification to the specified element itself, not one of its ancestors
+ if (renderer)
+ NSAccessibilityPostNotification(get(renderer), message);
+}
+
+void AXObjectCache::handleFocusedUIElementChanged()
+{
+ [[WebCoreViewFactory sharedFactory] accessibilityHandleFocusChanged];
+}
+
+}
diff --git a/WebCore/page/mac/ChromeMac.mm b/WebCore/page/mac/ChromeMac.mm
new file mode 100644
index 0000000..9fc8107
--- /dev/null
+++ b/WebCore/page/mac/ChromeMac.mm
@@ -0,0 +1,55 @@
+// -*- mode: c++; c-basic-offset: 4 -*-
+/*
+ * Copyright (C) 2007 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
+ * 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.
+ */
+
+#import "config.h"
+#import "Chrome.h"
+
+#import "BlockExceptions.h"
+#import "Frame.h"
+#import "Page.h"
+
+namespace WebCore {
+
+void Chrome::focusNSView(NSView* view)
+{
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ WebCoreFrameBridge *bridge = m_page->mainFrame()->bridge();
+ NSResponder *firstResponder = [bridge firstResponder];
+ if (firstResponder == view)
+ return;
+
+ if (![view window] || ![view superview] || ![view acceptsFirstResponder])
+ return;
+
+ [bridge 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];
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+} // namespace WebCore
+
diff --git a/WebCore/page/mac/DragControllerMac.mm b/WebCore/page/mac/DragControllerMac.mm
new file mode 100644
index 0000000..2ab9d41
--- /dev/null
+++ b/WebCore/page/mac/DragControllerMac.mm
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ *
+ * 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 "DragController.h"
+
+#import "DragData.h"
+#import "Frame.h"
+#import "FrameView.h"
+#import "Page.h"
+
+namespace WebCore {
+
+const int DragController::LinkDragBorderInset = -2;
+
+const int DragController::MaxOriginalImageArea = 1500 * 1500;
+const int DragController::DragIconRightInset = 7;
+const int DragController::DragIconBottomInset = 3;
+
+const float DragController::DragImageAlpha = 0.75f;
+
+bool DragController::isCopyKeyDown()
+{
+ return [[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask;
+}
+
+DragOperation DragController::dragOperation(DragData* dragData)
+{
+ ASSERT(dragData);
+ if ([NSApp modalWindow] || !dragData->containsURL())
+ return DragOperationNone;
+
+ if (!m_document || ![[m_page->mainFrame()->view()->getOuterView() window] attachedSheet]
+ && [dragData->platformData() draggingSource] != m_page->mainFrame()->view()->getOuterView())
+ return DragOperationCopy;
+
+ return DragOperationNone;
+}
+
+const IntSize& DragController::maxDragImageSize()
+{
+ static const IntSize maxDragImageSize(400, 400);
+
+ return maxDragImageSize;
+}
+
+}
diff --git a/WebCore/page/mac/EventHandlerMac.mm b/WebCore/page/mac/EventHandlerMac.mm
new file mode 100644
index 0000000..5c2a979
--- /dev/null
+++ b/WebCore/page/mac/EventHandlerMac.mm
@@ -0,0 +1,627 @@
+/*
+ * 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 "EventHandler.h"
+
+#include "BlockExceptions.h"
+#include "ClipboardMac.h"
+#include "EventNames.h"
+#include "FocusController.h"
+#include "FrameLoader.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "KeyboardEvent.h"
+#include "MouseEventWithHitTestResults.h"
+#include "Page.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformScrollBar.h"
+#include "PlatformWheelEvent.h"
+#include "RenderWidget.h"
+#include "Settings.h"
+#include "WebCoreFrameBridge.h"
+
+namespace WebCore {
+
+using namespace EventNames;
+
+static RetainPtr<NSEvent>& currentEvent()
+{
+ static RetainPtr<NSEvent> event;
+ return event;
+}
+
+NSEvent *EventHandler::currentNSEvent()
+{
+ return currentEvent().get();
+}
+
+bool EventHandler::wheelEvent(NSEvent *event)
+{
+ RetainPtr<NSEvent> oldCurrentEvent = currentEvent();
+ currentEvent() = event;
+
+ PlatformWheelEvent wheelEvent(event);
+ handleWheelEvent(wheelEvent);
+
+ ASSERT(currentEvent() == event);
+ currentEvent() = oldCurrentEvent;
+
+ return wheelEvent.isAccepted();
+}
+
+PassRefPtr<KeyboardEvent> EventHandler::currentKeyboardEvent() const
+{
+ NSEvent *event = [NSApp currentEvent];
+ if (!event)
+ return 0;
+ switch ([event type]) {
+ case NSKeyDown: {
+ PlatformKeyboardEvent platformEvent(event);
+ platformEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown);
+ return new KeyboardEvent(platformEvent, m_frame->document() ? m_frame->document()->defaultView() : 0);
+ }
+ case NSKeyUp:
+ return new KeyboardEvent(event, m_frame->document() ? m_frame->document()->defaultView() : 0);
+ default:
+ return 0;
+ }
+}
+
+static inline bool isKeyboardOptionTab(KeyboardEvent* event)
+{
+ return event
+ && (event->type() == keydownEvent || event->type() == keypressEvent)
+ && event->altKey()
+ && event->keyIdentifier() == "U+0009";
+}
+
+bool EventHandler::invertSenseOfTabsToLinks(KeyboardEvent* event) const
+{
+ return isKeyboardOptionTab(event);
+}
+
+bool EventHandler::tabsToAllControls(KeyboardEvent* event) const
+{
+ KeyboardUIMode keyboardUIMode = [m_frame->bridge() keyboardUIMode];
+ bool handlingOptionTab = isKeyboardOptionTab(event);
+
+ // If tab-to-links is off, option-tab always highlights all controls
+ if ((keyboardUIMode & KeyboardAccessTabsToLinks) == 0 && handlingOptionTab)
+ return true;
+
+ // If system preferences say to include all controls, we always include all controls
+ if (keyboardUIMode & KeyboardAccessFull)
+ return true;
+
+ // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
+ if (keyboardUIMode & KeyboardAccessTabsToLinks)
+ return !handlingOptionTab;
+
+ return handlingOptionTab;
+}
+
+bool EventHandler::needsKeyboardEventDisambiguationQuirks() const
+{
+ static BOOL checkedSafari = NO;
+ static BOOL isSafari = NO;
+
+ if (!checkedSafari) {
+ isSafari = [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Safari"];
+ checkedSafari = YES;
+ }
+
+ Document* document = m_frame->document();
+ if (!document)
+ return false;
+
+ // RSS view needs arrow key keypress events.
+ 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();
+}
+
+bool EventHandler::keyEvent(NSEvent *event)
+{
+ bool result;
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
+
+ RetainPtr<NSEvent> oldCurrentEvent = currentEvent();
+ currentEvent() = event;
+
+ result = keyEvent(PlatformKeyboardEvent(event));
+
+ ASSERT(currentEvent() == event);
+ currentEvent() = oldCurrentEvent;
+
+ return result;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+
+ return false;
+}
+
+void EventHandler::focusDocumentView()
+{
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ if (FrameView* frameView = m_frame->view())
+ if (NSView *documentView = frameView->getDocumentView())
+ page->chrome()->focusNSView(documentView);
+
+ page->focusController()->setFocusedFrame(m_frame);
+}
+
+bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
+{
+ // Figure out which view to send the event to.
+ RenderObject* target = event.targetNode() ? event.targetNode()->renderer() : 0;
+ if (!target || !target->isWidget())
+ return false;
+
+ // Double-click events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget will
+ // just pass currentEvent down to the widget, we don't want to call it for events that
+ // don't correspond to Cocoa events. The mousedown/ups will have already been passed on as
+ // part of the pressed/released handling.
+ return passMouseDownEventToWidget(static_cast<RenderWidget*>(target)->widget());
+}
+
+bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
+{
+ return passMouseDownEventToWidget(renderWidget->widget());
+}
+
+static bool lastEventIsMouseUp()
+{
+ // Many AK widgets run their own event loops and consume events while the mouse is down.
+ // When they finish, currentEvent is the mouseUp that they exited on. We need to update
+ // the khtml state with this mouseUp, which khtml never saw. This method lets us detect
+ // that state.
+
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
+ if (currentEvent() != currentEventAfterHandlingMouseDown &&
+ [currentEventAfterHandlingMouseDown type] == NSLeftMouseUp &&
+ [currentEventAfterHandlingMouseDown timestamp] >= [currentEvent().get() timestamp])
+ return true;
+ END_BLOCK_OBJC_EXCEPTIONS;
+
+ return false;
+}
+
+bool EventHandler::passMouseDownEventToWidget(Widget* widget)
+{
+ // FIXME: this method always returns true
+
+ if (!widget) {
+ LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
+ return true;
+ }
+
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ NSView *nodeView = widget->getView();
+ ASSERT(nodeView);
+ ASSERT([nodeView superview]);
+ NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentEvent().get() locationInWindow] fromView:nil]];
+ if (!view) {
+ // We probably hit the border of a RenderWidget
+ return true;
+ }
+
+ if ([m_frame->bridge() 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];
+ }
+
+ // We need to "defer loading" while tracking the mouse, because tearing down the
+ // page while an AppKit control is tracking the mouse can cause a crash.
+
+ // FIXME: In theory, WebCore now tolerates tear-down while tracking the
+ // mouse. We should confirm that, and then remove the deferrsLoading
+ // hack entirely.
+
+ bool wasDeferringLoading = m_frame->page()->defersLoading();
+ if (!wasDeferringLoading)
+ m_frame->page()->setDefersLoading(true);
+
+ ASSERT(!m_sendingEventToSubview);
+ m_sendingEventToSubview = true;
+ [view mouseDown:currentEvent().get()];
+ m_sendingEventToSubview = false;
+
+ if (!wasDeferringLoading)
+ m_frame->page()->setDefersLoading(false);
+
+ // Remember which view we sent the event to, so we can direct the release event properly.
+ m_mouseDownView = view;
+ m_mouseDownWasInSubframe = false;
+
+ // Many AppKit widgets run their own event loops and consume events while the mouse is down.
+ // When they finish, currentEvent is the mouseUp that they exited on. We need to update
+ // the EventHandler state with this mouseUp, which we never saw.
+ // If this event isn't a mouseUp, we assume that the mouseUp will be coming later. There
+ // is a hole here if the widget consumes both the mouseUp and subsequent events.
+ if (lastEventIsMouseUp())
+ m_mousePressed = false;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+
+ return true;
+}
+
+// Note that this does the same kind of check as [target isDescendantOf:superview].
+// There are two differences: This is a lot slower because it has to walk the whole
+// tree, and this works in cases where the target has already been deallocated.
+static bool findViewInSubviews(NSView *superview, NSView *target)
+{
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ NSEnumerator *e = [[superview subviews] objectEnumerator];
+ NSView *subview;
+ while ((subview = [e nextObject])) {
+ if (subview == target || findViewInSubviews(subview, target)) {
+ return true;
+ }
+ }
+ END_BLOCK_OBJC_EXCEPTIONS;
+
+ return false;
+}
+
+NSView *EventHandler::mouseDownViewIfStillGood()
+{
+ // Since we have no way of tracking the lifetime of m_mouseDownView, we have to assume that
+ // it could be deallocated already. We search for it in our subview tree; if we don't find
+ // it, we set it to nil.
+ NSView *mouseDownView = m_mouseDownView;
+ if (!mouseDownView) {
+ return nil;
+ }
+ FrameView* topFrameView = m_frame->view();
+ NSView *topView = topFrameView ? topFrameView->getView() : nil;
+ if (!topView || !findViewInSubviews(topView, mouseDownView)) {
+ m_mouseDownView = nil;
+ return nil;
+ }
+ return mouseDownView;
+}
+
+bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const
+{
+ return m_activationEventNumber == event.eventNumber();
+}
+
+bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
+{
+ NSView *view = mouseDownViewIfStillGood();
+
+ if (!view)
+ return false;
+
+ if (!m_mouseDownWasInSubframe) {
+ m_sendingEventToSubview = true;
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ [view mouseDragged:currentEvent().get()];
+ END_BLOCK_OBJC_EXCEPTIONS;
+ m_sendingEventToSubview = false;
+ }
+
+ return true;
+}
+
+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);
+}
+
+bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
+{
+ NSView *view = mouseDownViewIfStillGood();
+ if (!view)
+ return false;
+
+ if (!m_mouseDownWasInSubframe) {
+ m_sendingEventToSubview = true;
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+ [view mouseUp:currentEvent().get()];
+ END_BLOCK_OBJC_EXCEPTIONS;
+ m_sendingEventToSubview = false;
+ }
+
+ return true;
+}
+
+bool EventHandler::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframe, HitTestResult* hoveredNode)
+{
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ switch ([currentEvent().get() type]) {
+ case NSMouseMoved:
+ // Since we're passing in currentEvent() here, we can call
+ // handleMouseMoveEvent() directly, since the save/restore of
+ // currentEvent() that mouseMoved() does would have no effect.
+ subframe->eventHandler()->handleMouseMoveEvent(currentEvent().get(), hoveredNode);
+ return true;
+
+ case NSLeftMouseDown: {
+ Node* node = event.targetNode();
+ if (!node)
+ return false;
+ RenderObject* renderer = node->renderer();
+ if (!renderer || !renderer->isWidget())
+ return false;
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ if (!widget || !widget->isFrameView())
+ return false;
+ if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget*>(renderer)))
+ return false;
+ m_mouseDownWasInSubframe = true;
+ return true;
+ }
+ case NSLeftMouseUp: {
+ if (!m_mouseDownWasInSubframe)
+ return false;
+ NSView *view = mouseDownViewIfStillGood();
+ if (!view)
+ return false;
+ ASSERT(!m_sendingEventToSubview);
+ m_sendingEventToSubview = true;
+ [view mouseUp:currentEvent().get()];
+ m_sendingEventToSubview = false;
+ return true;
+ }
+ case NSLeftMouseDragged: {
+ if (!m_mouseDownWasInSubframe)
+ return false;
+ NSView *view = mouseDownViewIfStillGood();
+ if (!view)
+ return false;
+ ASSERT(!m_sendingEventToSubview);
+ m_sendingEventToSubview = true;
+ [view mouseDragged:currentEvent().get()];
+ m_sendingEventToSubview = false;
+ return true;
+ }
+ default:
+ return false;
+ }
+ END_BLOCK_OBJC_EXCEPTIONS;
+
+ return false;
+}
+
+bool EventHandler::passWheelEventToWidget(PlatformWheelEvent&, Widget* widget)
+{
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ if ([currentEvent().get() type] != NSScrollWheel || m_sendingEventToSubview || !widget)
+ return false;
+
+ NSView *nodeView = widget->getView();
+ ASSERT(nodeView);
+ ASSERT([nodeView superview]);
+ NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[currentEvent().get() locationInWindow] fromView:nil]];
+ if (!view)
+ // We probably hit the border of a RenderWidget
+ return false;
+
+ m_sendingEventToSubview = true;
+ [view scrollWheel:currentEvent().get()];
+ m_sendingEventToSubview = false;
+ return true;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+ return false;
+}
+
+void EventHandler::mouseDown(NSEvent *event)
+{
+ FrameView* v = m_frame->view();
+ if (!v || m_sendingEventToSubview)
+ return;
+
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ m_frame->loader()->resetMultipleFormSubmissionProtection();
+
+ m_mouseDownView = nil;
+
+ RetainPtr<NSEvent> oldCurrentEvent = currentEvent();
+ currentEvent() = event;
+ m_mouseDown = PlatformMouseEvent(event);
+
+ handleMousePressEvent(event);
+
+ ASSERT(currentEvent() == event);
+ currentEvent() = oldCurrentEvent;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+void EventHandler::mouseDragged(NSEvent *event)
+{
+ FrameView* v = m_frame->view();
+ if (!v || m_sendingEventToSubview)
+ return;
+
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ RetainPtr<NSEvent> oldCurrentEvent = currentEvent();
+ currentEvent() = event;
+
+ handleMouseMoveEvent(event);
+
+ ASSERT(currentEvent() == event);
+ currentEvent() = oldCurrentEvent;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+void EventHandler::mouseUp(NSEvent *event)
+{
+ FrameView* v = m_frame->view();
+ if (!v || m_sendingEventToSubview)
+ return;
+
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ RetainPtr<NSEvent> oldCurrentEvent = currentEvent();
+ currentEvent() = event;
+
+ // Our behavior here is a little different that Qt. Qt always sends
+ // a mouse release event, even for a double click. To correct problems
+ // in khtml's DOM click event handling we do not send a release here
+ // for a double click. Instead we send that event from FrameView's
+ // handleMouseDoubleClickEvent. Note also that the third click of
+ // a triple click is treated as a single click, but the fourth is then
+ // treated as another double click. Hence the "% 2" below.
+ int clickCount = [event clickCount];
+ if (clickCount > 0 && clickCount % 2 == 0)
+ handleMouseDoubleClickEvent(event);
+ else
+ handleMouseReleaseEvent(event);
+
+ ASSERT(currentEvent() == event);
+ currentEvent() = oldCurrentEvent;
+
+ m_mouseDownView = nil;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+/*
+ A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
+ eats all subsequent events after it is starts its modal tracking loop. After the interaction
+ is done, this routine is used to fix things up. When a mouse down started us tracking in
+ the widget, we post a fake mouse up to balance the mouse down we started with. When a
+ key down started us tracking in the widget, we post a fake key up to balance things out.
+ In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to
+ be over after the tracking is done.
+ */
+void EventHandler::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
+{
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ m_sendingEventToSubview = false;
+ int eventType = [initiatingEvent type];
+ if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
+ 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]];
+
+ [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]];
+ [NSApp postEvent:fakeEvent atStart:YES];
+ }
+ // 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]]
+ modifierFlags:[initiatingEvent modifierFlags]
+ timestamp:[initiatingEvent timestamp]
+ windowNumber:[initiatingEvent windowNumber]
+ context:[initiatingEvent context]
+ eventNumber:0
+ clickCount:0
+ pressure:0];
+ [NSApp postEvent:fakeEvent atStart:YES];
+ }
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+void EventHandler::mouseMoved(NSEvent *event)
+{
+ // Reject a mouse moved if the button is down - screws up tracking during autoscroll
+ // These happen because WebKit sometimes has to fake up moved events.
+ if (!m_frame->view() || m_mousePressed || m_sendingEventToSubview)
+ return;
+
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ RetainPtr<NSEvent> oldCurrentEvent = currentEvent();
+ currentEvent() = event;
+
+ mouseMoved(PlatformMouseEvent(event));
+
+ ASSERT(currentEvent() == event);
+ currentEvent() = oldCurrentEvent;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ return passSubframeEventToSubframe(mev, subframe);
+}
+
+bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
+{
+ return passSubframeEventToSubframe(mev, subframe, hoveredNode);
+}
+
+bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ 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
new file mode 100644
index 0000000..6f6f096
--- /dev/null
+++ b/WebCore/page/mac/FrameMac.mm
@@ -0,0 +1,678 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ * Copyright (C) 2007 Trolltech ASA
+ *
+ * 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 "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"
+#import "HitTestResult.h"
+#import "KeyboardEvent.h"
+#import "Logging.h"
+#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 "SimpleFontData.h"
+#import "SystemTime.h"
+#import "TextResourceDecoder.h"
+#import "UserStyleSheetLoader.h"
+#import "WebCoreFrameBridge.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
+
+@interface NSObject (WebPlugIn)
+- (id)objectForWebScript;
+- (NPObject *)createPluginScriptableObject;
+@end
+
+@interface NSView (WebCoreHTMLDocumentView)
+- (void)drawSingleRect:(NSRect)rect;
+@end
+
+using namespace std;
+using namespace KJS::Bindings;
+
+using KJS::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)
+{
+ // All the ObjC calls in this method are simple array and string
+ // calls which we can assume do not raise exceptions
+
+
+ // Parallel arrays that we use to cache regExps. In practice the number of expressions
+ // that the app will use is equal to the number of locales is used in searching.
+ static const unsigned int regExpCacheSize = 4;
+ static NSMutableArray* regExpLabels = nil;
+ static Vector<RegularExpression*> regExps;
+ static RegularExpression wordRegExp = RegularExpression("\\w");
+
+ RegularExpression* result;
+ if (!regExpLabels)
+ regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
+ CFIndex cacheHit = [regExpLabels indexOfObject:labels];
+ if (cacheHit != NSNotFound)
+ result = regExps.at(cacheHit);
+ else {
+ String pattern("(");
+ unsigned int numLabels = [labels count];
+ unsigned int i;
+ for (i = 0; i < numLabels; i++) {
+ String label = [labels objectAtIndex:i];
+
+ bool startsWithWordChar = false;
+ bool endsWithWordChar = false;
+ if (label.length() != 0) {
+ startsWithWordChar = wordRegExp.search(label.substring(0, 1)) >= 0;
+ endsWithWordChar = wordRegExp.search(label.substring(label.length() - 1, 1)) >= 0;
+ }
+
+ if (i != 0)
+ pattern.append("|");
+ // 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)
+ pattern.append("\\b");
+ pattern.append(label);
+ if (endsWithWordChar)
+ pattern.append("\\b");
+ }
+ pattern.append(")");
+ result = new RegularExpression(pattern, false);
+ }
+
+ // add regexp to the cache, making sure it is at the front for LRU ordering
+ if (cacheHit != 0) {
+ if (cacheHit != NSNotFound) {
+ // remove from old spot
+ [regExpLabels removeObjectAtIndex:cacheHit];
+ regExps.remove(cacheHit);
+ }
+ // add to start
+ [regExpLabels insertObject:labels atIndex:0];
+ regExps.insert(0, result);
+ // trim if too big
+ if ([regExpLabels count] > regExpCacheSize) {
+ [regExpLabels removeObjectAtIndex:regExpCacheSize];
+ RegularExpression* last = regExps.last();
+ regExps.removeLast();
+ delete last;
+ }
+ }
+ return result;
+}
+
+NSString* Frame::searchForNSLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
+{
+ RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
+
+ if (cellRenderer && cellRenderer->isTableCell()) {
+ RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
+
+ if (cellAboveRenderer) {
+ HTMLTableCellElement* aboveCell =
+ static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
+
+ if (aboveCell) {
+ // search within the above cell we found for a match
+ 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
+ String nodeString = n->nodeValue();
+ int pos = regExp->searchRev(nodeString);
+ if (pos >= 0)
+ return nodeString.substring(pos, regExp->matchedLength());
+ }
+ }
+ }
+ }
+ }
+ // Any reason in practice to search all cells in that are above cell?
+ return nil;
+}
+
+NSString* Frame::searchForLabelsBeforeElement(NSArray* labels, Element* element)
+{
+ RegularExpression* regExp = regExpForLabels(labels);
+ // We stop searching after we've seen this many chars
+ const unsigned int charsSearchedThreshold = 500;
+ // This is the absolute max we search. We allow a little more slop than
+ // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
+ const unsigned int maxCharsSearched = 600;
+ // If the starting element is within a table, the cell that contains it
+ HTMLTableCellElement* startingTableCell = 0;
+ bool searchedCellAbove = false;
+
+ // walk backwards in the node tree, until another element, or form, or end of tree
+ int unsigned lengthSearched = 0;
+ Node* n;
+ for (n = element->traversePreviousNode();
+ n && lengthSearched < charsSearchedThreshold;
+ n = n->traversePreviousNode())
+ {
+ if (n->hasTagName(formTag)
+ || (n->isHTMLElement()
+ && static_cast<HTMLElement*>(n)->isGenericFormElement()))
+ {
+ // We hit another form element or the start of the form - bail out
+ break;
+ } else if (n->hasTagName(tdTag) && !startingTableCell) {
+ startingTableCell = static_cast<HTMLTableCellElement*>(n);
+ } else if (n->hasTagName(trTag) && startingTableCell) {
+ NSString* result = searchForLabelsAboveCell(regExp, startingTableCell);
+ if (result && [result length] > 0)
+ return result;
+ searchedCellAbove = true;
+ } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
+ // For each text chunk, run the regexp
+ 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.substring(pos, regExp->matchedLength());
+
+ lengthSearched += nodeString.length();
+ }
+ }
+
+ // If we started in a cell, but bailed because we found the start of the form or the
+ // previous element, we still might need to search the row above us for a label.
+ if (startingTableCell && !searchedCellAbove) {
+ NSString* result = searchForLabelsAboveCell(regExp, startingTableCell);
+ if (result && [result length] > 0)
+ return result;
+ }
+
+ return nil;
+}
+
+NSString* Frame::matchLabelsAgainstElement(NSArray* labels, Element* element)
+{
+ String name = element->getAttribute(nameAttr);
+ if (name.isEmpty())
+ return nil;
+
+ // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
+ replace(name, RegularExpression("\\d"), " ");
+ name.replace('_', ' ');
+
+ RegularExpression* regExp = regExpForLabels(labels);
+ // Use the largest match we can find in the whole name string
+ int pos;
+ int length;
+ int bestPos = -1;
+ int bestLength = -1;
+ int start = 0;
+ do {
+ pos = regExp->search(name, start);
+ if (pos != -1) {
+ length = regExp->matchedLength();
+ if (length >= bestLength) {
+ bestPos = pos;
+ bestLength = length;
+ }
+ start = pos + 1;
+ }
+ } while (pos != -1);
+
+ if (bestPos != -1)
+ return name.substring(bestPos, bestLength);
+ return nil;
+}
+
+NSImage* Frame::imageFromRect(NSRect rect) const
+{
+ NSView* view = d->m_view->getDocumentView();
+ if (!view)
+ return nil;
+ if (![view respondsToSelector:@selector(drawSingleRect:)])
+ return nil;
+
+ NSImage* resultImage;
+ BEGIN_BLOCK_OBJC_EXCEPTIONS;
+
+ NSRect bounds = [view bounds];
+
+ // Round image rect size in window coordinate space to avoid pixel cracks at HiDPI (4622794)
+ rect = [view convertRect:rect toView:nil];
+ rect.size.height = roundf(rect.size.height);
+ rect.size.width = roundf(rect.size.width);
+ rect = [view convertRect:rect fromView:nil];
+
+ resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
+
+ if (rect.size.width != 0 && rect.size.height != 0) {
+ [resultImage setFlipped:YES];
+ [resultImage lockFocus];
+ CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+ CGContextSaveGState(context);
+ CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
+
+ // Note: Must not call drawRect: here, because drawRect: assumes that it's called from AppKit's
+ // display machinery. It calls getRectsBeingDrawn:count:, which can only be called inside
+ // when a real AppKit display is underway.
+ [view drawSingleRect:rect];
+
+ CGContextRestoreGState(context);
+ [resultImage unlockFocus];
+ [resultImage setFlipped:NO];
+ }
+
+ return resultImage;
+
+ END_BLOCK_OBJC_EXCEPTIONS;
+
+ return nil;
+}
+
+NSImage* Frame::selectionImage(bool forceBlackText) const
+{
+ d->m_paintRestriction = forceBlackText ? PaintRestrictionSelectionOnlyBlackText : PaintRestrictionSelectionOnly;
+ d->m_doc->updateLayout();
+ NSImage* result = imageFromRect(selectionRect());
+ d->m_paintRestriction = PaintRestrictionNone;
+ return result;
+}
+
+NSImage* Frame::snapshotDragImage(Node* node, NSRect* imageRect, NSRect* elementRect) const
+{
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return nil;
+
+ renderer->updateDragState(true); // mark dragged nodes (so they pick up the right CSS)
+ d->m_doc->updateLayout(); // forces style recalc - needed since changing the drag state might
+ // imply new styles, plus JS could have changed other things
+ IntRect topLevelRect;
+ NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
+
+ d->m_elementToDraw = node; // invoke special sub-tree drawing mode
+ NSImage* result = imageFromRect(paintingRect);
+ renderer->updateDragState(false);
+ d->m_doc->updateLayout();
+ d->m_elementToDraw = 0;
+
+ if (elementRect)
+ *elementRect = topLevelRect;
+ if (imageRect)
+ *imageRect = paintingRect;
+ return result;
+}
+
+NSDictionary* Frame::fontAttributesForSelectionStart() const
+{
+ Node* nodeToRemove;
+ RenderStyle* style = styleForSelectionStart(nodeToRemove);
+ if (!style)
+ return nil;
+
+ NSMutableDictionary* result = [NSMutableDictionary dictionary];
+
+ if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
+ [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
+
+ if (style->font().primaryFont()->getNSFont())
+ [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
+
+ if (style->color().isValid() && style->color() != Color::black)
+ [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
+
+ ShadowData* shadow = style->textShadow();
+ if (shadow) {
+ NSShadow* s = [[NSShadow alloc] init];
+ [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
+ [s setShadowBlurRadius:shadow->blur];
+ [s setShadowColor:nsColor(shadow->color)];
+ [result setObject:s forKey:NSShadowAttributeName];
+ }
+
+ int decoration = style->textDecorationsInEffect();
+ if (decoration & LINE_THROUGH)
+ [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
+
+ int superscriptInt = 0;
+ switch (style->verticalAlign()) {
+ case BASELINE:
+ case BOTTOM:
+ case BASELINE_MIDDLE:
+ case LENGTH:
+ case MIDDLE:
+ case TEXT_BOTTOM:
+ case TEXT_TOP:
+ case TOP:
+ break;
+ case SUB:
+ superscriptInt = -1;
+ break;
+ case SUPER:
+ superscriptInt = 1;
+ break;
+ }
+ if (superscriptInt)
+ [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
+
+ if (decoration & UNDERLINE)
+ [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
+
+ if (nodeToRemove) {
+ ExceptionCode ec = 0;
+ nodeToRemove->remove(ec);
+ ASSERT(ec == 0);
+ }
+
+ return result;
+}
+
+NSWritingDirection Frame::baseWritingDirectionForSelectionStart() const
+{
+ NSWritingDirection result = NSWritingDirectionLeftToRight;
+
+ Position pos = selectionController()->selection().visibleStart().deepEquivalent();
+ Node* node = pos.node();
+ if (!node || !node->renderer() || !node->renderer()->containingBlock())
+ return result;
+ RenderStyle* style = node->renderer()->containingBlock()->style();
+ if (!style)
+ return result;
+
+ switch (style->direction()) {
+ case LTR:
+ result = NSWritingDirectionLeftToRight;
+ break;
+ case RTL:
+ result = NSWritingDirectionRightToLeft;
+ break;
+ }
+
+ return result;
+}
+
+void Frame::issuePasteCommand()
+{
+ [d->m_bridge issuePasteCommand];
+}
+
+const short enableRomanKeyboardsOnly = -23;
+void Frame::setUseSecureKeyboardEntry(bool enable)
+{
+ if (enable == IsSecureEventInputEnabled())
+ return;
+ if (enable) {
+ EnableSecureEventInput();
+#ifdef BUILDING_ON_TIGER
+ KeyScript(enableRomanKeyboardsOnly);
+#else
+ CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
+ TSMSetDocumentProperty(TSMGetActiveDocument(), kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources);
+ CFRelease(inputSources);
+#endif
+ } else {
+ DisableSecureEventInput();
+#ifdef BUILDING_ON_TIGER
+ KeyScript(smKeyEnableKybds);
+#else
+ TSMRemoveDocumentProperty(TSMGetActiveDocument(), kTSMDocumentEnabledInputSourcesPropertyTag);
+#endif
+ }
+}
+
+NSMutableDictionary* Frame::dashboardRegionsDictionary()
+{
+ Document* doc = document();
+ if (!doc)
+ return nil;
+
+ const Vector<DashboardRegionValue>& regions = doc->dashboardRegions();
+ size_t n = regions.size();
+
+ // Convert the Vector<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
+ NSMutableDictionary* webRegions = [NSMutableDictionary dictionaryWithCapacity:n];
+ for (size_t i = 0; i < n; i++) {
+ const DashboardRegionValue& region = regions[i];
+
+ if (region.type == StyleDashboardRegion::None)
+ continue;
+
+ NSString *label = region.label;
+ WebDashboardRegionType type = WebDashboardRegionTypeNone;
+ if (region.type == StyleDashboardRegion::Circle)
+ type = WebDashboardRegionTypeCircle;
+ else if (region.type == StyleDashboardRegion::Rectangle)
+ type = WebDashboardRegionTypeRectangle;
+ NSMutableArray *regionValues = [webRegions objectForKey:label];
+ if (!regionValues) {
+ regionValues = [[NSMutableArray alloc] initWithCapacity:1];
+ [webRegions setObject:regionValues forKey:label];
+ [regionValues release];
+ }
+
+ WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type];
+ [regionValues addObject:webRegion];
+ [webRegion release];
+ }
+
+ 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];
+}
+
+DragImageRef Frame::dragImageForSelection()
+{
+ if (!selectionController()->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;
+ d->m_userStyleSheetLoader = 0;
+ if (d->m_doc && d->m_doc->docLoader())
+ d->m_userStyleSheetLoader = new UserStyleSheetLoader(d->m_doc, url.string());
+}
+
+void Frame::setUserStyleSheet(const String& styleSheet)
+{
+ delete d->m_userStyleSheetLoader;
+ d->m_userStyleSheetLoader = 0;
+ if (d->m_doc)
+ d->m_doc->setUserStyleSheet(styleSheet);
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/mac/GlobalHistoryMac.mm b/WebCore/page/mac/GlobalHistoryMac.mm
new file mode 100644
index 0000000..466f60e
--- /dev/null
+++ b/WebCore/page/mac/GlobalHistoryMac.mm
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#import "config.h"
+#import "GlobalHistory.h"
+
+#import "WebCoreHistory.h"
+
+namespace WebCore {
+
+bool historyContains(const UChar* characters, unsigned length)
+{
+ // the other side of the bridge is careful not to throw exceptions here
+ return [[WebCoreHistory historyProvider] containsURL:characters length:length];
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/mac/WebCoreAXObject.h b/WebCore/page/mac/WebCoreAXObject.h
new file mode 100644
index 0000000..2014ca6
--- /dev/null
+++ b/WebCore/page/mac/WebCoreAXObject.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2003, 2006 Apple Computer, 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 "AXObjectCache.h"
+
+namespace WebCore {
+ class HTMLAreaElement;
+ class RenderObject;
+}
+
+@interface WebCoreAXObject : NSObject
+{
+ WebCore::RenderObject* m_renderer;
+ id m_data;
+ WebCore::HTMLAreaElement* m_areaElement;
+ NSMutableArray* m_children;
+ WebCore::AXID m_id;
+}
+
+- (id)initWithRenderer:(WebCore::RenderObject*)renderer;
+
+- (BOOL)detached;
+- (void)detach;
+
+- (id)data;
+- (void)setData:(id)data;
+
+- (WebCore::AXID)axObjectID;
+- (void)setAXObjectID:(WebCore::AXID)axObjectID;
+- (void)removeAXObjectID;
+
+- (WebCoreAXObject*)firstChild;
+- (WebCoreAXObject*)lastChild;
+- (WebCoreAXObject*)previousSibling;
+- (WebCoreAXObject*)nextSibling;
+- (WebCoreAXObject*)parentObject;
+
+- (WebCoreAXObject*)observableObject;
+
+- (void)childrenChanged;
+- (void)clearChildren;
+
+@end
diff --git a/WebCore/page/mac/WebCoreAXObject.mm b/WebCore/page/mac/WebCoreAXObject.mm
new file mode 100644
index 0000000..d514b0e
--- /dev/null
+++ b/WebCore/page/mac/WebCoreAXObject.mm
@@ -0,0 +1,2781 @@
+/*
+ * Copyright (C) 2004, 2005, 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.
+ */
+
+#import "config.h"
+#import "WebCoreAXObject.h"
+
+#import "DOMInternal.h"
+#import "ColorMac.h"
+#import "Document.h"
+#import "EventNames.h"
+#import "FocusController.h"
+#import "Frame.h"
+#import "FrameLoader.h"
+#import "FrameView.h"
+#import "HTMLAreaElement.h"
+#import "HTMLCollection.h"
+#import "HTMLFrameElementBase.h"
+#import "HTMLImageElement.h"
+#import "HTMLInputElement.h"
+#import "HTMLLabelElement.h"
+#import "HTMLMapElement.h"
+#import "HTMLNames.h"
+#import "HTMLSelectElement.h"
+#import "HTMLTextAreaElement.h"
+#import "HitTestRequest.h"
+#import "HitTestResult.h"
+#import "LocalizedStrings.h"
+#import "NodeList.h"
+#import "Page.h"
+#import "RenderImage.h"
+#import "RenderListMarker.h"
+#import "RenderMenuList.h"
+#import "RenderTextControl.h"
+#import "RenderTheme.h"
+#import "RenderView.h"
+#import "RenderWidget.h"
+#import "SelectionController.h"
+#import "SimpleFontData.h"
+#import "TextIterator.h"
+#import "WebCoreFrameBridge.h"
+#import "WebCoreFrameView.h"
+#import "WebCoreObjCExtras.h"
+#import "WebCoreViewFactory.h"
+#import "htmlediting.h"
+#import "kjs_html.h"
+#import "visible_units.h"
+#include <mach-o/dyld.h>
+
+using namespace WebCore;
+using namespace EventNames;
+using namespace HTMLNames;
+
+@interface WebCoreAXObject (PrivateWebCoreAXObject)
+// forward declarations as needed
+- (WebCoreTextMarker*)textMarkerForIndex: (NSNumber*) index lastIndexOK: (BOOL)lastIndexOK;
+- (id)doAXLineForTextMarker: (WebCoreTextMarker* ) textMarker;
+@end
+
+@implementation WebCoreAXObject
+
+#ifndef BUILDING_ON_TIGER
++ (void)initialize
+{
+ WebCoreObjCFinalizeOnMainThread(self);
+}
+#endif
+
+-(id)initWithRenderer:(RenderObject*)renderer
+{
+ [super init];
+ m_renderer = renderer;
+ return self;
+}
+
+-(BOOL)detached
+{
+ return !m_renderer;
+}
+
+-(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.
+ [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self];
+ [m_data release];
+ m_data = 0;
+ [self removeAXObjectID];
+ m_renderer = 0;
+ [self clearChildren];
+}
+
+- (void)dealloc
+{
+ [self detach];
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ [self detach];
+ [super finalize];
+}
+
+-(id)data
+{
+ return m_data;
+}
+
+-(void)setData:(id)data
+{
+ if (!m_renderer)
+ return;
+
+ [data retain];
+ [m_data release];
+ m_data = data;
+}
+
+-(HTMLAnchorElement*)anchorElement
+{
+ // return already-known anchor for image areas
+ if (m_areaElement)
+ return m_areaElement;
+
+ // search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
+ RenderObject* currRenderer;
+ for (currRenderer = m_renderer; currRenderer && !currRenderer->element(); currRenderer = currRenderer->parent()) {
+ if (currRenderer->continuation())
+ return [currRenderer->document()->axObjectCache()->get(currRenderer->continuation()) anchorElement];
+ }
+
+ // bail of 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* elt = currRenderer->element();
+ for ( ; elt; elt = elt->parentNode()) {
+ if (elt->isLink() && elt->renderer() && !elt->renderer()->isImage())
+ return static_cast<HTMLAnchorElement*>(elt);
+ }
+
+ return 0;
+}
+
+-(BOOL)isImageButton
+{
+ return m_renderer->isImage() && m_renderer->element() && m_renderer->element()->hasTagName(inputTag);
+}
+
+-(Element*)mouseButtonListener
+{
+ // FIXME: Do the continuation search like anchorElement does
+ for (EventTargetNode* elt = static_cast<EventTargetNode*>(m_renderer->element()); elt; elt = static_cast<EventTargetNode*>(elt->parentNode())) {
+ if (elt->getHTMLEventListener(clickEvent) || elt->getHTMLEventListener(mousedownEvent) || elt->getHTMLEventListener(mouseupEvent))
+ return static_cast<Element*>(elt);
+ }
+
+ return 0;
+}
+
+-(Element*)actionElement
+{
+ if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
+ if (!input->disabled() && (input->inputType() == HTMLInputElement::CHECKBOX ||
+ input->inputType() == HTMLInputElement::RADIO ||
+ input->isTextButton()))
+ return input;
+ }
+
+ if ([self isImageButton] || m_renderer->isMenuList())
+ return static_cast<Element*>(m_renderer->element());
+
+ Element* elt = [self anchorElement];
+ if (!elt)
+ elt = [self mouseButtonListener];
+
+ return elt;
+}
+
+-(WebCoreAXObject*)firstChild
+{
+ if (!m_renderer || !m_renderer->firstChild())
+ return nil;
+
+ return m_renderer->document()->axObjectCache()->get(m_renderer->firstChild());
+}
+
+-(WebCoreAXObject*)lastChild
+{
+ if (!m_renderer || !m_renderer->lastChild())
+ return nil;
+
+ return m_renderer->document()->axObjectCache()->get(m_renderer->lastChild());
+}
+
+-(WebCoreAXObject*)previousSibling
+{
+ if (!m_renderer || !m_renderer->previousSibling())
+ return nil;
+
+ return m_renderer->document()->axObjectCache()->get(m_renderer->previousSibling());
+}
+
+-(WebCoreAXObject*)nextSibling
+{
+ if (!m_renderer || !m_renderer->nextSibling())
+ return nil;
+
+ return m_renderer->document()->axObjectCache()->get(m_renderer->nextSibling());
+}
+
+-(WebCoreAXObject*)parentObject
+{
+ if (m_areaElement)
+ return m_renderer->document()->axObjectCache()->get(m_renderer);
+
+ if (!m_renderer || !m_renderer->parent())
+ return nil;
+
+ return m_renderer->document()->axObjectCache()->get(m_renderer->parent());
+}
+
+-(WebCoreAXObject*)parentObjectUnignored
+{
+ WebCoreAXObject* obj = [self parentObject];
+ if ([obj accessibilityIsIgnored])
+ return [obj parentObjectUnignored];
+
+ return obj;
+}
+
+-(void)addChildrenToArray:(NSMutableArray*)array
+{
+ // nothing to add if there is no RenderObject
+ if (!m_renderer)
+ return;
+
+ // try to add RenderWidget's children, but fall thru if there are none
+ if (m_renderer->isWidget()) {
+ RenderWidget* renderWidget = static_cast<RenderWidget*>(m_renderer);
+ Widget* widget = renderWidget->widget();
+ if (widget) {
+ NSArray* childArr = [(widget->getOuterView()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute];
+ [array addObjectsFromArray: childArr];
+ return;
+ }
+ }
+
+ // add all unignored acc children
+ for (WebCoreAXObject* obj = [self firstChild]; obj; obj = [obj nextSibling]) {
+ if ([obj accessibilityIsIgnored])
+ [obj addChildrenToArray: array];
+ else
+ [array addObject: obj];
+ }
+
+ // for a RenderImage, add the <area> elements as individual accessibility objects
+ if (m_renderer->isImage() && !m_areaElement) {
+ 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
+ // NOTE: can't cache these because they all have the same renderer, which is the cache key, right?
+ // plus there may be little reason to since they are being added to the handy array
+ if (current->isLink()) {
+ WebCoreAXObject* obj = [[[WebCoreAXObject alloc] initWithRenderer: m_renderer] autorelease];
+ obj->m_areaElement = static_cast<HTMLAreaElement*>(current);
+ [array addObject: obj];
+ }
+ }
+ }
+ }
+}
+
+-(BOOL)isWebArea
+{
+ return m_renderer->isRenderView();
+}
+
+-(BOOL)isAnchor
+{
+ return m_areaElement || (!m_renderer->isImage() && m_renderer->element() && m_renderer->element()->isLink());
+}
+
+-(BOOL)isTextControl
+{
+ return m_renderer->isTextField() || m_renderer->isTextArea();
+}
+
+static bool isPasswordFieldElement(Node* node)
+{
+ if (!node || !node->hasTagName(inputTag))
+ return false;
+
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+ return input->inputType() == HTMLInputElement::PASSWORD;
+}
+
+-(BOOL)isPasswordField
+{
+ return m_renderer && isPasswordFieldElement(m_renderer->element());
+}
+
+-(BOOL)isAttachment
+{
+ // widgets are the replaced elements that we represent to AX as attachments
+ BOOL result = m_renderer && m_renderer->isWidget();
+
+ // assert that a widget is a replaced element that is not an image
+ ASSERT(!result || (m_renderer->isReplaced() && !m_renderer->isImage()));
+ return result;
+}
+
+-(NSView*)attachmentView
+{
+ ASSERT(m_renderer->isReplaced() && m_renderer->isWidget() && !m_renderer->isImage());
+
+ RenderWidget* renderWidget = static_cast<RenderWidget*>(m_renderer);
+ Widget* widget = renderWidget->widget();
+ if (widget)
+ return widget->getView();
+
+ return nil;
+}
+
+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 int headingLevel(RenderObject* renderer)
+{
+ if (!renderer->isBlockFlow())
+ return 0;
+
+ Node* node = renderer->element();
+ if (!node)
+ return 0;
+
+ 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;
+}
+
+-(int)headingLevel
+{
+ return headingLevel(m_renderer);
+}
+
+-(BOOL)isHeading
+{
+ return [self headingLevel] != 0;
+}
+
+-(NSString*)role
+{
+ if (!m_renderer)
+ return NSAccessibilityUnknownRole;
+
+ if (m_areaElement)
+ return @"AXLink";
+ if (m_renderer->element() && m_renderer->element()->isLink()) {
+ if (m_renderer->isImage())
+ return @"AXImageMap";
+ return @"AXLink";
+ }
+ if (m_renderer->isListMarker())
+ return @"AXListMarker";
+ if (m_renderer->element() && m_renderer->element()->hasTagName(buttonTag))
+ return NSAccessibilityButtonRole;
+ if (m_renderer->isText())
+ return NSAccessibilityStaticTextRole;
+ if (m_renderer->isImage()) {
+ if ([self isImageButton])
+ return NSAccessibilityButtonRole;
+ return NSAccessibilityImageRole;
+ }
+ if ([self isWebArea])
+ return @"AXWebArea";
+
+ if (m_renderer->isTextField())
+ return NSAccessibilityTextFieldRole;
+
+ if (m_renderer->isTextArea())
+ return NSAccessibilityTextAreaRole;
+
+ if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
+ if (input->inputType() == HTMLInputElement::CHECKBOX)
+ return NSAccessibilityCheckBoxRole;
+ if (input->inputType() == HTMLInputElement::RADIO)
+ return NSAccessibilityRadioButtonRole;
+ if (input->isTextButton())
+ return NSAccessibilityButtonRole;
+ }
+
+ if (m_renderer->isMenuList())
+ return NSAccessibilityPopUpButtonRole;
+
+ if ([self isHeading])
+ return @"AXHeading";
+
+ if (m_renderer->isBlockFlow())
+ return NSAccessibilityGroupRole;
+ if ([self isAttachment])
+ return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute];
+
+ return NSAccessibilityUnknownRole;
+}
+
+-(NSString*)subrole
+{
+ if ([self isPasswordField])
+ return NSAccessibilitySecureTextFieldSubrole;
+
+ if ([self isAttachment]) {
+ NSView* attachmentView = [self attachmentView];
+ if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) {
+ return [attachmentView accessibilityAttributeValue:NSAccessibilitySubroleAttribute];
+ }
+ }
+
+ return nil;
+}
+
+-(NSString*)roleDescription
+{
+ if (!m_renderer)
+ return nil;
+
+ // attachments have the AXImage role, but a different subrole
+ if ([self 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* role = [self role];
+ if ([role isEqualToString:NSAccessibilityButtonRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityButtonRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityPopUpButtonRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityPopUpButtonRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityStaticTextRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityImageRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityImageRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityGroupRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityCheckBoxRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityCheckBoxRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityRadioButtonRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityRadioButtonRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityTextFieldRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityTextFieldRole, [self subrole]);
+
+ if ([role isEqualToString:NSAccessibilityTextAreaRole])
+ return NSAccessibilityRoleDescription(NSAccessibilityTextAreaRole, [self subrole]);
+
+ if ([role isEqualToString:@"AXWebArea"])
+ return AXWebAreaText();
+
+ if ([role isEqualToString:@"AXLink"])
+ return AXLinkText();
+
+ if ([role isEqualToString:@"AXListMarker"])
+ return AXListMarkerText();
+
+ if ([role isEqualToString:@"AXImageMap"])
+ return AXImageMapText();
+
+ if ([role isEqualToString:@"AXHeading"])
+ return AXHeadingText();
+
+ return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil);
+}
+
+-(NSString*)helpText
+{
+ if (!m_renderer)
+ return nil;
+
+ if (m_areaElement) {
+ const AtomicString& summary = static_cast<Element*>(m_areaElement)->getAttribute(summaryAttr);
+ if (!summary.isEmpty())
+ return summary;
+ const AtomicString& title = static_cast<Element*>(m_areaElement)->getAttribute(titleAttr);
+ if (!title.isEmpty())
+ return title;
+ }
+
+ 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 nil;
+}
+
+-(NSString*)textUnderElement
+{
+ if (!m_renderer)
+ return nil;
+
+ Node* e = m_renderer->element();
+ Document* d = m_renderer->document();
+ if (e && d) {
+ Frame* p = d->frame();
+ if (p) {
+ // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
+ if (p->document() != d)
+ return nil;
+ return plainText(rangeOfContents(e).get());
+ }
+ }
+
+ // return nil for anonymous text because it is non-trivial to get
+ // the actual text and, so far, that is not needed
+ return nil;
+}
+
+-(id)value
+{
+ if (!m_renderer || m_areaElement || [self isPasswordField])
+ return nil;
+
+ if (m_renderer->isText())
+ return [self textUnderElement];
+
+ if (m_renderer->isMenuList())
+ return static_cast<RenderMenuList*>(m_renderer)->text();
+
+ if (m_renderer->isListMarker())
+ return static_cast<RenderListMarker*>(m_renderer)->text();
+
+ if ([self isWebArea]) {
+ if (m_renderer->document()->frame())
+ return nil;
+
+ // 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 nil;
+
+ return plainText(makeRange(startVisiblePosition, endVisiblePosition).get());
+ }
+
+ if ([self isAttachment]) {
+ NSView* attachmentView = [self attachmentView];
+ if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute])
+ return [attachmentView accessibilityAttributeValue:NSAccessibilityValueAttribute];
+ return nil;
+ }
+
+ if ([self isHeading])
+ return [NSNumber numberWithInt:[self headingLevel]];
+
+ if ([self isTextControl])
+ return (NSString*)(static_cast<RenderTextControl*>(m_renderer)->text());
+
+ if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
+
+ // Checkboxes return their state as an integer. 0 for off, 1 for on.
+ if (input->inputType() == HTMLInputElement::CHECKBOX ||
+ input->inputType() == HTMLInputElement::RADIO)
+ return [NSNumber numberWithInt:input->checked()];
+ }
+
+ // 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 nil;
+}
+
+static HTMLLabelElement* labelForElement(Element* element)
+{
+ RefPtr<NodeList> list = element->document()->getElementsByTagName("label");
+ unsigned len = list->length();
+ for (unsigned i = 0; i < len; i++) {
+ HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i));
+ if (label->correspondingControl() == element)
+ return label;
+ }
+
+ return 0;
+}
+
+-(NSString*)title
+{
+ if (!m_renderer || m_areaElement || !m_renderer->element())
+ return nil;
+
+ if (m_renderer->element()->hasTagName(buttonTag))
+ return [self textUnderElement];
+
+ if (m_renderer->element()->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
+ if (input->isTextButton())
+ return input->value();
+
+ HTMLLabelElement* label = labelForElement(input);
+ if (label)
+ return label->innerText();
+ }
+
+ if (m_renderer->element()->isLink() || [self isHeading])
+ return [self textUnderElement];
+
+ if ([self isAttachment]) {
+ NSView* attachmentView = [self attachmentView];
+ if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute])
+ return [attachmentView accessibilityAttributeValue:NSAccessibilityTitleAttribute];
+ }
+
+ return nil;
+}
+
+- (NSString*)accessibilityDescription
+{
+ if (!m_renderer || m_areaElement)
+ return nil;
+
+ if (m_renderer->isImage()) {
+ if (m_renderer->element() && m_renderer->element()->isHTMLElement()) {
+ const AtomicString& alt = static_cast<Element*>(m_renderer->element())->getAttribute(altAttr);
+ if (alt.isEmpty())
+ return nil;
+ return alt;
+ }
+ } else if ([self isAttachment]) {
+ NSView* attachmentView = [self attachmentView];
+ if ([[attachmentView accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute])
+ return [attachmentView accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
+ }
+
+ if ([self isWebArea]) {
+ Document *document = m_renderer->document();
+ Node* owner = document->ownerElement();
+ if (owner) {
+ if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
+ HTMLFrameElementBase* frameElement = static_cast<HTMLFrameElementBase*>(owner);
+ return frameElement->name();
+ } else if (owner->isHTMLElement()) {
+ return static_cast<Element*>(owner)->getAttribute(nameAttr);
+ }
+ } else {
+ owner = document->body();
+ if (owner && owner->isHTMLElement())
+ return static_cast<Element*>(owner)->getAttribute(nameAttr);
+ }
+ }
+
+ return nil;
+}
+
+static IntRect boundingBoxRect(RenderObject* obj)
+{
+ IntRect rect;
+ if (obj) {
+ if (obj->isInlineContinuation())
+ obj = obj->element()->renderer();
+ 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;
+}
+
+-(NSValue*)position
+{
+ IntRect rect = m_areaElement ? m_areaElement->getRect(m_renderer) : boundingBoxRect(m_renderer);
+
+ // The Cocoa accessibility API wants the lower-left corner.
+ NSPoint point = NSMakePoint(rect.x(), rect.bottom());
+ if (m_renderer && m_renderer->view() && m_renderer->view()->frameView()) {
+ NSView* view = m_renderer->view()->frameView()->getDocumentView();
+ point = [[view window] convertBaseToScreen: [view convertPoint: point toView:nil]];
+ }
+
+ return [NSValue valueWithPoint: point];
+}
+
+-(NSValue*)size
+{
+ IntRect rect = m_areaElement ? m_areaElement->getRect(m_renderer) : boundingBoxRect(m_renderer);
+ return [NSValue valueWithSize: NSMakeSize(rect.width(), rect.height())];
+}
+
+// the closest object for an internal anchor
+-(id)linkedUIElement
+{
+ if (![self isAnchor])
+ return nil;
+
+ HTMLAnchorElement* anchor = [self anchorElement];
+ if (!anchor)
+ return nil;
+
+ KURL linkURL = anchor->href();
+ String ref = linkURL.ref();
+ if (ref.isEmpty())
+ return nil;
+
+ // check if URL is the same as current URL
+ linkURL.setRef("");
+ if (m_renderer->document()->url() != linkURL)
+ return nil;
+
+ Node* linkedNode = m_renderer->document()->getElementById(ref);
+ if (!linkedNode) {
+ linkedNode = m_renderer->document()->anchors()->namedItem(ref, !m_renderer->document()->inCompatMode());
+ if (!linkedNode)
+ return nil;
+ }
+
+ // the element we find may not be accessible, keep searching until we find a good one
+ WebCoreAXObject* linkedAXElement = m_renderer->document()->axObjectCache()->get(linkedNode->renderer());
+ while (linkedAXElement && [linkedAXElement accessibilityIsIgnored]) {
+ linkedNode = linkedNode->traverseNextNode(NULL);
+ if (!linkedNode)
+ return nil;
+ linkedAXElement = m_renderer->document()->axObjectCache()->get(linkedNode->renderer());
+ }
+
+ return linkedAXElement;
+}
+
+// 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)accessibilityShouldUseUniqueId {
+ if (!m_renderer)
+ return NO;
+
+ if ([self isWebArea])
+ return YES;
+
+ if ([self isTextControl])
+ return YES;
+
+ return NO;
+}
+
+-(BOOL)accessibilityIsIgnored
+{
+ // ignore invisible element
+ if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
+ return YES;
+
+ // ignore popup menu items because AppKit does
+ for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
+ if (parent->isMenuList())
+ return YES;
+ }
+
+ // NOTE: BRs always have text boxes now, so the text box check here can be removed
+ if (m_renderer->isText())
+ return m_renderer->isBR() || !static_cast<RenderText*>(m_renderer)->firstTextBox();
+
+ // delegate to the attachment
+ if ([self isAttachment])
+ return [[self attachmentView] accessibilityIsIgnored];
+
+ if (m_areaElement || (m_renderer->element() && m_renderer->element()->isLink()))
+ return NO;
+
+ // all controls are accessible
+ if (m_renderer->element() && m_renderer->element()->isControl())
+ return NO;
+
+ if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
+ return !static_cast<RenderBlock*>(m_renderer)->firstLineBox() && ![self mouseButtonListener];
+
+ // ignore images seemingly used as spacers
+ if (m_renderer->isImage()) {
+ // informal standard is to ignore images with zero-length alt strings
+ Element* elt = static_cast<Element*>(m_renderer->element());
+ if (elt) {
+ const AtomicString& alt = elt->getAttribute(altAttr);
+ if (alt.isEmpty() && !alt.isNull())
+ return YES;
+ }
+
+ // check for one-dimensional image
+ if (m_renderer->height() <= 1 || m_renderer->width() <= 1)
+ return YES;
+
+ // check whether rendered image was stretched from one-dimensional file image
+ RenderImage* image = static_cast<RenderImage*>(m_renderer);
+ if (image->cachedImage()) {
+ IntSize imageSize = image->cachedImage()->imageSize();
+ return (imageSize.height() <= 1 || imageSize.width() <= 1);
+ }
+
+ return NO;
+ }
+
+ return (!m_renderer->isListMarker() && ![self isWebArea]);
+}
+
+- (NSArray*)accessibilityAttributeNames
+{
+ if ([self isAttachment])
+ return [[self attachmentView] accessibilityAttributeNames];
+
+ static NSArray* attributes = nil;
+ static NSArray* anchorAttrs = nil;
+ static NSArray* webAreaAttrs = nil;
+ static NSArray* textAttrs = 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,
+ 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"];
+ 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];
+ textAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+
+ if (!m_renderer || [self isPasswordField])
+ return attributes;
+
+ if ([self isWebArea])
+ return webAreaAttrs;
+
+ if ([self isTextControl])
+ return textAttrs;
+
+ if ([self isAnchor] || m_renderer->isImage())
+ return anchorAttrs;
+
+ return attributes;
+}
+
+- (NSArray*)accessibilityActionNames
+{
+ static NSArray* actions = nil;
+
+ if (actions == nil) {
+ if ([self actionElement])
+ actions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, nil];
+ else if ([self isAttachment])
+ actions = [[[self attachmentView] accessibilityActionNames] retain];
+ }
+
+ return actions;
+}
+
+- (NSString*)accessibilityActionDescription:(NSString*)action
+{
+ // we have no custom actions
+ return NSAccessibilityActionDescription(action);
+}
+
+- (void)accessibilityPerformAction:(NSString*)action
+{
+ if ([action isEqualToString:NSAccessibilityPressAction]) {
+ if ([self isAttachment]) {
+ [[self attachmentView] accessibilityPerformAction:action];
+ return;
+ }
+
+ Element* actionElement = [self actionElement];
+ if (!actionElement)
+ return;
+ if (Frame* f = actionElement->document()->frame())
+ f->loader()->resetMultipleFormSubmissionProtection();
+ actionElement->accessKeyAction(true);
+ }
+}
+
+- (WebCoreTextMarkerRange*) textMarkerRangeFromMarkers: (WebCoreTextMarker*) textMarker1 andEndMarker:(WebCoreTextMarker*) textMarker2
+{
+ return [[WebCoreViewFactory sharedFactory] textMarkerRangeWithStart:textMarker1 end:textMarker2];
+}
+
+- (WebCoreTextMarker*) textMarkerForVisiblePosition: (VisiblePosition)visiblePos
+{
+ if (visiblePos.isNull())
+ return nil;
+
+ if (isPasswordFieldElement(visiblePos.deepEquivalent().node()))
+ return nil;
+
+ return m_renderer->document()->axObjectCache()->textMarkerForVisiblePosition(visiblePos);
+}
+
+- (VisiblePosition) visiblePositionForTextMarker: (WebCoreTextMarker*)textMarker
+{
+ return m_renderer->document()->axObjectCache()->visiblePositionForTextMarker(textMarker);
+}
+
+- (VisiblePosition) visiblePositionForStartOfTextMarkerRange: (WebCoreTextMarkerRange*)textMarkerRange
+{
+ return [self visiblePositionForTextMarker:[[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]];
+}
+
+- (VisiblePosition) visiblePositionForEndOfTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange
+{
+ return [self visiblePositionForTextMarker:[[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]];
+}
+
+- (WebCoreTextMarkerRange*) textMarkerRangeFromVisiblePositions: (VisiblePosition) startPosition andEndPos: (VisiblePosition) endPosition
+{
+ WebCoreTextMarker* startTextMarker = [self textMarkerForVisiblePosition: startPosition];
+ WebCoreTextMarker* endTextMarker = [self textMarkerForVisiblePosition: endPosition];
+ return [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker];
+}
+
+- (WebCoreTextMarkerRange*)textMarkerRange
+{
+ if (!m_renderer)
+ return nil;
+
+ // construct VisiblePositions for start and end
+ Node* node = m_renderer->element();
+ VisiblePosition visiblePos1 = VisiblePosition(node, 0, VP_DEFAULT_AFFINITY);
+ VisiblePosition visiblePos2 = VisiblePosition(node, maxDeepOffset(node), VP_DEFAULT_AFFINITY);
+
+ // the VisiblePositions are equal for nodes like buttons, so adjust for that
+ if (visiblePos1 == visiblePos2) {
+ visiblePos2 = visiblePos2.next();
+ if (visiblePos2.isNull())
+ visiblePos2 = visiblePos1;
+ }
+
+ WebCoreTextMarker* startTextMarker = [self textMarkerForVisiblePosition: visiblePos1];
+ WebCoreTextMarker* endTextMarker = [self textMarkerForVisiblePosition: visiblePos2];
+ return [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker];
+}
+
+- (RenderObject*)topRenderer
+{
+ return m_renderer->document()->topDocument()->renderer();
+}
+
+- (FrameView*)frameView
+{
+ return m_renderer->document()->view();
+}
+
+- (FrameView*)topFrameView
+{
+ return m_renderer->document()->topDocument()->renderer()->view()->frameView();
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attributeName
+{
+ if (!m_renderer)
+ 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_renderer->isRenderView() && m_renderer->view() && m_renderer->view()->frameView())
+ return m_renderer->view()->frameView()->getView();
+ return [self parentObjectUnignored];
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
+ if (!m_children) {
+ m_children = [NSMutableArray arrayWithCapacity: 8];
+ [m_children retain];
+ [self addChildrenToArray: m_children];
+ }
+ return m_children;
+ }
+
+ if ([self isWebArea]) {
+ if ([attributeName isEqualToString: @"AXLinkUIElements"]) {
+ NSMutableArray* links = [NSMutableArray arrayWithCapacity: 32];
+ RefPtr<HTMLCollection> coll = m_renderer->document()->links();
+ Node* curr = coll->firstItem();
+ while (curr) {
+ RenderObject* obj = curr->renderer();
+ if (obj) {
+ WebCoreAXObject* axobj = obj->document()->axObjectCache()->get(obj);
+ ASSERT([[axobj role] isEqualToString:@"AXLink"]);
+ if (![axobj accessibilityIsIgnored])
+ [links addObject: axobj];
+ }
+ curr = coll->nextItem();
+ }
+ return links;
+ }
+ if ([attributeName isEqualToString: @"AXLoaded"])
+ return [NSNumber numberWithBool: (!m_renderer->document()->tokenizer())];
+ if ([attributeName isEqualToString: @"AXLayoutCount"])
+ return [NSNumber numberWithInt: (static_cast<RenderView*>(m_renderer)->frameView()->layoutCount())];
+ }
+
+ if ([self isTextControl]) {
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+ if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute])
+ return [self isPasswordField] ? nil : [NSNumber numberWithUnsignedInt: textControl->text().length()];
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
+ if ([self isPasswordField])
+ return nil;
+ NSString* text = textControl->text();
+ return [text substringWithRange: NSMakeRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart())];
+ }
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute])
+ return [self isPasswordField] ? nil : [NSValue valueWithRange: NSMakeRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart())];
+ // TODO: Get actual visible range. <rdar://problem/4712101>
+ if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
+ return [self isPasswordField] ? nil : [NSValue valueWithRange: NSMakeRange(0, textControl->text().length())];
+ if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) {
+ if ([self isPasswordField] || textControl->selectionStart() != textControl->selectionEnd())
+ return nil;
+ NSNumber* index = [NSNumber numberWithInt: textControl->selectionStart()];
+ return [self doAXLineForTextMarker: [self textMarkerForIndex: index lastIndexOK: YES]];
+ }
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) {
+ if ([self isAnchor]) {
+ if (HTMLAnchorElement* anchor = [self anchorElement]) {
+ NSURL *href = anchor->href();
+ return href;
+ }
+ } else if (m_renderer->isImage() && m_renderer->element() && m_renderer->element()->hasTagName(imgTag)) {
+ NSURL *src = static_cast<HTMLImageElement*>(m_renderer->element())->src();
+ return src;
+ }
+ return nil;
+ }
+
+ if ([attributeName isEqualToString: @"AXVisited"])
+ return [NSNumber numberWithBool: m_renderer->style()->pseudoState() == PseudoVisited];
+
+ if ([attributeName isEqualToString: NSAccessibilityTitleAttribute])
+ return [self title];
+
+ if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute])
+ return [self accessibilityDescription];
+
+ if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
+ return [self value];
+
+ if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
+ return [self helpText];
+
+ if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
+ return [NSNumber numberWithBool: (m_renderer->element() && m_renderer->document()->focusedNode() == m_renderer->element())];
+
+ if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute])
+ return [NSNumber numberWithBool: m_renderer->element() ? m_renderer->element()->isEnabled() : YES];
+
+ if ([attributeName isEqualToString: NSAccessibilitySizeAttribute])
+ return [self size];
+
+ if ([attributeName isEqualToString: NSAccessibilityPositionAttribute])
+ return [self position];
+
+ if ([attributeName isEqualToString: NSAccessibilityWindowAttribute]) {
+ if (m_renderer && m_renderer->view() && m_renderer->view()->frameView())
+ return [m_renderer->view()->frameView()->getView() window];
+
+ return nil;
+ }
+
+ if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) {
+ // get the selection from the document
+ Selection selection = [self frameView]->frame()->selectionController()->selection();
+ if (selection.isNone())
+ return nil;
+
+ return (id) [self textMarkerRangeFromVisiblePositions:selection.visibleStart() andEndPos:selection.visibleEnd()];
+ }
+
+ if ([attributeName isEqualToString: @"AXStartTextMarker"])
+ return (id) [self textMarkerForVisiblePosition: startOfDocument(m_renderer->document())];
+
+ if ([attributeName isEqualToString: @"AXEndTextMarker"])
+ return (id) [self textMarkerForVisiblePosition: endOfDocument(m_renderer->document())];
+
+ if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute])
+ return (id) [self linkedUIElement];
+
+ return nil;
+}
+
+- (NSArray* )accessibilityParameterizedAttributeNames
+{
+ if ([self isAttachment])
+ return nil;
+
+ static NSArray* paramAttrs = nil;
+ static NSArray* textParamAttrs = 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",
+ 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 ([self isPasswordField])
+ return [NSArray array];
+
+ if (!m_renderer)
+ return paramAttrs;
+
+ if ([self isTextControl])
+ return textParamAttrs;
+
+ return paramAttrs;
+}
+
+- (id)doAXUIElementForTextMarker: (WebCoreTextMarker* ) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ RenderObject* obj = visiblePos.deepEquivalent().node()->renderer();
+ if (!obj)
+ return nil;
+
+ return obj->document()->axObjectCache()->get(obj);
+}
+
+- (id)doAXTextMarkerRangeForUIElement: (id) uiElement
+{
+ return (id)[uiElement textMarkerRange];
+}
+
+- (id)doAXLineForTextMarker: (WebCoreTextMarker* ) textMarker
+{
+ unsigned int lineCount = 0;
+ VisiblePosition savedVisiblePos;
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // move up until we get to the top
+ // NOTE: BUG This only takes us to the top of the rootEditableElement, not the top of the
+ // top document.
+ while (visiblePos.isNotNull() && !(inSameLine(visiblePos, savedVisiblePos))) {
+ lineCount += 1;
+ savedVisiblePos = visiblePos;
+ visiblePos = previousLinePosition(visiblePos, 0);
+ }
+
+ return [NSNumber numberWithUnsignedInt:(lineCount - 1)];
+}
+
+- (id)doAXTextMarkerRangeForLine: (NSNumber*) lineNumber
+{
+ unsigned lineCount = [lineNumber unsignedIntValue];
+ if (lineCount == 0 || !m_renderer)
+ return nil;
+
+ // iterate over the lines
+ // NOTE: BUG 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 nil;
+ }
+
+ // 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 selectionController;
+ selectionController.setSelection(Selection(visiblePos));
+ selectionController.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
+
+ // return a marker range for the selection start to end
+ VisiblePosition startPosition = selectionController.selection().visibleStart();
+ VisiblePosition endPosition = selectionController.selection().visibleEnd();
+ return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+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
+ WebCoreAXObject* 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];
+}
+
+- (id)doAXStringForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange
+{
+ // extract the start and end VisiblePosition
+ VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
+ if (startVisiblePosition.isNull())
+ return nil;
+
+ VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
+ if (endVisiblePosition.isNull())
+ return nil;
+
+ NSMutableString* resultString = [[[NSMutableString alloc] init] autorelease];
+ TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
+ while (!it.atEnd()) {
+ // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
+ if (it.length() != 0) {
+ [resultString appendString:[NSString stringWithCharacters:it.characters() 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);
+ NSString* attachmentString = nsStringForReplacedNode(node->childNode(offset));
+
+ // append the replacement string
+ if (attachmentString)
+ [resultString appendString:attachmentString];
+ }
+ it.advance();
+ }
+
+ return [resultString length] > 0 ? resultString : nil;
+}
+
+- (id)doAXTextMarkerForPosition: (NSPoint) point
+{
+ // convert absolute point to view coordinates
+ FrameView* frameView = [self topFrameView];
+ NSView* view = frameView->getDocumentView();
+ RenderObject* renderer = [self topRenderer];
+ Node* innerNode = 0;
+
+ // locate the node containing the point
+ IntPoint pointResult;
+ while (1) {
+ // ask the document layer to hitTest
+ NSPoint windowCoord = [[view window] convertScreenToBase: point];
+ IntPoint ourpoint([view convertPoint:windowCoord fromView:nil]);
+
+ HitTestRequest request(true, true);
+ HitTestResult result(ourpoint);
+ renderer->layer()->hitTest(request, result);
+ innerNode = result.innerNode();
+ if (!innerNode || !innerNode->renderer())
+ return nil;
+
+ 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);
+ view = frameView->getDocumentView();
+ }
+
+ // get position within the node
+ VisiblePosition pos = innerNode->renderer()->positionForPoint(pointResult);
+ return (id) [self textMarkerForVisiblePosition:pos];
+}
+
+- (id)doAXBoundsForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange
+{
+ // extract the start and end VisiblePosition
+ VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
+ if (startVisiblePosition.isNull())
+ return nil;
+
+ VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
+ if (endVisiblePosition.isNull())
+ return nil;
+
+ IntRect rect1 = startVisiblePosition.caretRect();
+ IntRect rect2 = endVisiblePosition.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(startVisiblePosition);
+ if (startVisiblePosition == endOfFirstLine) {
+ startVisiblePosition.setAffinity(DOWNSTREAM);
+ rect1 = startVisiblePosition.caretRect();
+ }
+ if (endVisiblePosition == endOfFirstLine) {
+ endVisiblePosition.setAffinity(UPSTREAM);
+ rect2 = endVisiblePosition.caretRect();
+ }
+ }
+
+ IntRect ourrect = rect1;
+ ourrect.unite(rect2);
+
+ // try to use the document view from the first position, so that nested WebAreas work,
+ // but fall back to the top level doc if we do not find it easily
+ RenderObject* renderer = startVisiblePosition.deepEquivalent().node()->renderer();
+ FrameView* frameView = renderer ? renderer->document()->view() : 0;
+ if (!frameView)
+ frameView = [self frameView];
+ NSView *view = frameView->getView();
+
+ // 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(startVisiblePosition, endVisiblePosition);
+ IntRect boundingBox = dataRange->boundingBox();
+ String rangeString = plainText(dataRange.get());
+ if (rangeString.length() > 1 && !boundingBox.isEmpty())
+ ourrect = boundingBox;
+ }
+
+ // convert our rectangle to screen coordinates
+ NSRect rect = ourrect;
+ rect = NSOffsetRect(rect, -frameView->contentsX(), -frameView->contentsY());
+ rect = [view convertRect:rect toView:nil];
+ rect.origin = [[view window] convertBaseToScreen:rect.origin];
+
+ // return the converted rect
+ return [NSValue valueWithRect:rect];
+}
+
+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 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 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 void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
+{
+ int parentHeadingLevel = headingLevel(renderer->parent());
+
+ if (parentHeadingLevel)
+ [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
+ else
+ [attrString removeAttribute:@"AXHeadingLevel" range:range];
+}
+
+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 AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, id element, NSRange range)
+{
+ if (element) {
+ // make a serialiazable AX object
+ AXUIElementRef axElement = [[WebCoreViewFactory sharedFactory] AXUIElementForElement:element];
+ if (axElement) {
+ [attrString addAttribute:attribute value:(id)axElement range:range];
+ CFRelease(axElement);
+ }
+ } else
+ [attrString removeAttribute:attribute range:range];
+}
+
+static WebCoreAXObject* AXLinkElementForNode (Node* node)
+{
+ RenderObject* obj = node->renderer();
+ if (!obj)
+ return nil;
+
+ WebCoreAXObject* axObj = obj->document()->axObjectCache()->get(obj);
+ HTMLAnchorElement* anchor = [axObj anchorElement];
+ if (!anchor || !anchor->renderer())
+ return nil;
+
+ return anchor->renderer()->document()->axObjectCache()->get(anchor->renderer());
+}
+
+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 AXAttributedStringAppendText(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(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);
+}
+
+- (id)doAXAttributedStringForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange
+{
+ // extract the start and end VisiblePosition
+ VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
+ if (startVisiblePosition.isNull())
+ return nil;
+
+ VisiblePosition endVisiblePosition = [self 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(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
+ WebCoreAXObject* obj = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer());
+ AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange);
+ }
+ }
+ it.advance();
+ }
+
+ return [attrString autorelease];
+}
+
+- (id)doAXTextMarkerRangeForUnorderedTextMarkers: (NSArray*) markers
+{
+ // get and validate the markers
+ if ([markers count] < 2)
+ return nil;
+
+ WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [markers objectAtIndex:0];
+ WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [markers objectAtIndex:1];
+ if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1] || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2])
+ return nil;
+
+ // convert to VisiblePosition
+ VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:textMarker1];
+ VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:textMarker2];
+ if (visiblePos1.isNull() || visiblePos2.isNull())
+ return nil;
+
+ WebCoreTextMarker* startTextMarker;
+ WebCoreTextMarker* endTextMarker;
+ 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) {
+ startTextMarker = textMarker1;
+ endTextMarker = textMarker2;
+ } else {
+ startTextMarker = textMarker2;
+ endTextMarker = textMarker1;
+ }
+
+ return (id) [self textMarkerRangeFromMarkers: startTextMarker andEndMarker:endTextMarker];
+}
+
+- (id)doAXNextTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return nil;
+
+ return (id) [self textMarkerForVisiblePosition:nextVisiblePos];
+}
+
+- (id)doAXPreviousTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ VisiblePosition previousVisiblePos = visiblePos.previous();
+ if (previousVisiblePos.isNull())
+ return nil;
+
+ return (id) [self textMarkerForVisiblePosition:previousVisiblePos];
+}
+
+- (id)doAXLeftWordTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
+ VisiblePosition endPosition = endOfWord(startPosition);
+
+ return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+- (id)doAXRightWordTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
+ VisiblePosition endPosition = endOfWord(startPosition);
+
+ return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos: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->inlineBox(p.offset(), tempPosition.affinity()) || (renderer->isRenderBlock() && p.offset() == 0))
+ break;
+ startPosition = tempPosition;
+ }
+
+ return startPosition;
+}
+
+- (id)doAXLeftLineTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // 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 nil;
+
+ VisiblePosition startPosition = startOfLine(prevVisiblePos);
+
+ // keep searching for a valid line start position. Unless the textmarker 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 (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+- (id)doAXRightLineTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a line end
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return nil;
+
+ 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 textmarker 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 (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+- (id)doAXSentenceTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
+ // Related? <rdar://problem/3927736> Text selection broken in 8A336
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ VisiblePosition startPosition = startOfSentence(visiblePos);
+ VisiblePosition endPosition = endOfSentence(startPosition);
+ return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+- (id)doAXParagraphTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ VisiblePosition startPosition = startOfParagraph(visiblePos);
+ VisiblePosition endPosition = endOfParagraph(startPosition);
+ return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+- (id)doAXNextWordEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a word end
+ visiblePos = visiblePos.next();
+ if (visiblePos.isNull())
+ return nil;
+
+ VisiblePosition endPosition = endOfWord(visiblePos, LeftWordIfOnBoundary);
+ return (id) [self textMarkerForVisiblePosition:endPosition];
+}
+
+- (id)doAXPreviousWordStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a word start
+ visiblePos = visiblePos.previous();
+ if (visiblePos.isNull())
+ return nil;
+
+ VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
+ return (id) [self textMarkerForVisiblePosition:startPosition];
+}
+
+- (id)doAXNextLineEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // to make sure we move off of a line end
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return nil;
+
+ 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 (id) [self textMarkerForVisiblePosition: endPosition];
+}
+
+- (id)doAXPreviousLineStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a line start
+ VisiblePosition prevVisiblePos = visiblePos.previous();
+ if (prevVisiblePos.isNull())
+ return nil;
+
+ 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 (id) [self textMarkerForVisiblePosition: startPosition];
+}
+
+- (id)doAXNextSentenceEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
+ // Related? <rdar://problem/3927736> Text selection broken in 8A336
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a sentence end
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return nil;
+
+ // 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 (id) [self textMarkerForVisiblePosition: endPosition];
+}
+
+- (id)doAXPreviousSentenceStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ // NOTE: BUG FO 2 IMPLEMENT (currently returns incorrect answer)
+ // Related? <rdar://problem/3927736> Text selection broken in 8A336
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a sentence start
+ VisiblePosition previousVisiblePos = visiblePos.previous();
+ if (previousVisiblePos.isNull())
+ return nil;
+
+ // 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 (id) [self textMarkerForVisiblePosition: startPosition];
+}
+
+- (id)doAXNextParagraphEndTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a paragraph end
+ visiblePos = visiblePos.next();
+ if (visiblePos.isNull())
+ return nil;
+
+ VisiblePosition endPosition = endOfParagraph(visiblePos);
+ return (id) [self textMarkerForVisiblePosition: endPosition];
+}
+
+- (id)doAXPreviousParagraphStartTextMarkerForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ // make sure we move off of a paragraph start
+ visiblePos = visiblePos.previous();
+ if (visiblePos.isNull())
+ return nil;
+
+ VisiblePosition startPosition = startOfParagraph(visiblePos);
+ return (id) [self textMarkerForVisiblePosition: startPosition];
+}
+
+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);
+}
+
+- (id)doAXStyleTextMarkerRangeForTextMarker: (WebCoreTextMarker*) textMarker
+{
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:textMarker];
+ if (visiblePos.isNull())
+ return nil;
+
+ VisiblePosition startPosition = startOfStyleRange(visiblePos);
+ VisiblePosition endPosition = endOfStyleRange(visiblePos);
+ return (id) [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+- (id)doAXLengthForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange
+{
+ // NOTE: BUG Multi-byte support
+ CFStringRef string = (CFStringRef) [self doAXStringForTextMarkerRange: textMarkerRange];
+ if (!string)
+ return nil;
+
+ return [NSNumber numberWithInt:CFStringGetLength(string)];
+}
+
+// NOTE: Consider providing this utility method as AX API
+- (WebCoreTextMarker*)textMarkerForIndex: (NSNumber*) index lastIndexOK: (BOOL)lastIndexOK
+{
+ ASSERT(m_renderer->isTextField() || m_renderer->isTextArea());
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+ unsigned int indexValue = [index unsignedIntValue];
+
+ // lastIndexOK specifies whether the position after the last character is acceptable
+ if (indexValue >= textControl->text().length()) {
+ if (!lastIndexOK || indexValue > textControl->text().length())
+ return nil;
+ }
+ VisiblePosition position = textControl->visiblePositionForIndex(indexValue);
+ position.setAffinity(DOWNSTREAM);
+ return [self textMarkerForVisiblePosition: position];
+}
+
+// NOTE: Consider providing this utility method as AX API
+- (NSNumber*)indexForTextMarker: (WebCoreTextMarker*) marker
+{
+ ASSERT(m_renderer->isTextField() || m_renderer->isTextArea());
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+
+ VisiblePosition position = [self visiblePositionForTextMarker: marker];
+ Node* node = position.deepEquivalent().node();
+ if (!node)
+ return nil;
+
+ for (RenderObject* renderer = node->renderer(); renderer && renderer->element(); renderer = renderer->parent()) {
+ if (renderer == textControl)
+ return [NSNumber numberWithInt: textControl->indexForVisiblePosition(position)];
+ }
+
+ return nil;
+}
+
+// NOTE: Consider providing this utility method as AX API
+- (WebCoreTextMarkerRange*)textMarkerRangeForRange: (NSRange) range
+{
+ ASSERT(m_renderer->isTextField() || m_renderer->isTextArea());
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+ if (range.location + range.length > textControl->text().length())
+ return nil;
+
+ VisiblePosition startPosition = textControl->visiblePositionForIndex(range.location);
+ startPosition.setAffinity(DOWNSTREAM);
+ VisiblePosition endPosition = textControl->visiblePositionForIndex(range.location + range.length);
+ return [self textMarkerRangeFromVisiblePositions:startPosition andEndPos:endPosition];
+}
+
+// NOTE: Consider providing this utility method as AX API
+- (NSValue*)rangeForTextMarkerRange: (WebCoreTextMarkerRange*) textMarkerRange
+{
+ WebCoreTextMarker* textMarker1 = [[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange];
+ WebCoreTextMarker* textMarker2 = [[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange];
+ NSNumber* index1 = [self indexForTextMarker: textMarker1];
+ NSNumber* index2 = [self indexForTextMarker: textMarker2];
+ if (!index1 || !index2 || [index1 unsignedIntValue] > [index2 unsignedIntValue])
+ return nil;
+
+ return [NSValue valueWithRange: NSMakeRange([index1 unsignedIntValue], [index2 unsignedIntValue] - [index1 unsignedIntValue])];
+}
+
+// Given an indexed character, the line number of the text associated with this accessibility
+// object that contains the character.
+- (id)doAXLineForIndex: (NSNumber*) index
+{
+ return [self doAXLineForTextMarker: [self textMarkerForIndex: index lastIndexOK: NO]];
+}
+
+// Given a line number, the range of characters of the text associated with this accessibility
+// object that contains the line number.
+- (id)doAXRangeForLine: (NSNumber*) lineNumber
+{
+ ASSERT(m_renderer->isTextField() || m_renderer->isTextArea());
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+
+ // iterate to the specified line
+ VisiblePosition visiblePos = textControl->visiblePositionForIndex(0);
+ VisiblePosition savedVisiblePos;
+ for (unsigned lineCount = [lineNumber unsignedIntValue]; lineCount != 0; lineCount -= 1) {
+ savedVisiblePos = visiblePos;
+ visiblePos = nextLinePosition(visiblePos, 0);
+ if (visiblePos.isNull() || visiblePos == savedVisiblePos)
+ return nil;
+ }
+
+ // make a caret selection for the marker position, then extend it to the line
+ // NOTE: ignores results of selectionController.modify because it returns false when
+ // starting at an empty line. The resulting selection in that case
+ // will be a caret at visiblePos.
+ SelectionController selectionController;
+ selectionController.setSelection(Selection(visiblePos));
+ selectionController.modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary);
+ selectionController.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
+
+ // calculate the indices for the selection start and end
+ VisiblePosition startPosition = selectionController.selection().visibleStart();
+ VisiblePosition endPosition = selectionController.selection().visibleEnd();
+ int index1 = textControl->indexForVisiblePosition(startPosition);
+ int index2 = textControl->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 nil;
+
+ return [NSValue valueWithRange: NSMakeRange(index1, index2 - index1)];
+}
+
+// A substring of the text associated with this accessibility object that is
+// specified by the given character range.
+- (id)doAXStringForRange: (NSRange) range
+{
+ if ([self isPasswordField])
+ return nil;
+
+ if (range.length == 0)
+ return @"";
+
+ ASSERT(m_renderer->isTextField() || m_renderer->isTextArea());
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+ String text = textControl->text();
+ if (range.location + range.length > text.length())
+ return nil;
+
+ return text.substring(range.location, range.length);
+}
+
+// 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?
+- (id)doAXRangeForPosition: (NSPoint) point
+{
+ NSNumber* index = [self indexForTextMarker: [self doAXTextMarkerForPosition: point]];
+ if (!index)
+ return nil;
+
+ return [NSValue valueWithRange: NSMakeRange([index unsignedIntValue], 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.
+- (id)doAXRangeForIndex: (NSNumber*) number
+{
+ ASSERT(m_renderer->isTextField() || m_renderer->isTextArea());
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+ String text = textControl->text();
+ if (!text.length() || [number unsignedIntValue] > text.length() - 1)
+ return nil;
+
+ return [NSValue valueWithRange: NSMakeRange([number unsignedIntValue], 1)];
+}
+
+// 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.
+- (id)doAXBoundsForRange: (NSRange) range
+{
+ return [self doAXBoundsForTextMarkerRange: [self textMarkerRangeForRange:range]];
+}
+
+// The CFAttributedStringType representation of the text associated with this accessibility
+// object that is specified by the given range.
+- (id)doAXAttributedStringForRange: (NSRange) range
+{
+ return [self doAXAttributedStringForTextMarkerRange: [self textMarkerRangeForRange:range]];
+}
+
+// The RTF representation of the text associated with this accessibility object that is
+// specified by the given range.
+- (id)doAXRTFForRange: (NSRange) range
+{
+ NSAttributedString* attrString = [self doAXAttributedStringForRange: range];
+ return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil];
+}
+
+// Given a character index, the range of text associated with this accessibility object
+// over which the style in effect at that character index applies.
+- (id)doAXStyleRangeForIndex: (NSNumber*) index
+{
+ WebCoreTextMarkerRange* textMarkerRange = [self doAXStyleTextMarkerRangeForTextMarker: [self textMarkerForIndex: index lastIndexOK: NO]];
+ return [self rangeForTextMarkerRange: textMarkerRange];
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
+{
+ WebCoreTextMarker* textMarker = nil;
+ WebCoreTextMarkerRange* textMarkerRange = nil;
+ NSNumber* number = nil;
+ NSArray* array = nil;
+ WebCoreAXObject* uiElement = nil;
+ NSPoint point = NSZeroPoint;
+ bool pointSet = false;
+ NSRange range = {0, 0};
+ bool rangeSet = false;
+
+ // basic parameter validation
+ if (!m_renderer || !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:[WebCoreAXObject self]])
+ uiElement = (WebCoreAXObject*) parameter;
+
+ 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;
+ }
+
+ // dispatch
+ if ([attribute isEqualToString: @"AXUIElementForTextMarker"])
+ return [self doAXUIElementForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXTextMarkerRangeForUIElement"])
+ return [self doAXTextMarkerRangeForUIElement: uiElement];
+
+ if ([attribute isEqualToString: @"AXLineForTextMarker"])
+ return [self doAXLineForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXTextMarkerRangeForLine"])
+ return [self doAXTextMarkerRangeForLine: number];
+
+ if ([attribute isEqualToString: @"AXStringForTextMarkerRange"])
+ return [self doAXStringForTextMarkerRange: textMarkerRange];
+
+ if ([attribute isEqualToString: @"AXTextMarkerForPosition"])
+ return pointSet ? [self doAXTextMarkerForPosition: point] : nil;
+
+ if ([attribute isEqualToString: @"AXBoundsForTextMarkerRange"])
+ return [self doAXBoundsForTextMarkerRange: textMarkerRange];
+
+ if ([attribute isEqualToString: @"AXAttributedStringForTextMarkerRange"])
+ return [self doAXAttributedStringForTextMarkerRange: textMarkerRange];
+
+ if ([attribute isEqualToString: @"AXTextMarkerRangeForUnorderedTextMarkers"])
+ return [self doAXTextMarkerRangeForUnorderedTextMarkers: array];
+
+ if ([attribute isEqualToString: @"AXNextTextMarkerForTextMarker"])
+ return [self doAXNextTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXPreviousTextMarkerForTextMarker"])
+ return [self doAXPreviousTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXLeftWordTextMarkerRangeForTextMarker"])
+ return [self doAXLeftWordTextMarkerRangeForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXRightWordTextMarkerRangeForTextMarker"])
+ return [self doAXRightWordTextMarkerRangeForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXLeftLineTextMarkerRangeForTextMarker"])
+ return [self doAXLeftLineTextMarkerRangeForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXRightLineTextMarkerRangeForTextMarker"])
+ return [self doAXRightLineTextMarkerRangeForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXSentenceTextMarkerRangeForTextMarker"])
+ return [self doAXSentenceTextMarkerRangeForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXParagraphTextMarkerRangeForTextMarker"])
+ return [self doAXParagraphTextMarkerRangeForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXNextWordEndTextMarkerForTextMarker"])
+ return [self doAXNextWordEndTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXPreviousWordStartTextMarkerForTextMarker"])
+ return [self doAXPreviousWordStartTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXNextLineEndTextMarkerForTextMarker"])
+ return [self doAXNextLineEndTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXPreviousLineStartTextMarkerForTextMarker"])
+ return [self doAXPreviousLineStartTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXNextSentenceEndTextMarkerForTextMarker"])
+ return [self doAXNextSentenceEndTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXPreviousSentenceStartTextMarkerForTextMarker"])
+ return [self doAXPreviousSentenceStartTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXNextParagraphEndTextMarkerForTextMarker"])
+ return [self doAXNextParagraphEndTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXPreviousParagraphStartTextMarkerForTextMarker"])
+ return [self doAXPreviousParagraphStartTextMarkerForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXStyleTextMarkerRangeForTextMarker"])
+ return [self doAXStyleTextMarkerRangeForTextMarker: textMarker];
+
+ if ([attribute isEqualToString: @"AXLengthForTextMarkerRange"])
+ return [self doAXLengthForTextMarkerRange: textMarkerRange];
+
+ if ([self isTextControl]) {
+ if ([attribute isEqualToString: (NSString*)kAXLineForIndexParameterizedAttribute])
+ return [self doAXLineForIndex: number];
+
+ if ([attribute isEqualToString: (NSString*)kAXRangeForLineParameterizedAttribute])
+ return [self doAXRangeForLine: number];
+
+ if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute])
+ return rangeSet ? [self doAXStringForRange: range] : nil;
+
+ if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute])
+ return pointSet ? [self doAXRangeForPosition: point] : nil;
+
+ if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute])
+ return [self doAXRangeForIndex: number];
+
+ if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute])
+ return rangeSet ? [self doAXBoundsForRange: range] : nil;
+
+ 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])
+ return [self doAXStyleRangeForIndex: number];
+ }
+
+ return nil;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ if (!m_renderer)
+ return NSAccessibilityUnignoredAncestor(self);
+
+ HitTestRequest request(true, true);
+ HitTestResult result = HitTestResult(IntPoint(point));
+ m_renderer->layer()->hitTest(request, result);
+ if (!result.innerNode())
+ return NSAccessibilityUnignoredAncestor(self);
+ Node* node = result.innerNode()->shadowAncestorNode();
+ RenderObject* obj = node->renderer();
+ if (!obj)
+ return NSAccessibilityUnignoredAncestor(self);
+
+ return NSAccessibilityUnignoredAncestor(obj->document()->axObjectCache()->get(obj));
+}
+
+- (RenderObject*)rendererForView:(NSView*)view
+{
+ // check for WebKit NSView that lets us find its bridge
+ WebCoreFrameBridge* bridge = nil;
+ if ([view conformsToProtocol:@protocol(WebCoreBridgeHolder)]) {
+ NSView<WebCoreBridgeHolder>* bridgeHolder = (NSView<WebCoreBridgeHolder>*)view;
+ bridge = [bridgeHolder webCoreBridge];
+ }
+
+ Frame* frame = [bridge _frame];
+ if (!frame)
+ return nil;
+
+ Document* document = frame->document();
+ if (!document)
+ return nil;
+
+ Node* node = document->ownerElement();
+ if (!node)
+ return nil;
+
+ return node->renderer();
+}
+
+// _accessibilityParentForSubview is called by AppKit when moving up the tree
+// we override it so that we can return our WebCoreAXObject parent of an AppKit AX object
+- (id)_accessibilityParentForSubview:(NSView*)subview
+{
+ RenderObject* renderer = [self rendererForView:subview];
+ if (!renderer)
+ return nil;
+
+ WebCoreAXObject* obj = renderer->document()->axObjectCache()->get(renderer);
+ return [obj parentObjectUnignored];
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ // get the focused node in the page
+ Page* page = m_renderer->document()->page();
+ if (!page)
+ return nil;
+
+ Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
+ Node* focusedNode = focusedDocument->focusedNode();
+ if (!focusedNode || !focusedNode->renderer())
+ focusedNode = focusedDocument;
+
+ WebCoreAXObject* obj = focusedNode->renderer()->document()->axObjectCache()->get(focusedNode->renderer());
+
+ // the HTML element, for example, is focusable but has an AX object that is ignored
+ if ([obj accessibilityIsIgnored])
+ obj = [obj parentObjectUnignored];
+
+ return obj;
+}
+
+- (void)doSetAXSelectedTextMarkerRange: (WebCoreTextMarkerRange*)textMarkerRange
+{
+ // extract the start and end VisiblePosition
+ VisiblePosition startVisiblePosition = [self visiblePositionForStartOfTextMarkerRange: textMarkerRange];
+ if (startVisiblePosition.isNull())
+ return;
+
+ VisiblePosition endVisiblePosition = [self visiblePositionForEndOfTextMarkerRange: textMarkerRange];
+ if (endVisiblePosition.isNull())
+ return;
+
+ // make selection and tell the document to use it
+ Selection newSelection = Selection(startVisiblePosition, endVisiblePosition);
+ m_renderer->document()->frame()->selectionController()->setSelection(newSelection);
+}
+
+- (BOOL)canSetFocusAttribute
+{
+ // 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 NO;
+
+ NSString* role = [self role];
+ if ([role isEqualToString:@"AXLink"] ||
+ [role isEqualToString:NSAccessibilityTextFieldRole] ||
+ [role isEqualToString:NSAccessibilityTextAreaRole] ||
+ [role isEqualToString:NSAccessibilityButtonRole] ||
+ [role isEqualToString:NSAccessibilityPopUpButtonRole] ||
+ [role isEqualToString:NSAccessibilityCheckBoxRole] ||
+ [role isEqualToString:NSAccessibilityRadioButtonRole])
+ return YES;
+
+ return NO;
+}
+
+- (BOOL)canSetValueAttribute
+{
+ return [self isTextControl];
+}
+
+- (BOOL)canSetTextRangeAttributes
+{
+ return [self isTextControl];
+}
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
+{
+ if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
+ return YES;
+
+ if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
+ return [self canSetFocusAttribute];
+
+ if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
+ return [self canSetValueAttribute];
+
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] ||
+ [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] ||
+ [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
+ return [self canSetTextRangeAttributes];
+
+ return NO;
+}
+
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName
+{
+ WebCoreTextMarkerRange* textMarkerRange = nil;
+ NSNumber* number = nil;
+ NSString* string = nil;
+ NSRange range = {0, 0};
+
+ // 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];
+
+ // handle the command
+ if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) {
+ ASSERT(textMarkerRange);
+ [self doSetAXSelectedTextMarkerRange:textMarkerRange];
+
+ } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
+ ASSERT(number);
+ if ([self canSetFocusAttribute] && number) {
+ if ([number intValue] == 0)
+ 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());
+ }
+ }
+ } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
+ if (!string)
+ return;
+ 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);
+ }
+ } else if ([self isTextControl]) {
+ RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
+ // TODO: set selected text (ReplaceSelectionCommand). <rdar://problem/4712125>
+ } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
+ textControl->setSelectionRange(range.location, range.location + range.length);
+ } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) {
+ // TODO: make range visible (scrollRectToVisible). <rdar://problem/4712101>
+ }
+ }
+}
+
+- (WebCoreAXObject*)observableObject
+{
+ for (RenderObject* renderer = m_renderer; renderer && renderer->element(); renderer = renderer->parent()) {
+ if (renderer->isTextField() || renderer->isTextArea())
+ return renderer->document()->axObjectCache()->get(renderer);
+ }
+
+ return nil;
+}
+
+- (void)childrenChanged
+{
+ [self clearChildren];
+
+ if ([self accessibilityIsIgnored])
+ [[self parentObject] childrenChanged];
+}
+
+- (void)clearChildren
+{
+ [m_children release];
+ m_children = nil;
+}
+
+-(AXID)axObjectID
+{
+ return m_id;
+}
+
+-(void)setAXObjectID:(AXID) axObjectID
+{
+ m_id = axObjectID;
+}
+
+- (void)removeAXObjectID
+{
+ if (!m_id)
+ return;
+
+ m_renderer->document()->axObjectCache()->removeAXID(self);
+}
+
+@end
diff --git a/WebCore/page/mac/WebCoreFrameBridge.h b/WebCore/page/mac/WebCoreFrameBridge.h
new file mode 100644
index 0000000..76315f7
--- /dev/null
+++ b/WebCore/page/mac/WebCoreFrameBridge.h
@@ -0,0 +1,254 @@
+/*
+ * 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;
+
+@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
new file mode 100644
index 0000000..3f20706
--- /dev/null
+++ b/WebCore/page/mac/WebCoreFrameBridge.mm
@@ -0,0 +1,1242 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2008 Apple 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 "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?
+ return doc->completeURL(parseURL(string));
+}
+
+- (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->document()->baseURL();
+}
+
+- (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());
+}
+
+- (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
new file mode 100644
index 0000000..a478dca
--- /dev/null
+++ b/WebCore/page/mac/WebCoreFrameView.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2003 Apple Computer, 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.
+ */
+
+@class WebCoreFrameBridge;
+
+// 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;
+
+- (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;
+@end
diff --git a/WebCore/page/mac/WebCoreKeyboardUIMode.h b/WebCore/page/mac/WebCoreKeyboardUIMode.h
new file mode 100644
index 0000000..187cf09
--- /dev/null
+++ b/WebCore/page/mac/WebCoreKeyboardUIMode.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2003 Apple Computer, 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 WebCoreKeyboardUIMode_h
+#define WebCoreKeyboardUIMode_h
+
+namespace WebCore {
+
+ enum KeyboardUIMode {
+ KeyboardAccessDefault = 0x00000000,
+ KeyboardAccessFull = 0x00000001,
+ // this flag may be or'ed with either of the two above
+ KeyboardAccessTabsToLinks = 0x10000000
+ };
+
+}
+
+#endif
diff --git a/WebCore/page/mac/WebCoreScriptDebugger.h b/WebCore/page/mac/WebCoreScriptDebugger.h
new file mode 100644
index 0000000..fd56632
--- /dev/null
+++ b/WebCore/page/mac/WebCoreScriptDebugger.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2005 Apple Computer, 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.
+ */
+
+@class WebScriptObject; // from JavaScriptCore
+@class WebCoreScriptCallFrame; // below
+
+#ifdef __cplusplus
+class WebCoreScriptDebuggerImp;
+namespace KJS { class ExecState; }
+using KJS::ExecState;
+#else
+@class WebCoreScriptDebuggerImp;
+@class ExecState;
+#endif
+
+
+
+// "WebScriptDebugger" protocol - must be implemented by a delegate
+
+@protocol WebScriptDebugger
+
+- (WebScriptObject *)globalObject; // return the WebView's windowScriptObject
+- (id)newWrapperForFrame:(WebCoreScriptCallFrame *)frame; // return a (retained) stack-frame object
+
+// debugger callbacks
+- (void)parsedSource:(NSString *)source fromURL:(NSURL *)url sourceId:(int)sid startLine:(int)startLine errorLine:(int)errorLine errorMessage:(NSString *)errorMessage;
+- (void)enteredFrame:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno;
+- (void)hitStatement:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno;
+- (void)leavingFrame:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno;
+- (void)exceptionRaised:(WebCoreScriptCallFrame *)frame sourceId:(int)sid line:(int)lineno;
+
+@end
+
+
+
+@interface WebCoreScriptDebugger : NSObject
+{
+@private
+ id<WebScriptDebugger> _delegate; // interface to WebKit (not retained)
+ WebScriptObject *_globalObj; // the global object's proxy (not retained)
+ WebCoreScriptCallFrame *_current; // top of stack
+ WebCoreScriptDebuggerImp *_debugger; // [KJS::Debugger]
+}
+
+- (WebCoreScriptDebugger *)initWithDelegate:(id<WebScriptDebugger>)delegate;
+- (id<WebScriptDebugger>)delegate;
+
+@end
+
+
+
+@interface WebCoreScriptCallFrame : NSObject
+{
+@private
+ id _wrapper; // WebKit's version of this object
+ WebScriptObject *_globalObj; // the global object's proxy (not retained)
+ WebCoreScriptCallFrame *_caller; // previous stack frame
+ ExecState *_state; // [KJS::ExecState]
+}
+
+- (id)wrapper;
+- (WebCoreScriptCallFrame *)caller;
+
+- (NSArray *)scopeChain;
+- (NSString *)functionName;
+- (id)exception;
+- (id)evaluateWebScript:(NSString *)script;
+
+@end
diff --git a/WebCore/page/mac/WebCoreScriptDebugger.mm b/WebCore/page/mac/WebCoreScriptDebugger.mm
new file mode 100644
index 0000000..ae68a63
--- /dev/null
+++ b/WebCore/page/mac/WebCoreScriptDebugger.mm
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2005, 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 "WebCoreScriptDebugger.h"
+
+#import "KURL.h"
+#import "PlatformString.h"
+#import "WebCoreObjCExtras.h"
+#import "WebScriptObjectPrivate.h"
+#import <JavaScriptCore/ExecState.h>
+#import <JavaScriptCore/JSGlobalObject.h>
+#import <JavaScriptCore/debugger.h>
+#import <JavaScriptCore/function.h>
+#import <JavaScriptCore/interpreter.h>
+
+using namespace KJS;
+using namespace WebCore;
+
+@interface WebCoreScriptDebugger (WebCoreScriptDebuggerInternal)
+
+- (WebCoreScriptCallFrame *)_enterFrame:(ExecState *)state;
+- (WebCoreScriptCallFrame *)_leaveFrame;
+
+@end
+
+@interface WebCoreScriptCallFrame (WebCoreScriptDebuggerInternal)
+
+- (WebCoreScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebCoreScriptCallFrame *)caller state:(ExecState *)state;
+- (void)_setWrapper:(id)wrapper;
+- (id)_convertValueToObjcValue:(JSValue *)value;
+
+@end
+
+// convert UString to NSString
+static NSString *toNSString(const UString& s)
+{
+ if (s.isEmpty())
+ return nil;
+ return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(s.data()) length:s.size()];
+}
+
+// convert UString to NSURL
+static NSURL *toNSURL(const UString& s)
+{
+ if (s.isEmpty())
+ return nil;
+ return KURL(s);
+}
+
+// C++ interface to KJS debugger callbacks
+
+class WebCoreScriptDebuggerImp : public KJS::Debugger {
+
+ private:
+ WebCoreScriptDebugger *_objc; // our ObjC half
+ bool _nested; // true => this is a nested call
+ WebCoreScriptCallFrame *_current; // top stack frame (copy of same field from ObjC side)
+
+ public:
+ // constructor
+ WebCoreScriptDebuggerImp(WebCoreScriptDebugger *objc, JSGlobalObject* globalObject) : _objc(objc) {
+ _nested = true;
+ _current = [_objc _enterFrame:globalObject->globalExec()];
+ attach(globalObject);
+ [[_objc delegate] enteredFrame:_current sourceId:-1 line:-1];
+ _nested = false;
+ }
+
+ // callbacks - relay to delegate
+ virtual bool sourceParsed(ExecState *state, int sid, const UString &url, const UString &source, int lineNumber, int errorLine, const UString &errorMsg) {
+ if (!_nested) {
+ _nested = true;
+ [[_objc delegate] parsedSource:toNSString(source) fromURL:toNSURL(url) sourceId:sid startLine:lineNumber errorLine:errorLine errorMessage:toNSString(errorMsg)];
+ _nested = false;
+ }
+ return true;
+ }
+ virtual bool callEvent(ExecState *state, int sid, int lineno, JSObject *func, const List &args) {
+ if (!_nested) {
+ _nested = true;
+ _current = [_objc _enterFrame:state];
+ [[_objc delegate] enteredFrame:_current sourceId:sid line:lineno];
+ _nested = false;
+ }
+ return true;
+ }
+ virtual bool atStatement(ExecState *state, int sid, int lineno, int lastLine) {
+ if (!_nested) {
+ _nested = true;
+ [[_objc delegate] hitStatement:_current sourceId:sid line:lineno];
+ _nested = false;
+ }
+ return true;
+ }
+ virtual bool returnEvent(ExecState *state, int sid, int lineno, JSObject *func) {
+ if (!_nested) {
+ _nested = true;
+ [[_objc delegate] leavingFrame:_current sourceId:sid line:lineno];
+ _current = [_objc _leaveFrame];
+ _nested = false;
+ }
+ return true;
+ }
+ virtual bool exception(ExecState *state, int sid, int lineno, JSValue *exception) {
+ if (!_nested) {
+ _nested = true;
+ [[_objc delegate] exceptionRaised:_current sourceId:sid line:lineno];
+ _nested = false;
+ }
+ return true;
+ }
+
+};
+
+// WebCoreScriptDebugger
+//
+// This is the main (behind-the-scenes) debugger class in WebCore.
+//
+// The WebCoreScriptDebugger has two faces, one for Objective-C (this class), and another (WebCoreScriptDebuggerImp)
+// for C++. The ObjC side creates the C++ side, which does the real work of attaching to the global object and
+// forwarding the KJS debugger callbacks to the delegate.
+
+@implementation WebCoreScriptDebugger
+
+#ifndef BUILDING_ON_TIGER
++ (void)initialize
+{
+ WebCoreObjCFinalizeOnMainThread(self);
+}
+#endif
+
+- (WebCoreScriptDebugger *)initWithDelegate:(id<WebScriptDebugger>)delegate
+{
+ if ((self = [super init])) {
+ _delegate = delegate;
+ _globalObj = [_delegate globalObject];
+ _debugger = new WebCoreScriptDebuggerImp(self, [_globalObj _rootObject]->globalObject());
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [_current release];
+ delete _debugger;
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ delete _debugger;
+ [super finalize];
+}
+
+- (id<WebScriptDebugger>)delegate
+{
+ return _delegate;
+}
+
+@end
+
+@implementation WebCoreScriptDebugger (WebCoreScriptDebuggerInternal)
+
+- (WebCoreScriptCallFrame *)_enterFrame:(ExecState *)state;
+{
+ WebCoreScriptCallFrame *callee = [[WebCoreScriptCallFrame alloc] _initWithGlobalObject:_globalObj caller:_current state:state];
+ [callee _setWrapper:[_delegate newWrapperForFrame:callee]];
+ return _current = callee;
+}
+
+- (WebCoreScriptCallFrame *)_leaveFrame;
+{
+ WebCoreScriptCallFrame *caller = [[_current caller] retain];
+ [_current release];
+ return _current = caller;
+}
+
+@end
+
+// WebCoreScriptCallFrame
+//
+// One of these is created to represent each stack frame. Additionally, there is a "global"
+// frame to represent the outermost scope. This global frame is always the last frame in
+// the chain of callers.
+//
+// The delegate can assign a "wrapper" to each frame object so it can relay calls through its
+// own exported interface. This class is private to WebCore (and the delegate).
+
+@implementation WebCoreScriptCallFrame (WebCoreScriptDebuggerInternal)
+
+- (WebCoreScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj caller:(WebCoreScriptCallFrame *)caller state:(ExecState *)state
+{
+ if ((self = [super init])) {
+ _globalObj = globalObj;
+ _caller = caller; // (already retained)
+ _state = state;
+ }
+ return self;
+}
+
+- (void)_setWrapper:(id)wrapper
+{
+ _wrapper = wrapper; // (already retained)
+}
+
+- (id)_convertValueToObjcValue:(JSValue *)value
+{
+ if (!value)
+ return nil;
+
+ if (value == [_globalObj _imp])
+ return _globalObj;
+
+ Bindings::RootObject* root1 = [_globalObj _originRootObject];
+ if (!root1)
+ return nil;
+
+ Bindings::RootObject* root2 = [_globalObj _rootObject];
+ if (!root2)
+ return nil;
+
+ return [WebScriptObject _convertValueToObjcValue:value originRootObject:root1 rootObject:root2];
+}
+
+@end
+
+@implementation WebCoreScriptCallFrame
+
+- (void)dealloc
+{
+ [_wrapper release];
+ [_caller release];
+ [super dealloc];
+}
+
+- (id)wrapper
+{
+ return _wrapper;
+}
+
+- (WebCoreScriptCallFrame *)caller
+{
+ return _caller;
+}
+
+// Returns an array of scope objects (most local first).
+// The properties of each scope object are the variables for that scope.
+// Note that the last entry in the array will _always_ be the global object (windowScriptObject),
+// whose properties are the global variables.
+
+- (NSArray *)scopeChain
+{
+ if (!_state->scopeNode()) { // global frame
+ return [NSArray arrayWithObject:_globalObj];
+ }
+
+ ScopeChain chain = _state->scopeChain();
+ NSMutableArray *scopes = [[NSMutableArray alloc] init];
+
+ while (!chain.isEmpty()) {
+ [scopes addObject:[self _convertValueToObjcValue:chain.top()]];
+ chain.pop();
+ }
+
+ NSArray *result = [NSArray arrayWithArray:scopes];
+ [scopes release];
+ return result;
+}
+
+// Returns the name of the function for this frame, if available.
+// Returns nil for anonymous functions and for the global frame.
+
+- (NSString *)functionName
+{
+ if (_state->scopeNode()) {
+ FunctionImp* func = _state->function();
+ if (func) {
+ Identifier fn = func->functionName();
+ return toNSString(fn.ustring());
+ }
+ }
+ return nil;
+}
+
+// Returns the pending exception for this frame (nil if none).
+
+- (id)exception
+{
+ if (!_state->hadException()) return nil;
+ return [self _convertValueToObjcValue:_state->exception()];
+}
+
+// Evaluate some JavaScript code in the context of this frame.
+// The code is evaluated as if by "eval", and the result is returned.
+// If there is an (uncaught) exception, it is returned as though _it_ were the result.
+// Calling this method on the global frame is not quite the same as calling the WebScriptObject
+// method of the same name, due to the treatment of exceptions.
+
+// FIXME: If "script" contains var declarations, the machinery to handle local variables
+// efficiently in JavaScriptCore will not work properly. This could lead to crashes or
+// incorrect variable values. So this is not appropriate for evaluating arbitrary script.
+- (id)evaluateWebScript:(NSString *)script
+{
+ JSLock lock;
+
+ UString code = String(script);
+
+ ExecState* state = _state;
+ JSGlobalObject* globalObject = state->dynamicGlobalObject();
+
+ // find "eval"
+ JSObject *eval = NULL;
+ if (state->scopeNode()) { // "eval" won't work without context (i.e. at global scope)
+ JSValue *v = globalObject->get(state, "eval");
+ if (v->isObject() && static_cast<JSObject *>(v)->implementsCall())
+ eval = static_cast<JSObject *>(v);
+ else
+ // no "eval" - fallback operates on global exec state
+ state = globalObject->globalExec();
+ }
+
+ JSValue *savedException = state->exception();
+ state->clearException();
+
+ // evaluate
+ JSValue *result;
+ if (eval) {
+ List args;
+ args.append(jsString(code));
+ result = eval->call(state, NULL, args);
+ } else
+ // no "eval", or no context (i.e. global scope) - use global fallback
+ result = Interpreter::evaluate(state, UString(), 0, code.data(), code.size(), globalObject).value();
+
+ if (state->hadException())
+ result = state->exception(); // (may be redundant depending on which eval path was used)
+ state->setException(savedException);
+
+ return [self _convertValueToObjcValue:result];
+}
+
+@end
diff --git a/WebCore/page/mac/WebCoreViewFactory.h b/WebCore/page/mac/WebCoreViewFactory.h
new file mode 100644
index 0000000..4cf21e3
--- /dev/null
+++ b/WebCore/page/mac/WebCoreViewFactory.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2003, 2005 Apple Computer, 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.
+ */
+
+@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;
+
+- (NSString *)inputElementAltText;
+- (NSString *)resetButtonDefaultLabel;
+- (NSString *)searchableIndexIntroduction;
+- (NSString *)submitButtonDefaultLabel;
+- (NSString *)fileButtonChooseFileLabel;
+- (NSString *)fileButtonNoFileSelectedLabel;
+- (NSString *)copyImageUnknownFileLabel;
+
+// Context menu item titles
+- (NSString *)contextMenuItemTagOpenLinkInNewWindow;
+- (NSString *)contextMenuItemTagDownloadLinkToDisk;
+- (NSString *)contextMenuItemTagCopyLinkToClipboard;
+- (NSString *)contextMenuItemTagOpenImageInNewWindow;
+- (NSString *)contextMenuItemTagDownloadImageToDisk;
+- (NSString *)contextMenuItemTagCopyImageToClipboard;
+- (NSString *)contextMenuItemTagOpenFrameInNewWindow;
+- (NSString *)contextMenuItemTagCopy;
+- (NSString *)contextMenuItemTagGoBack;
+- (NSString *)contextMenuItemTagGoForward;
+- (NSString *)contextMenuItemTagStop;
+- (NSString *)contextMenuItemTagReload;
+- (NSString *)contextMenuItemTagCut;
+- (NSString *)contextMenuItemTagPaste;
+- (NSString *)contextMenuItemTagNoGuessesFound;
+- (NSString *)contextMenuItemTagIgnoreSpelling;
+- (NSString *)contextMenuItemTagLearnSpelling;
+- (NSString *)contextMenuItemTagSearchInSpotlight;
+- (NSString *)contextMenuItemTagSearchWeb;
+- (NSString *)contextMenuItemTagLookUpInDictionary;
+- (NSString *)contextMenuItemTagOpenLink;
+- (NSString *)contextMenuItemTagIgnoreGrammar;
+- (NSString *)contextMenuItemTagSpellingMenu;
+- (NSString *)contextMenuItemTagShowSpellingPanel:(bool)show;
+- (NSString *)contextMenuItemTagCheckSpelling;
+- (NSString *)contextMenuItemTagCheckSpellingWhileTyping;
+- (NSString *)contextMenuItemTagCheckGrammarWithSpelling;
+- (NSString *)contextMenuItemTagFontMenu;
+- (NSString *)contextMenuItemTagShowFonts;
+- (NSString *)contextMenuItemTagBold;
+- (NSString *)contextMenuItemTagItalic;
+- (NSString *)contextMenuItemTagUnderline;
+- (NSString *)contextMenuItemTagOutline;
+- (NSString *)contextMenuItemTagStyles;
+- (NSString *)contextMenuItemTagShowColors;
+- (NSString *)contextMenuItemTagSpeechMenu;
+- (NSString *)contextMenuItemTagStartSpeaking;
+- (NSString *)contextMenuItemTagStopSpeaking;
+- (NSString *)contextMenuItemTagWritingDirectionMenu;
+- (NSString *)contextMenuItemTagDefaultDirection;
+- (NSString *)contextMenuItemTagLeftToRight;
+- (NSString *)contextMenuItemTagRightToLeft;
+- (NSString *)contextMenuItemTagInspectElement;
+
+- (NSString *)searchMenuNoRecentSearchesText;
+- (NSString *)searchMenuRecentSearchesText;
+- (NSString *)searchMenuClearRecentSearchesText;
+
+- (NSString *)defaultLanguageCode;
+
+- (NSString *)imageTitleForFilename:(NSString *)filename size:(NSSize)size;
+
+- (BOOL)objectIsTextMarker:(id)object;
+- (BOOL)objectIsTextMarkerRange:(id)object;
+
+- (WebCoreTextMarker *)textMarkerWithBytes:(const void *)bytes length:(size_t)length;
+- (BOOL)getBytes:(void *)bytes fromTextMarker:(WebCoreTextMarker *)textMarker length:(size_t)length;
+
+- (WebCoreTextMarkerRange *)textMarkerRangeWithStart:(WebCoreTextMarker *)start end:(WebCoreTextMarker *)end;
+- (WebCoreTextMarker *)startOfTextMarkerRange:(WebCoreTextMarkerRange *)range;
+- (WebCoreTextMarker *)endOfTextMarkerRange:(WebCoreTextMarkerRange *)range;
+
+- (void)accessibilityHandleFocusChanged;
+
+- (AXUIElementRef)AXUIElementForElement:(id)element;
+- (void)unregisterUniqueIdForUIElement:(id)element;
+
+- (WebCoreFrameBridge *)bridgeForView:(NSView *)aView;
+
+- (NSString *)AXWebAreaText;
+- (NSString *)AXLinkText;
+- (NSString *)AXListMarkerText;
+- (NSString *)AXImageMapText;
+- (NSString *)AXHeadingText;
+
+// FTP Directory Related
+- (NSString *)unknownFileSizeText;
+
+@end
+
+@interface WebCoreViewFactory : NSObject
+{
+}
+
++ (WebCoreViewFactory *)sharedFactory;
+
+@end
+
+@interface WebCoreViewFactory (SubclassResponsibility) <WebCoreViewFactory>
+@end
+
+@protocol WebCorePluginInfo <NSObject>
+- (NSString *)name;
+- (NSString *)filename;
+- (NSString *)pluginDescription;
+- (NSEnumerator *)MIMETypeEnumerator;
+- (NSString *)descriptionForMIMEType:(NSString *)MIMEType;
+- (NSArray *)extensionsForMIMEType:(NSString *)MIMEType;
+@end
+
diff --git a/WebCore/page/mac/WebCoreViewFactory.m b/WebCore/page/mac/WebCoreViewFactory.m
new file mode 100644
index 0000000..5398989
--- /dev/null
+++ b/WebCore/page/mac/WebCoreViewFactory.m
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2003 Apple Computer, 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"
+#import "WebCoreViewFactory.h"
+#import <wtf/Assertions.h>
+
+@implementation WebCoreViewFactory
+
+static WebCoreViewFactory *sharedFactory;
+
++ (WebCoreViewFactory *)sharedFactory
+{
+ return sharedFactory;
+}
+
+- init
+{
+ [super init];
+
+ ASSERT(!sharedFactory);
+ sharedFactory = [self retain];
+
+ return self;
+}
+
+@end
diff --git a/WebCore/page/mac/WebDashboardRegion.h b/WebCore/page/mac/WebDashboardRegion.h
new file mode 100644
index 0000000..81113e9
--- /dev/null
+++ b/WebCore/page/mac/WebDashboardRegion.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004 Apple Computer, 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.
+ */
+
+typedef enum {
+ WebDashboardRegionTypeNone,
+ WebDashboardRegionTypeCircle,
+ WebDashboardRegionTypeRectangle,
+ WebDashboardRegionTypeScrollerRectangle
+} WebDashboardRegionType;
+
+@interface WebDashboardRegion : NSObject <NSCopying>
+{
+ NSRect rect;
+ NSRect clip;
+ WebDashboardRegionType type;
+}
+- initWithRect:(NSRect)rect clip:(NSRect)clip type:(WebDashboardRegionType)type;
+- (NSRect)dashboardRegionClip;
+- (NSRect)dashboardRegionRect;
+- (WebDashboardRegionType)dashboardRegionType;
+@end
diff --git a/WebCore/page/mac/WebDashboardRegion.m b/WebCore/page/mac/WebDashboardRegion.m
new file mode 100644
index 0000000..958e599
--- /dev/null
+++ b/WebCore/page/mac/WebDashboardRegion.m
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2004 Apple Computer, 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"
+#import "WebDashboardRegion.h"
+
+@implementation WebDashboardRegion
+- initWithRect:(NSRect)r clip:(NSRect)c type:(WebDashboardRegionType)t
+{
+ self = [super init];
+ rect = r;
+ clip = c;
+ type = t;
+ return self;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ return [self retain];
+}
+
+- (NSRect)dashboardRegionClip
+{
+ return clip;
+}
+
+- (NSRect)dashboardRegionRect
+{
+ return rect;
+}
+
+- (WebDashboardRegionType)dashboardRegionType
+{
+ return type;
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"rect:%@ clip:%@ type:%s",
+ NSStringFromRect(rect),
+ NSStringFromRect(clip),
+ type == WebDashboardRegionTypeNone ? "None" :
+ (type == WebDashboardRegionTypeCircle ? "Circle" :
+ (type == WebDashboardRegionTypeRectangle ? "Rectangle" :
+ (type == WebDashboardRegionTypeScrollerRectangle ? "ScrollerRectangle" :
+ "Unknown")))];
+}
+
+- (BOOL)isEqual:(id)other
+{
+ return NSEqualRects (rect, [other dashboardRegionRect]) && NSEqualRects (clip, [other dashboardRegionClip]) && type == [other dashboardRegionType];
+}
+
+@end
diff --git a/WebCore/page/qt/DragControllerQt.cpp b/WebCore/page/qt/DragControllerQt.cpp
new file mode 100644
index 0000000..62372d0
--- /dev/null
+++ b/WebCore/page/qt/DragControllerQt.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ *
+ * 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 "DragController.h"
+
+#include "DragData.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "Page.h"
+
+namespace WebCore
+{
+
+// FIXME: These values are straight out of DragControllerMac, so probably have
+// little correlation with Qt standards...
+const int DragController::LinkDragBorderInset = 2;
+const int DragController::MaxOriginalImageArea = 1500 * 1500;
+const int DragController::DragIconRightInset = 7;
+const int DragController::DragIconBottomInset = 3;
+
+const float DragController::DragImageAlpha = 0.75f;
+
+
+bool DragController::isCopyKeyDown()
+{
+ return false;
+}
+
+DragOperation DragController::dragOperation(DragData* dragData)
+{
+ //FIXME: This logic is incomplete
+ if (dragData->containsURL())
+ return DragOperationCopy;
+
+ return DragOperationNone;
+}
+
+const IntSize& DragController::maxDragImageSize()
+{
+ static const IntSize maxDragImageSize(400, 400);
+
+ return maxDragImageSize;
+}
+
+}
diff --git a/WebCore/page/qt/EventHandlerQt.cpp b/WebCore/page/qt/EventHandlerQt.cpp
new file mode 100644
index 0000000..ce16f5b
--- /dev/null
+++ b/WebCore/page/qt/EventHandlerQt.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2006 Zack Rusin <zack@kde.org>
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Trolltech ASA
+ *
+ * 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 "EventHandler.h"
+
+#include "ClipboardQt.h"
+#include "Cursor.h"
+#include "Document.h"
+#include "EventNames.h"
+#include "FloatPoint.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "FrameView.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 "NotImplemented.h"
+
+namespace WebCore {
+
+using namespace EventNames;
+
+static bool isKeyboardOptionTab(KeyboardEvent* event)
+{
+ return event
+ && (event->type() == keydownEvent || event->type() == keypressEvent)
+ && event->altKey()
+ && event->keyIdentifier() == "U+0009";
+}
+
+bool EventHandler::invertSenseOfTabsToLinks(KeyboardEvent* event) const
+{
+ return isKeyboardOptionTab(event);
+}
+
+bool EventHandler::tabsToAllControls(KeyboardEvent* event) const
+{
+ bool handlingOptionTab = isKeyboardOptionTab(event);
+
+ return handlingOptionTab;
+}
+
+void EventHandler::focusDocumentView()
+{
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+ page->focusController()->setFocusedFrame(m_frame);
+}
+
+bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults&)
+{
+ notImplemented();
+ return false;
+}
+
+bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const
+{
+ //Qt has an activation event which is sent independently
+ // of mouse event so this thing will be a snafu to implement
+ // correctly
+ return false;
+}
+
+bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& event, Widget* widget)
+{
+ Q_ASSERT(widget);
+ if (!widget->isFrameView())
+ return false;
+
+ return static_cast<FrameView*>(widget)->frame()->eventHandler()->handleWheelEvent(event);
+}
+
+Clipboard* EventHandler::createDraggingClipboard() const
+{
+ return new ClipboardQt(ClipboardWritable, true);
+}
+
+bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ subframe->eventHandler()->handleMousePressEvent(mev.event());
+ return true;
+}
+
+bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
+{
+ subframe->eventHandler()->handleMouseMoveEvent(mev.event(), hoveredNode);
+ return true;
+}
+
+bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ subframe->eventHandler()->handleMouseReleaseEvent(mev.event());
+ 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
new file mode 100644
index 0000000..7ae1dd9
--- /dev/null
+++ b/WebCore/page/qt/FrameQt.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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:
+ * 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 "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"
+
+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)
+{
+ QWidget* nativeWidget = widget->nativeWidget();
+ if (!nativeWidget)
+ return 0;
+ return KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::QtLanguage,
+ nativeWidget,
+ bindingRootObject());
+}
+
+void Frame::clearPlatformScriptObjects()
+{
+}
+
+DragImageRef Frame::dragImageForSelection()
+{
+ return 0;
+}
+
+void Frame::dashboardRegionsChanged()
+{
+}
+
+}
+// vim: ts=4 sw=4 et
diff --git a/WebCore/page/win/DragControllerWin.cpp b/WebCore/page/win/DragControllerWin.cpp
new file mode 100644
index 0000000..41f3008
--- /dev/null
+++ b/WebCore/page/win/DragControllerWin.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * 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 "DragController.h"
+
+#include "DragData.h"
+#include "windows.h"
+#include "SelectionController.h"
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+const int DragController::LinkDragBorderInset = 2;
+const int DragController::MaxOriginalImageArea = 1500 * 1500;
+const int DragController::DragIconRightInset = 7;
+const int DragController::DragIconBottomInset = 3;
+
+const float DragController::DragImageAlpha = 0.75f;
+
+DragOperation DragController::dragOperation(DragData* dragData)
+{
+ //FIXME: to match the macos behaviour we should return DragOperationNone
+ //if we are a modal window, we are the drag source, or the window is an attached sheet
+ //If this can be determined from within WebCore operationForDrag can be pulled into
+ //WebCore itself
+ ASSERT(dragData);
+ return dragData->containsURL() && !m_didInitiateDrag ? DragOperationCopy : DragOperationNone;
+}
+
+bool DragController::isCopyKeyDown() {
+ return ::GetAsyncKeyState(VK_CONTROL);
+}
+
+const IntSize& DragController::maxDragImageSize()
+{
+ static const IntSize maxDragImageSize(200, 200);
+
+ return maxDragImageSize;
+}
+
+}
diff --git a/WebCore/page/win/EventHandlerWin.cpp b/WebCore/page/win/EventHandlerWin.cpp
new file mode 100644
index 0000000..5e349e3
--- /dev/null
+++ b/WebCore/page/win/EventHandlerWin.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "EventHandler.h"
+
+#include "ClipboardWin.h"
+#include "Cursor.h"
+#include "FloatPoint.h"
+#include "FocusController.h"
+#include "FrameView.h"
+#include "Frame.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "MouseEventWithHitTestResults.h"
+#include "Page.h"
+#include "PlatformScrollbar.h"
+#include "PlatformWheelEvent.h"
+#include "SelectionController.h"
+#include "WCDataObject.h"
+#include "NotImplemented.h"
+
+namespace WebCore {
+
+bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ subframe->eventHandler()->handleMousePressEvent(mev.event());
+ return true;
+}
+
+bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, HitTestResult* hoveredNode)
+{
+ if (m_mouseDownMayStartDrag && !m_mouseDownWasInSubframe)
+ return false;
+ subframe->eventHandler()->handleMouseMoveEvent(mev.event(), hoveredNode);
+ return true;
+}
+
+bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ subframe->eventHandler()->handleMouseReleaseEvent(mev.event());
+ return true;
+}
+
+bool EventHandler::passWheelEventToWidget(PlatformWheelEvent& wheelEvent, Widget* widget)
+{
+ if (!widget->isFrameView())
+ return false;
+
+ 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;
+}
+
+bool EventHandler::eventActivatedView(const PlatformMouseEvent& event) const
+{
+ return event.activatedWebView();
+}
+
+Clipboard* EventHandler::createDraggingClipboard() const
+{
+ COMPtr<WCDataObject> dataObject;
+ WCDataObject::createInstance(&dataObject);
+ return new ClipboardWin(true, dataObject.get(), ClipboardWritable);
+}
+
+void EventHandler::focusDocumentView()
+{
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+ page->focusController()->setFocusedFrame(m_frame);
+}
+
+bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults&)
+{
+ notImplemented();
+ return false;
+}
+
+}
diff --git a/WebCore/page/win/FrameCGWin.cpp b/WebCore/page/win/FrameCGWin.cpp
new file mode 100644
index 0000000..eea6961
--- /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->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->setPaintRestriction(PaintRestrictionNone);
+
+ return hbmp;
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/win/FrameCairoWin.cpp b/WebCore/page/win/FrameCairoWin.cpp
new file mode 100644
index 0000000..a645a10
--- /dev/null
+++ b/WebCore/page/win/FrameCairoWin.cpp
@@ -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 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 "EditorClient.h"
+#include "NotImplemented.h"
+
+using std::min;
+
+namespace WebCore {
+
+HBITMAP imageFromSelection(Frame* frame, bool forceBlackText)
+{
+ notImplemented();
+ return 0;
+}
+
+} // namespace WebCore
diff --git a/WebCore/page/win/FrameWin.cpp b/WebCore/page/win/FrameWin.cpp
new file mode 100644
index 0000000..6599a88
--- /dev/null
+++ b/WebCore/page/win/FrameWin.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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 <winsock2.h>
+#include <windows.h>
+
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "Document.h"
+#include "EditorClient.h"
+#include "FrameLoader.h"
+#include "FrameLoadRequest.h"
+#include "FramePrivate.h"
+#include "FrameView.h"
+#include "HTMLIFrameElement.h"
+#include "HTMLNames.h"
+#include "HTMLTableCellElement.h"
+#include "KeyboardEvent.h"
+#include "NP_jsobject.h"
+#include "NotImplemented.h"
+#include "Page.h"
+#include "Plugin.h"
+#include "PluginDatabase.h"
+#include "PluginView.h"
+#include "RegularExpression.h"
+#include "RenderFrame.h"
+#include "RenderTableCell.h"
+#include "RenderView.h"
+#include "ResourceHandle.h"
+#include "TextResourceDecoder.h"
+#include "kjs_proxy.h"
+#include "kjs_window.h"
+#include "npruntime_impl.h"
+#include "runtime_root.h"
+#include "GraphicsContext.h"
+#include "Settings.h"
+
+using std::min;
+using namespace KJS::Bindings;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+void Frame::clearPlatformScriptObjects()
+{
+}
+
+KJS::Bindings::Instance* Frame::createScriptInstanceForWidget(Widget* widget)
+{
+ // FIXME: Ideally we'd have an isPluginView() here but we can't add that to the open source tree right now.
+ if (widget->isFrameView())
+ return 0;
+
+ return static_cast<PluginView*>(widget)->bindingInstance();
+}
+
+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 (selectionController()->isRange())
+ return imageFromSelection(this, false);
+
+ return 0;
+}
+
+void Frame::dashboardRegionsChanged()
+{
+}
+
+} // 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/GlobalHistoryWin.cpp b/WebCore/page/win/GlobalHistoryWin.cpp
new file mode 100644
index 0000000..c81f09b
--- /dev/null
+++ b/WebCore/page/win/GlobalHistoryWin.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "GlobalHistory.h"
+
+#include "WebCoreHistory.h"
+
+namespace WebCore {
+
+bool historyContains(const UChar* characters, unsigned length)
+{
+ WebCoreHistoryProvider* provider = WebCoreHistory::historyProvider();
+ return provider && provider->containsURL(characters, length);
+}
+
+} // namespace WebCore
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/DragControllerWx.cpp b/WebCore/page/wx/DragControllerWx.cpp
new file mode 100644
index 0000000..659364f
--- /dev/null
+++ b/WebCore/page/wx/DragControllerWx.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ *
+ * 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 "DragController.h"
+
+#include "DragData.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "NotImplemented.h"
+#include "Page.h"
+
+namespace WebCore {
+
+// FIXME: These values are straight out of DragControllerMac, so probably have
+// little correlation with wx standards...
+const int DragController::LinkDragBorderInset = 2;
+const int DragController::MaxOriginalImageArea = 1500 * 1500;
+const int DragController::DragIconRightInset = 7;
+const int DragController::DragIconBottomInset = 3;
+
+const float DragController::DragImageAlpha = 0.75f;
+
+bool DragController::isCopyKeyDown()
+{
+ notImplemented();
+ return false;
+}
+
+DragOperation DragController::dragOperation(DragData* dragData)
+{
+ //FIXME: This logic is incomplete
+ if (dragData->containsURL())
+ return DragOperationCopy;
+
+ return DragOperationNone;
+}
+
+const IntSize& DragController::maxDragImageSize()
+{
+ static const IntSize maxDragImageSize(400, 400);
+
+ return maxDragImageSize;
+}
+
+}
diff --git a/WebCore/page/wx/EventHandlerWx.cpp b/WebCore/page/wx/EventHandlerWx.cpp
new file mode 100644
index 0000000..6670c18
--- /dev/null
+++ b/WebCore/page/wx/EventHandlerWx.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007 Kevin Ollivier <kevino@theolliviers.com>. 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 "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 "RenderWidget.h"
+
+namespace WebCore {
+
+bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ return passSubframeEventToSubframe(mev, subframe);
+}
+
+bool EventHandler::passMouseMoveEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe, WebCore::HitTestResult* hittest)
+{
+ return passSubframeEventToSubframe(mev, subframe);
+}
+
+bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe)
+{
+ 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.
+ if (!event.targetNode() || !event.targetNode()->renderer() || !event.targetNode()->renderer()->isWidget())
+ return false;
+
+ return passMouseDownEventToWidget(static_cast<RenderWidget*>(event.targetNode()->renderer())->widget());
+}
+
+bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget)
+{
+ return passMouseDownEventToWidget(renderWidget->widget());
+}
+
+void EventHandler::focusDocumentView()
+{
+ if (Page* page = m_frame->page())
+ page->focusController()->setFocusedFrame(m_frame);
+}
+
+bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const
+{
+ // wx sends activate events separate from mouse events.
+ // We'll have to test the exact order of events,
+ // but for the moment just return false.
+ return false;
+}
+
+Clipboard* EventHandler::createDraggingClipboard() const
+{
+ return new ClipboardWx(ClipboardWritable, true);
+}
+
+}