+2006-01-15 Timothy Hatcher <timothy@apple.com>
+
+ Reviewed by Darin.
+
+ Support for the new DOM Inspector.
+
+ * khtml/ecma/kjs_dom.cpp: exposes scrollIntoViewIfNeeded to JS
+ * khtml/ecma/kjs_dom.h:
+ * khtml/rendering/RenderText.cpp:
+ (RenderText::lineBoxRects): returns text run rects
+ * khtml/rendering/RenderText.h:
+ * khtml/rendering/render_container.cpp:
+ (RenderContainer::lineBoxRects): returns all child line box rects
+ * khtml/rendering/render_container.h:
+ * khtml/rendering/render_object.cpp:
+ (RenderObject::lineBoxRects): returns an empty list
+ * khtml/rendering/render_object.h:
+ * khtml/xml/dom_elementimpl.cpp:
+ (ElementImpl::scrollIntoViewIfNeeded): scrolls only if needed, bool decides to center to align to nearest edge
+ * khtml/xml/dom_elementimpl.h: Ditto
+ * kwq/DOM.mm:
+ (-[DOMNode boundingBox]): returns the bounding box for a node
+ (-[DOMNode lineBoxRects]): returns text runs for a node
+ * kwq/DOMCore.h: added missing @protocol DOMEventListener and @class DOMEvent
+ * kwq/DOMExtensions.h: removes methods that haven't been API reviewed
+ * kwq/DOMPrivate.h: new pending public API added for the Inspector
+
2006-01-15 Darin Adler <darin@apple.com>
Reviewed by Eric.
getElementsByTagNameNS DOMElement::GetElementsByTagNameNS DontDelete|Function 2
hasAttributeNS DOMElement::HasAttributeNS DontDelete|Function 2
scrollIntoView DOMElement::ScrollIntoView DontDelete|Function 1
+ scrollIntoViewIfNeeded DOMElement::ScrollIntoViewIfNeeded DontDelete|Function 1
focus DOMElement::ElementFocus DontDelete|Function 0
blur DOMElement::ElementBlur DontDelete|Function 0
case DOMElement::ScrollIntoView:
element.scrollIntoView(args[0]->isUndefinedOrNull() || args[0]->toBoolean(exec));
return jsUndefined();
+ case DOMElement::ScrollIntoViewIfNeeded:
+ element.scrollIntoViewIfNeeded(args[0]->isUndefinedOrNull() || args[0]->toBoolean(exec));
+ return jsUndefined();
case DOMElement::ScrollByLines:
case DOMElement::ScrollByPages:
if (DocumentImpl* doc = element.getDocument()) {
OnMouseMove, OnMouseOut, OnMouseOver, OnMouseUp, OnMouseWheel, OnMove, OnReset,
OnResize, OnScroll, OnSearch, OnSelect, OnSubmit, OnUnload,
OffsetLeft, OffsetTop, OffsetWidth, OffsetHeight, OffsetParent,
- ClientWidth, ClientHeight, ScrollLeft, ScrollTop, ScrollWidth, ScrollHeight, ScrollIntoView };
+ ClientWidth, ClientHeight, ScrollLeft, ScrollTop, ScrollWidth, ScrollHeight,
+ ScrollIntoView, ScrollIntoViewIfNeeded };
protected:
// Constructor for inherited classes; doesn't set up a prototype.
box->height()));
}
+QValueList<IntRect> RenderText::lineBoxRects()
+{
+ QValueList<IntRect> rects;
+ int x = 0, y = 0;
+ absolutePosition(x, y);
+ absoluteRects(rects, x, y);
+ return rects;
+}
+
InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos) const
{
// The text runs point to parts of the rendertext's str string
HitTestAction hitTestAction) { assert(false); return false; }
virtual void absoluteRects(QValueList<IntRect>& rects, int _tx, int _ty);
+ virtual QValueList<IntRect> RenderText::lineBoxRects();
virtual VisiblePosition positionForCoordinates(int x, int y);
return VisiblePosition(element(), 0, DOWNSTREAM);
}
-
+
+QValueList<IntRect> RenderContainer::lineBoxRects()
+{
+ if (!firstChild() && (isInline() || isAnonymousBlock())) {
+ QValueList<IntRect> rects;
+ int x = 0, y = 0;
+ absolutePosition(x, y);
+ absoluteRects(rects, x, y);
+ return rects;
+ }
+
+ if (!firstChild())
+ return QValueList<IntRect>();
+
+ QValueList<IntRect> rects;
+ for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isText() || child->isInline() || child->isAnonymousBlock()) {
+ int x = 0, y = 0;
+ child->absolutePosition(x, y);
+ child->absoluteRects(rects, x, y);
+ }
+ }
+
+ return rects;
+}
+
#undef DEBUG_LAYOUT
void updatePseudoChild(RenderStyle::PseudoId type, RenderObject* child);
virtual VisiblePosition positionForCoordinates(int x, int y);
+
+ virtual QValueList<IntRect> lineBoxRects();
protected:
void setFirstChild(RenderObject *first) { m_first = first; }
p->restore(); // Undo the clip.
}
+QValueList<IntRect> RenderObject::lineBoxRects()
+{
+ return QValueList<IntRect>();
+}
+
void RenderObject::absoluteRects(QValueList<IntRect>& rects, int _tx, int _ty)
{
// For blocks inside inlines, we go ahead and include margins so that we run right up to the
absolutePosition(x, y);
QValueList<IntRect> rects;
absoluteRects(rects, x, y);
-
+
+ if (rects.isEmpty())
+ return IntRect(0, 0, 0, 0);
+
QValueList<IntRect>::ConstIterator it = rects.begin();
IntRect result = *it;
while (++it != rects.end()) {
virtual int borderLeft() const { return style()->borderLeftWidth(); }
virtual int borderRight() const { return style()->borderRightWidth(); }
+ virtual QValueList<IntRect> lineBoxRects();
+
virtual void absoluteRects(QValueList<IntRect>& rects, int _tx, int _ty);
IntRect absoluteBoundingBoxRect();
}
}
+void ElementImpl::scrollIntoViewIfNeeded(bool centerIfNeeded)
+{
+ IntRect bounds = this->getRect();
+ if (m_render) {
+ if (centerIfNeeded)
+ m_render->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignCenterIfNeeded, RenderLayer::gAlignCenterIfNeeded);
+ else
+ m_render->enclosingLayer()->scrollRectToVisible(bounds, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignToEdgeIfNeeded);
+ }
+}
+
const AtomicString& ElementImpl::getAttributeNS(const DOMString &namespaceURI,
const DOMString &localName) const
{
void setAttributeNS(const DOMString &namespaceURI, const DOMString &qualifiedName, const DOMString &value, int &exception);
void scrollIntoView (bool alignToTop);
-
+ void scrollIntoViewIfNeeded(bool centerIfNeeded);
+
void removeAttribute(const DOMString &name, int &exception) { removeAttributeNS(DOMString(), name, exception); }
void removeAttributeNS(const DOMString &namespaceURI, const DOMString &localName, int &exception);
return result;
}
+- (NSRect)boundingBox
+{
+ khtml::RenderObject *renderer = [self _nodeImpl]->renderer();
+ if (renderer)
+ return renderer->absoluteBoundingBoxRect();
+ return NSZeroRect;
+}
+
+- (NSArray *)lineBoxRects
+{
+ khtml::RenderObject *renderer = [self _nodeImpl]->renderer();
+ if (renderer) {
+ NSMutableArray *results = [[NSMutableArray alloc] init];
+ QValueList<IntRect> rects = renderer->lineBoxRects();
+ if (!rects.isEmpty()) {
+ for (QValueList<IntRect>::ConstIterator it = rects.begin(); it != rects.end(); ++it)
+ [results addObject:[NSValue valueWithRect:*it]];
+ }
+ return [results autorelease];
+ }
+ return nil;
+}
+
@end
@implementation DOMNode (WebCoreInternal)
[self _elementImpl]->blur();
}
+- (void)scrollIntoView:(BOOL)alignTop
+{
+ [self _elementImpl]->scrollIntoView(alignTop);
+}
+
+- (void)scrollIntoViewIfNeeded:(BOOL)centerIfNeeded
+{
+ [self _elementImpl]->scrollIntoViewIfNeeded(centerIfNeeded);
+}
+
@end
@implementation DOMElement (WebCoreInternal)
@class DOMElement;
@class DOMNamedNodeMap;
@class DOMNodeList;
+@class DOMEvent;
+@protocol DOMEventListener;
@protocol DOMEventTarget;
typedef struct DOMObjectInternal DOMObjectInternal;
- (DOMCSSStyleDeclaration *)createCSSStyleDeclaration;
@end
-@interface DOMElement (DOMElementExtensions)
-- (void)focus;
-- (void)blur;
-@end
-
@interface DOMHTMLElement (DOMHTMLElementExtensions)
- (NSString *)innerHTML;
- (void)setInnerHTML:(NSString *)innerHTML;
- (NSString *)getPropertyShorthand:(NSString *)propertyName;
- (BOOL)isPropertyImplicit:(NSString *)propertyName;
@end
+
+@interface DOMNode (DOMNodePendingPublic)
+- (BOOL)isSameNode:(DOMNode *) other;
+- (BOOL)isEqualNode:(DOMNode *) other;
+
+- (NSRect)boundingBox;
+- (NSArray *)lineBoxRects;
+
+- (NSString *)textContent;
+- (void)setTextContent:(NSString *)text;
+
+- (void)addEventListener:(NSString *)type :(id <DOMEventListener>)listener :(BOOL)useCapture;
+- (void)removeEventListener:(NSString *)type :(id <DOMEventListener>)listener :(BOOL)useCapture;
+- (BOOL)dispatchEvent:(DOMEvent *)event;
+@end
+
+@interface DOMElement (DOMElementExtensions)
+- (void)focus;
+- (void)blur;
+- (void)scrollIntoView:(BOOL)alignTop;
+- (void)scrollIntoViewIfNeeded:(BOOL)centerIfNeeded;
+@end
// END
// Pending DOM3 APIs
+2006-01-15 Timothy Hatcher <timothy@apple.com>
+
+ Reviewed by Darin.
+
+ New DOM Inspector that lives in WebKit and is accessible from any WebView.
+ Accessible from a contextual menu when the WebKitEnableInspectElementContextMenuItem default is
+ true or you have a development build. Browsing the tree, serialized HTML and CSS rules work.
+
+ To always enable enter the following in the Terminal (change the bundle id to affect other WebKit apps):
+ defaults write com.apple.Safari WebKitEnableInspectElementContextMenuItem -bool true
+
+ http://bugzilla.opendarwin.org/show_bug.cgi?id=6571
+
+ * English.lproj/Localizable.strings:
+ * English.lproj/StringsNotToBeLocalized.txt: reorder of the entries
+ * Misc.subproj/WebKitNSStringExtras.h:
+ * Misc.subproj/WebKitNSStringExtras.m:
+ (-[NSString _webkit_stringByCollapsingWhitespaceCharacters]): collapses consecutive whitespace into a single space
+ * WebCoreSupport.subproj/WebFrameBridge.m:
+ (-[WebFrameBridge pluginViewWithPackage:attributeNames:attributeValues:baseURL:]): cleanup
+ (-[WebFrameBridge viewForPluginWithURL:attributeNames:attributeValues:MIMEType:]): call the new UI delegate method
+ * WebInspector.subproj: Added.
+ * WebInspector.subproj/WebInspector.h: Added.
+ * WebInspector.subproj/WebInspector.m: Added.
+ (+[WebInspector sharedWebInspector]):
+ (-[WebInspector init]):
+ (-[WebInspector initWithWebFrame:]):
+ (-[WebInspector dealloc]):
+ (-[WebInspector window]):
+ (-[WebInspector windowDidLoad]):
+ (-[WebInspector windowWillClose:]):
+ (-[WebInspector showWindow:]):
+ (-[WebInspector setWebFrame:]):
+ (-[WebInspector webFrame]):
+ (-[WebInspector setRootDOMNode:]):
+ (-[WebInspector rootDOMNode]):
+ (-[WebInspector setFocusedDOMNode:]):
+ (-[WebInspector focusedDOMNode]):
+ (-[WebInspector setSearchQuery:]):
+ (-[WebInspector searchQuery]):
+ (-[WebInspector searchResults]):
+ (-[WebInspector showOptionsMenu]):
+ (-[WebInspector selectNewRoot:]):
+ (-[WebInspector resizeTopArea:]):
+ (-[WebInspector treeViewScrollTo:]):
+ (-[WebInspector treeViewOffsetTop]):
+ (-[WebInspector treeViewScrollHeight]):
+ (-[WebInspector traverseTreeBackward]):
+ (-[WebInspector traverseTreeForward]):
+ (-[WebInspector _toggleIgnoreWhitespace:]):
+ (-[WebInspector _highlightNode:]):
+ (-[WebInspector _nodeHighlightExpired:]):
+ (-[WebInspector _focusRootNode:]):
+ (-[WebInspector _revealAndSelectNodeInTree:]):
+ (-[WebInspector _showSearchResults:]):
+ (-[WebInspector _refreshSearch]):
+ (-[WebInspector _update]):
+ (-[WebInspector _updateRoot]):
+ (-[WebInspector _updateTreeScrollbar]):
+ (+[WebInspector isSelectorExcludedFromWebScript:]):
+ (+[WebInspector webScriptNameForSelector:]):
+ (+[WebInspector isKeyExcludedFromWebScript:]):
+ (-[WebInspector handleEvent:]):
+ (-[WebInspector webView:didFinishLoadForFrame:]):
+ (-[WebInspector webView:plugInViewWithArguments:]):
+ (-[WebInspector outlineView:numberOfChildrenOfItem:]):
+ (-[WebInspector outlineView:isItemExpandable:]):
+ (-[WebInspector outlineView:child:ofItem:]):
+ (-[WebInspector outlineView:objectValueForTableColumn:byItem:]):
+ (-[WebInspector outlineView:willDisplayOutlineCell:forTableColumn:item:]):
+ (-[WebInspector outlineViewItemDidCollapse:]):
+ (-[WebInspector outlineViewSelectionDidChange:]):
+ (-[WebInspectorPrivate dealloc]):
+ (-[DOMHTMLElement _addClassName:]): Helper method for the Inspector to append style classes
+ (-[DOMHTMLElement _removeClassName:]): Helper method for the Inspector to remove style classes
+ (-[DOMNode _contentPreview]):
+ (-[DOMNode _isAncestorOfNode:]):
+ (-[DOMNode _isDescendantOfNode:]):
+ (-[DOMNode _isWhitespace]):
+ (-[DOMNode _lengthOfChildNodesIgnoringWhitespace]):
+ (-[DOMNode _childNodeAtIndexIgnoringWhitespace:]):
+ (-[DOMNode _nextSiblingSkippingWhitespace]):
+ (-[DOMNode _previousSiblingSkippingWhitespace]):
+ (-[DOMNode _firstChildSkippingWhitespace]):
+ (-[DOMNode _lastChildSkippingWhitespace]):
+ (-[DOMNode _firstAncestorCommonWithNode:]):
+ (-[DOMNode _traverseNextNodeStayingWithin:]):
+ (-[DOMNode _traverseNextNodeSkippingWhitespaceStayingWithin:]):
+ (-[DOMNode _traversePreviousNode]):
+ (-[DOMNode _traversePreviousNodeSkippingWhitespace]):
+ (-[DOMNode _nodeTypeName]):
+ (-[DOMNode _shortDisplayName]):
+ (-[DOMNode _displayName]):
+ * WebInspector.subproj/WebInspectorInternal.h: Added.
+ * WebInspector.subproj/WebInspectorOutlineView.h: Added.
+ * WebInspector.subproj/WebInspectorOutlineView.m: Added.
+ (-[WebInspectorOutlineView isOpaque]):
+ (-[WebInspectorOutlineView _highlightColorForCell:]):
+ (-[WebInspectorOutlineView _highlightRow:clipRect:]):
+ (-[WebInspectorOutlineView drawBackgroundInClipRect:]):
+ * WebInspector.subproj/WebInspectorPanel.h: Added.
+ * WebInspector.subproj/WebInspectorPanel.m: Added.
+ (-[WebInspectorPanel canBecomeKeyWindow]):
+ (-[WebInspectorPanel canBecomeMainWindow]):
+ (-[WebInspectorPanel moveWindow:]):
+ (-[WebInspectorPanel resizeWindow:]):
+ (-[WebInspectorPanel sendEvent:]):
+ * WebInspector.subproj/WebNodeHighlight.h: Added.
+ * WebInspector.subproj/WebNodeHighlight.m: Added.
+ (-[WebNodeHighlight initWithBounds:andRects:forView:]):
+ (-[WebNodeHighlight dealloc]):
+ (-[WebNodeHighlight fractionComplete]):
+ (-[WebNodeHighlight expire]):
+ (-[WebNodeHighlight redraw:]):
+ * WebInspector.subproj/WebNodeHighlightView.h: Added.
+ * WebInspector.subproj/WebNodeHighlightView.m: Added.
+ (-[WebNodeHighlightView roundedRect:withRadius:]):
+ (-[WebNodeHighlightView initWithHighlight:andRects:forView:]):
+ (-[WebNodeHighlightView dealloc]):
+ (-[WebNodeHighlightView isOpaque]):
+ (-[WebNodeHighlightView drawRect:]):
+ * WebInspector.subproj/webInspector: Added.
+ * WebInspector.subproj/webInspector/Images: Added.
+ * WebInspector.subproj/webInspector/Images/close.png: Added.
+ * WebInspector.subproj/webInspector/Images/closePressed.png: Added.
+ * WebInspector.subproj/webInspector/Images/downTriangle.png: Added.
+ * WebInspector.subproj/webInspector/Images/menu.png: Added.
+ * WebInspector.subproj/webInspector/Images/menuPressed.png: Added.
+ * WebInspector.subproj/webInspector/Images/popupFill.png: Added.
+ * WebInspector.subproj/webInspector/Images/popupFillPressed.png: Added.
+ * WebInspector.subproj/webInspector/Images/popupLeft.png: Added.
+ * WebInspector.subproj/webInspector/Images/popupLeftPressed.png: Added.
+ * WebInspector.subproj/webInspector/Images/popupRight.png: Added.
+ * WebInspector.subproj/webInspector/Images/popupRightPressed.png: Added.
+ * WebInspector.subproj/webInspector/Images/rightTriangle.png: Added.
+ * WebInspector.subproj/webInspector/Images/scrollThumbBottom.png: Added.
+ * WebInspector.subproj/webInspector/Images/scrollThumbMiddle.png: Added.
+ * WebInspector.subproj/webInspector/Images/scrollThumbTop.png: Added.
+ * WebInspector.subproj/webInspector/Images/scrollTrackBottom.png: Added.
+ * WebInspector.subproj/webInspector/Images/scrollTrackMiddle.png: Added.
+ * WebInspector.subproj/webInspector/Images/scrollTrackTop.png: Added.
+ * WebInspector.subproj/webInspector/Images/squareButtonRight.png: Added.
+ * WebInspector.subproj/webInspector/Images/squareButtonRightPressed.png: Added.
+ * WebInspector.subproj/webInspector/Images/upTriangle.png: Added.
+ * WebInspector.subproj/webInspector/inspector.css: Added.
+ * WebInspector.subproj/webInspector/inspector.html: Added.
+ * WebInspector.subproj/webInspector/inspector.js: Added.
+ * WebKit.xcodeproj/project.pbxproj: Adds Web Inspector files
+ * WebView.subproj/WebUIDelegatePrivate.h: new UI delegate method to supply a replacement view for plugins
+ * WebView.subproj/WebView.m:
+ (-[WebView _menuForElement:defaultItems:]): Add a new context menu item for inspecting
+ (-[WebView _inspectElement:]): Context menu item target for inspecting
+
2006-01-14 Alexey Proskuryakov <ap@nypop.com>
Reviewed by Darin.
Plugins.subproj/WebNetscapePluginPackage.m:"RealPlayer Plugin"
Plugins.subproj/WebNetscapePluginPackage.m:"main"
Plugins.subproj/WebPluginDocumentView.m:"type"
+WebCoreSupport.subproj/WebCookieAdapter.m:"Cookie"
WebCoreSupport.subproj/WebFrameBridge.m:"height"
WebCoreSupport.subproj/WebFrameBridge.m:"width"
-WebCoreSupport.subproj/WebCookieAdapter.m:"Cookie"
WebCoreSupport.subproj/WebImageData.m:"Gray"
WebCoreSupport.subproj/WebImageData.m:"RGB"
WebCoreSupport.subproj/WebImageRendererFactory.m:"clear"
- (NSString *)_webkit_stringByTrimmingWhitespace;
- (NSString *)_webkit_stringByCollapsingNonPrintingCharacters;
+- (NSString *)_webkit_stringByCollapsingWhitespaceCharacters;
- (NSString *)_webkit_fixedCarbonPOSIXPath;
@end
return result;
}
+- (NSString *)_webkit_stringByCollapsingWhitespaceCharacters
+{
+ NSMutableString *result = [[NSMutableString alloc] initWithCapacity:[self length]];
+ NSCharacterSet *spaces = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ static NSCharacterSet *notSpaces = nil;
+
+ if (notSpaces == nil)
+ notSpaces = [[spaces invertedSet] retain];
+
+ unsigned length = [self length];
+ unsigned position = 0;
+ while (position != length) {
+ NSRange nonSpace = [self rangeOfCharacterFromSet:notSpaces options:0 range:NSMakeRange(position, length - position)];
+ if (nonSpace.location == NSNotFound)
+ break;
+
+ NSRange space = [self rangeOfCharacterFromSet:spaces options:0 range:NSMakeRange(nonSpace.location, length - nonSpace.location)];
+ if (space.location == NSNotFound)
+ space.location = length;
+
+ if (space.location > nonSpace.location) {
+ if (position != 0)
+ [result appendString:@" "];
+ [result appendString:[self substringWithRange:NSMakeRange(nonSpace.location, space.location - nonSpace.location)]];
+ }
+
+ position = space.location;
+ }
+
+ return [result autorelease];
+}
+
-(NSString *)_webkit_fixedCarbonPOSIXPath
{
NSFileManager *fileManager = [NSFileManager defaultManager];
WebPluginController *pluginController = [docView _pluginController];
// Store attributes in a dictionary so they can be passed to WebPlugins.
- NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
- unsigned count = [attributeNames count];
- unsigned i;
- for (i = 0; i < count; i++) {
- [attributes setObject:[attributeValues objectAtIndex:i] forKey:[attributeNames objectAtIndex:i]];
- }
+ NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithObjects:attributeValues forKeys:attributeNames];
[pluginPackage load];
Class viewFactory = [pluginPackage viewFactory];
nil];
LOG(Plugins, "arguments:\n%@", arguments);
}
-
+
view = [WebPluginController plugInViewWithArguments:arguments fromPluginPackage:pluginPackage];
[attributes release];
WebBasePluginPackage *pluginPackage = nil;
NSView *view = nil;
int errorCode = 0;
-
+
+ WebView *wv = [_frame webView];
+ id wd = [wv UIDelegate];
+
+ if ([wd respondsToSelector:@selector(webView:plugInViewWithArguments:)]) {
+ NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithObjects:attributeValues forKeys:attributeNames];
+ NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:
+ attributes, WebPlugInAttributesKey,
+ [NSNumber numberWithInt:WebPlugInModeEmbed], WebPlugInModeKey,
+ URL, WebPlugInBaseURLKey, // URL might be nil, so add it last
+ nil];
+ [attributes release];
+ view = [wd webView:wv plugInViewWithArguments:arguments];
+ if (view)
+ return view;
+ }
+
if ([MIMEType length] != 0) {
pluginPackage = [[WebPluginDatabase installedPlugins] pluginForMIMEType:MIMEType];
} else {
--- /dev/null
+/*
+ * 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.
+ * 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 <AppKit/NSWindowController.h>
+
+@class WebFrame;
+@class WebInspectorPrivate;
+@class DOMNode;
+
+@interface WebInspector : NSWindowController
+{
+@private
+ WebInspectorPrivate *_private;
+}
++ (WebInspector *)sharedWebInspector;
+
+- (id)initWithWebFrame:(WebFrame *)webFrame;
+
+- (void)setWebFrame:(WebFrame *)webFrame;
+- (WebFrame *)webFrame;
+
+- (void)setRootDOMNode:(DOMNode *)node;
+- (DOMNode *)rootDOMNode;
+
+- (void)setFocusedDOMNode:(DOMNode *)node;
+- (DOMNode *)focusedDOMNode;
+
+- (void)setSearchQuery:(NSString *)query;
+- (NSString *)searchQuery;
+
+- (NSArray *)searchResults;
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 <WebKit/WebView.h>
+#import <WebKit/WebViewPrivate.h>
+#import <WebKit/WebHTMLView.h>
+#import <WebKit/WebFrame.h>
+#import <WebKit/DOMCore.h>
+#import <WebKit/DOMEvents.h>
+#import <WebKit/DOMHTML.h>
+#import <WebKit/DOMCSS.h>
+#import <WebKit/DOMTraversal.h>
+#import <WebKit/DOMExtensions.h>
+#import <WebKit/DOMPrivate.h>
+#import <WebKit/WebInspector.h>
+#import <WebKit/WebInspectorInternal.h>
+#import <WebKit/WebLocalizableStrings.h>
+
+#import "WebKitNSStringExtras.h"
+#import "WebNodeHighlight.h"
+#import "WebInspectorPanel.h"
+#import "WebInspectorOutlineView.h"
+
+static NSMapTable *lengthIgnoringWhitespaceCache = NULL;
+static NSMapTable *lastChildIndexIgnoringWhitespaceCache = NULL;
+static NSMapTable *lastChildIgnoringWhitespaceCache = NULL;
+
+@implementation WebInspector
++ (WebInspector *)sharedWebInspector
+{
+ static WebInspector *_sharedWebInspector = nil;
+ if (!_sharedWebInspector) {
+ _sharedWebInspector = [[self alloc] initWithWebFrame:nil];
+ _sharedWebInspector->_private->isSharedInspector = YES;
+ }
+ return _sharedWebInspector;
+}
+
+#pragma mark -
+
+- (id)init
+{
+ if (![super initWithWindow:nil])
+ return nil;
+
+ [self setWindowFrameAutosaveName:@"Web Inspector"];
+
+ _private = [[WebInspectorPrivate alloc] init];
+ _private->ignoreWhitespace = YES;
+
+ return self;
+}
+
+- (id)initWithWebFrame:(WebFrame *)webFrame
+{
+ if (![self init])
+ return nil;
+ [self setWebFrame:webFrame];
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"WebNodeHighlightExpired" object:nil];
+ [_private release];
+ [super dealloc];
+}
+
+#pragma mark -
+
+- (NSWindow *)window
+{
+ NSWindow *window = [super window];
+ if (!window) {
+ NSPanel *window = [[WebInspectorPanel alloc] initWithContentRect:NSMakeRect(60.0, 200.0, 350.0, 550.0) styleMask:(NSBorderlessWindowMask | NSUtilityWindowMask) backing:NSBackingStoreBuffered defer:YES];
+ [window setBackgroundColor:[NSColor clearColor]];
+ [window setOpaque:NO];
+ [window setHasShadow:YES];
+ [window _setContentHasShadow:NO];
+ [window setWorksWhenModal:YES];
+ [window setAcceptsMouseMovedEvents:YES];
+ [window setIgnoresMouseEvents:NO];
+ [window setFloatingPanel:YES];
+ [window setReleasedWhenClosed:YES];
+ [window setMovableByWindowBackground:YES];
+ [window setDelegate:self];
+ [window setMinSize:NSMakeSize(280.0, 450.0)];
+ [window setMaxSize:NSMakeSize(600.0, 700.0)];
+
+ _private->webView = [[WebView alloc] initWithFrame:[[window contentView] frame] frameName:nil groupName:nil];
+ [_private->webView setFrameLoadDelegate:self];
+ [_private->webView setUIDelegate:self];
+ [_private->webView setDrawsBackground:NO];
+ [_private->webView _setDashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows to:YES];
+ [_private->webView _setDashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse to:YES];
+ [[_private->webView windowScriptObject] setValue:self forKey:@"Inspector"];
+
+ [window setContentView:_private->webView];
+
+ NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"inspector" ofType:@"html" inDirectory:@"webInspector"];
+ [[_private->webView mainFrame] loadRequest:[[[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path]] autorelease]];
+
+ [self setWindow:window];
+ [self windowDidLoad];
+ return window;
+ }
+
+ return window;
+}
+
+- (void)windowDidLoad
+{
+ if (_private->windowLoaded)
+ return;
+ _private->windowLoaded = YES;
+
+ _private->rightArrowImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"rightTriangle" ofType:@"png" inDirectory:@"webInspector/Images"]];
+ _private->downArrowImage = [[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForResource:@"downTriangle" ofType:@"png" inDirectory:@"webInspector/Images"]];
+
+ if ([_private->searchQuery length]) {
+ [self _showSearchResults:YES];
+ [self _refreshSearch];
+ }
+
+ [self performSelector:@selector(_highlightNode:) withObject:_private->focusedNode afterDelay:0.0];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_nodeHighlightExpired:) name:@"WebNodeHighlightExpired" object:nil];
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ if (!_private->isSharedInspector)
+ [self release];
+}
+
+- (IBAction)showWindow:(id)sender
+{
+ [self _update];
+ [super showWindow:sender];
+}
+
+#pragma mark -
+
+- (void)setWebFrame:(WebFrame *)webFrame
+{
+ if ([webFrame isEqual:_private->webFrame])
+ return;
+
+ [webFrame retain];
+ [_private->webFrame release];
+ _private->webFrame = webFrame;
+
+ [self setFocusedDOMNode:[webFrame DOMDocument]];
+}
+
+- (WebFrame *)webFrame
+{
+ return _private->webFrame;
+}
+
+#pragma mark -
+
+- (void)setRootDOMNode:(DOMNode *)node
+{
+ if (![node hasChildNodes] || [node isSameNode:_private->rootNode])
+ return;
+
+ [node retain];
+ [_private->rootNode release];
+ _private->rootNode = node;
+
+ [self _updateRoot];
+}
+
+- (DOMNode *)rootDOMNode
+{
+ return _private->rootNode;
+}
+
+#pragma mark -
+
+- (void)setFocusedDOMNode:(DOMNode *)node
+{
+ if ([node isSameNode:_private->focusedNode])
+ return;
+
+ [[NSRunLoop currentRunLoop] cancelPerformSelector:@selector(_highlightNode:) target:self argument:_private->focusedNode];
+
+ [node retain];
+ [_private->focusedNode release];
+ _private->focusedNode = node;
+
+ DOMNode *root = _private->rootNode;
+ if (!root || (![root isSameNode:node] && ![root _isAncestorOfNode:node]))
+ [self setRootDOMNode:node];
+
+ if (!_private->webViewLoaded)
+ return;
+
+ [self _revealAndSelectNodeInTree:node];
+ [self _update];
+
+ if (!_private->preventHighlight) {
+ NSRect bounds = [node boundingBox];
+ if (!NSIsEmptyRect(bounds)) {
+ NSRect visible = [[[_private->webFrame frameView] documentView] visibleRect];
+ BOOL needsScroll = !NSContainsRect(visible, bounds) && !NSContainsRect(bounds, visible);
+
+ // only scroll if the bounds isn't in the visible rect and dosen't contain the visible rect
+ if (needsScroll) {
+ // scroll to the parent element if we arn't focued on an element
+ DOMElement *element = (DOMElement *)_private->focusedNode;
+ if (![element isKindOfClass:[DOMElement class]])
+ element = (DOMElement *)[element parentNode];
+
+ if ([element isKindOfClass:[DOMElement class]])
+ [element scrollIntoViewIfNeeded:YES];
+
+ // give time for the scroll to happen
+ [self performSelector:@selector(_highlightNode:) withObject:node afterDelay:0.25];
+ } else
+ [self _highlightNode:node];
+ } else
+ [_private->currentHighlight expire];
+ }
+}
+
+- (DOMNode *)focusedDOMNode
+{
+ return _private->focusedNode;
+}
+
+#pragma mark -
+
+- (void)setSearchQuery:(NSString *)query
+{
+ if ([query isEqualToString:_private->searchQuery])
+ return;
+
+ [query retain];
+ [_private->searchQuery release];
+ _private->searchQuery = query;
+
+ if (_private->webViewLoaded) {
+ DOMHTMLInputElement *search = (DOMHTMLInputElement *)[_private->domDocument getElementById:@"search"];
+ [search setValue:(query ? query : @"")];
+ [self _showSearchResults:([query length] ? YES : NO)];
+ [self _refreshSearch];
+ }
+}
+
+- (NSString *)searchQuery
+{
+ return _private->searchQuery;
+}
+
+#pragma mark -
+
+- (NSArray *)searchResults
+{
+ return [NSArray arrayWithArray:_private->searchResults];
+}
+@end
+
+#pragma mark -
+
+@implementation WebInspector (WebInspectorScripting)
+- (void)showOptionsMenu
+{
+ NSMenu *menu = [[NSMenu alloc] init];
+ [menu setAutoenablesItems:NO];
+
+ NSMenuItem *item = [[[NSMenuItem alloc] init] autorelease];
+ [item setTitle:@"Ignore Whitespace"];
+ [item setTarget:self];
+ [item setAction:@selector(_toggleIgnoreWhitespace:)];
+ [item setState:_private->ignoreWhitespace];
+ [menu addItem:item];
+
+ [NSMenu popUpContextMenu:menu withEvent:[[self window] currentEvent] forView:_private->webView];
+ [menu release];
+
+ // hack to force a layout and balance mouse events
+ NSEvent *currentEvent = [[self window] currentEvent];
+ NSEvent *event = [NSEvent mouseEventWithType:NSLeftMouseUp location:[currentEvent locationInWindow] modifierFlags:[currentEvent modifierFlags] timestamp:[currentEvent timestamp] windowNumber:[[currentEvent window] windowNumber] context:[currentEvent context] eventNumber:[currentEvent eventNumber] clickCount:[currentEvent clickCount] pressure:[currentEvent pressure]];
+ [[[[_private->webView mainFrame] frameView] documentView] mouseUp:event];
+}
+
+- (void)selectNewRoot:(DOMHTMLSelectElement *)popup
+{
+ unsigned index = [popup selectedIndex];
+ unsigned count = 0;
+
+ DOMNode *currentNode = _private->rootNode;
+ while (currentNode) {
+ if (count == index) {
+ [self setRootDOMNode:currentNode];
+ break;
+ }
+ count++;
+ currentNode = [currentNode parentNode];
+ }
+}
+
+- (void)resizeTopArea:(DOMEvent *)domEvent
+{
+ NSWindow *window = [self window];
+ NSEvent *event = [window currentEvent];
+ NSPoint lastLocation = [window convertBaseToScreen:[event locationInWindow]];
+ NSRect lastFrame = [window frame];
+ NSSize minSize = [window minSize];
+ NSSize maxSize = [window maxSize];
+ BOOL mouseUpOccurred = NO;
+
+ DOMHTMLElement *topArea = (DOMHTMLElement *)[_private->domDocument getElementById:@"top"];
+ DOMHTMLElement *splitter = (DOMHTMLElement *)[_private->domDocument getElementById:@"splitter"];
+ DOMHTMLElement *bottomArea = (DOMHTMLElement *)[_private->domDocument getElementById:@"bottom"];
+
+ while (!mouseUpOccurred) {
+ // set mouseUp flag here, but process location of event before exiting from loop, leave mouseUp in queue
+ event = [window nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask) untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:YES];
+
+ if ([event type] == NSLeftMouseUp)
+ mouseUpOccurred = YES;
+
+ NSPoint newLocation = [window convertBaseToScreen:[event locationInWindow]];
+ if (NSEqualPoints(newLocation, lastLocation))
+ continue;
+
+ NSRect proposedRect = lastFrame;
+ long delta = newLocation.y - lastLocation.y;
+ proposedRect.size.height -= delta;
+ proposedRect.origin.y += delta;
+
+ if (proposedRect.size.height < minSize.height) {
+ proposedRect.origin.y -= minSize.height - proposedRect.size.height;
+ proposedRect.size.height = minSize.height;
+ } else if (proposedRect.size.height > maxSize.height) {
+ proposedRect.origin.y -= maxSize.height - proposedRect.size.height;
+ proposedRect.size.height = maxSize.height;
+ }
+
+ NSNumber *baseValue = [topArea valueForKey:@"offsetHeight"];
+ NSString *newValue = [NSString stringWithFormat:@"%dpx", [baseValue unsignedLongValue] - delta];
+ [[topArea style] setProperty:@"height" :newValue :@""];
+
+ baseValue = [splitter valueForKey:@"offsetTop"];
+ newValue = [NSString stringWithFormat:@"%dpx", [baseValue unsignedLongValue] - delta];
+ [[splitter style] setProperty:@"top" :newValue :@""];
+
+ baseValue = [bottomArea valueForKey:@"offsetTop"];
+ newValue = [NSString stringWithFormat:@"%dpx", [baseValue unsignedLongValue] - delta];
+ [[bottomArea style] setProperty:@"top" :newValue :@""];
+
+ [window setFrame:proposedRect display:YES];
+ lastLocation = newLocation;
+ lastFrame = proposedRect;
+
+ [self _updateTreeScrollbar];
+ }
+
+ // post the mouse up event back to the queue so the WebView can get it
+ [window postEvent:event atStart:YES];
+}
+
+- (void)treeViewScrollTo:(float)number
+{
+ float bottom = NSHeight([_private->treeOutlineView frame]) - NSHeight([_private->treeScrollView documentVisibleRect]);
+ number = MAX(0.0, MIN(bottom, number));
+ [[_private->treeScrollView contentView] scrollToPoint:NSMakePoint(0.0, number)];
+}
+
+- (float)treeViewOffsetTop
+{
+ return NSMinY([_private->treeScrollView documentVisibleRect]);
+}
+
+- (float)treeViewScrollHeight
+{
+ return NSHeight([_private->treeOutlineView frame]);
+}
+
+- (void)traverseTreeBackward
+{
+ if (_private->searchResultsVisible) {
+ // if we have a search showing, we will only walk up and down the results
+ int row = [_private->treeOutlineView selectedRow];
+ if (row == -1)
+ return;
+ if ((row - 1) >= 0) {
+ row--;
+ [_private->treeOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
+ [_private->treeOutlineView scrollRowToVisible:row];
+ }
+ return;
+ }
+
+ DOMNode *node = nil;
+ // traverse backward, holding opton will traverse only to the previous sibling
+ if ([[[self window] currentEvent] modifierFlags] & NSAlternateKeyMask) {
+ if (!_private->ignoreWhitespace)
+ node = [_private->focusedNode previousSibling];
+ else
+ node = [_private->focusedNode _previousSiblingSkippingWhitespace];
+ } else {
+ if (!_private->ignoreWhitespace)
+ node = [_private->focusedNode _traversePreviousNode];
+ else
+ node = [_private->focusedNode _traversePreviousNodeSkippingWhitespace];
+ }
+
+ if (node) {
+ DOMNode *root = _private->rootNode;
+ if (![root isSameNode:node] && ![root _isAncestorOfNode:node])
+ [self setRootDOMNode:[_private->focusedNode _firstAncestorCommonWithNode:node]];
+ [self setFocusedDOMNode:node];
+ }
+}
+
+- (void)traverseTreeForward
+{
+ if (_private->searchResultsVisible) {
+ // if we have a search showing, we will only walk up and down the results
+ int row = [_private->treeOutlineView selectedRow];
+ if (row == -1)
+ return;
+ if ((row + 1) < (int)[_private->searchResults count]) {
+ row++;
+ [_private->treeOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
+ [_private->treeOutlineView scrollRowToVisible:row];
+ }
+ return;
+ }
+
+ DOMNode *node = nil;
+ // traverse forward, holding opton will traverse only to the next sibling
+ if ([[[self window] currentEvent] modifierFlags] & NSAlternateKeyMask) {
+ if (!_private->ignoreWhitespace)
+ node = [_private->focusedNode nextSibling];
+ else
+ node = [_private->focusedNode _nextSiblingSkippingWhitespace];
+ } else {
+ if (!_private->ignoreWhitespace)
+ node = [_private->focusedNode _traverseNextNodeStayingWithin:nil];
+ else
+ node = [_private->focusedNode _traverseNextNodeSkippingWhitespaceStayingWithin:nil];
+ }
+
+ if (node) {
+ DOMNode *root = _private->rootNode;
+ if (![root isSameNode:node] && ![root _isAncestorOfNode:node])
+ [self setRootDOMNode:[_private->focusedNode _firstAncestorCommonWithNode:node]];
+ [self setFocusedDOMNode:node];
+ }
+}
+@end
+
+#pragma mark -
+
+@implementation WebInspector (WebInspectorPrivate)
+- (IBAction)_toggleIgnoreWhitespace:(id)sender
+{
+ _private->ignoreWhitespace = !_private->ignoreWhitespace;
+ [_private->treeOutlineView reloadData];
+}
+
+- (void)_highlightNode:(DOMNode *)node
+{
+ if (_private->currentHighlight) {
+ [_private->currentHighlight expire];
+ [_private->currentHighlight release];
+ _private->currentHighlight = nil;
+ }
+
+ NSView *view = [[_private->webFrame frameView] documentView];
+ NSRect bounds = NSIntersectionRect([node boundingBox], [view visibleRect]);
+ if (!NSIsEmptyRect(bounds)) {
+ NSArray *rects = nil;
+ if ([node isKindOfClass:[DOMElement class]]) {
+ DOMCSSStyleDeclaration *style = [_private->domDocument getComputedStyle:(DOMElement *)node :@""];
+ if ([[style getPropertyValue:@"display"] isEqualToString:@"inline"])
+ rects = [node lineBoxRects];
+ } else if ([node isKindOfClass:[DOMText class]])
+ rects = [node lineBoxRects];
+
+ if (![rects count])
+ rects = [NSArray arrayWithObject:[NSValue valueWithRect:bounds]];
+
+ _private->currentHighlight = [[WebNodeHighlight alloc] initWithBounds:bounds andRects:rects forView:view];
+ }
+}
+
+- (void)_nodeHighlightExpired:(NSNotification *)notification
+{
+ if (_private->currentHighlight == [notification object]) {
+ [_private->currentHighlight release];
+ _private->currentHighlight = nil;
+ }
+}
+
+- (void)_focusRootNode:(id)sender
+{
+ id node = nil;
+ if ([sender isKindOfClass:[NSMenuItem class]]) {
+ node = [sender representedObject];
+ } else if(sender == _private->treeOutlineView) {
+ int index = [sender selectedRow];
+ if (index == -1)
+ return;
+ node = [sender itemAtRow:index];
+ }
+
+ if (![node isSameNode:_private->rootNode]) {
+ [self setRootDOMNode:node];
+ [self setSearchQuery:nil]; // exit search mode
+ }
+}
+
+- (void)_revealAndSelectNodeInTree:(DOMNode *)node
+{
+
+ if (!_private->webViewLoaded || !node)
+ return;
+
+ if (!_private->preventRevealOnFocus) {
+ NSMutableArray *ancestors = [[NSMutableArray alloc] init];
+ DOMNode *currentNode = [node parentNode];
+ while (currentNode) {
+ [ancestors addObject:currentNode];
+ if ([currentNode isSameNode:_private->rootNode])
+ break;
+ currentNode = [currentNode parentNode];
+ }
+
+ NSEnumerator *enumerator = [ancestors reverseObjectEnumerator];
+ while ((currentNode = [enumerator nextObject]))
+ [_private->treeOutlineView expandItem:currentNode];
+
+ [ancestors release];
+ }
+
+ int index = [_private->treeOutlineView rowForItem:node];
+ if (index != -1) {
+ [_private->treeOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
+ [_private->treeOutlineView scrollRowToVisible:index];
+ }
+}
+
+- (void)_showSearchResults:(BOOL)show
+{
+ if (show == _private->searchResultsVisible)
+ return;
+
+ DOMHTMLElement *treePopup = (DOMHTMLElement *)[_private->domDocument getElementById:@"treePopup"];
+ DOMHTMLElement *searchField = (DOMHTMLElement *)[_private->domDocument getElementById:@"search"];
+ DOMHTMLElement *searchCount = (DOMHTMLElement *)[_private->domDocument getElementById:@"searchCount"];
+
+ if (show) {
+ [[treePopup style] setDisplay:@"none"];
+ [[searchCount style] setDisplay:@"block"];
+ [[searchField style] setWidth:@"150px"];
+ } else {
+ [[treePopup style] removeProperty:@"display"];
+ [[searchCount style] removeProperty:@"display"];
+ [[searchField style] setWidth:@"90px"];
+ }
+
+ _private->preventRevealOnFocus = show;
+ _private->searchResultsVisible = show;
+}
+
+- (void)_refreshSearch
+{
+ if (!_private->searchResults)
+ _private->searchResults = [[NSMutableArray alloc] initWithCapacity:100];
+ else
+ [_private->searchResults removeAllObjects];
+
+ NSString *query = _private->searchQuery;
+ if (![query length]) {
+ [_private->treeOutlineView reloadData];
+ return;
+ }
+
+ BOOL matched = NO;
+ unsigned count = 0;
+ DOMNode *node = [_private->webFrame DOMDocument];
+ while ((node = [node _traverseNextNodeStayingWithin:nil])) {
+ if ([[node nodeName] _webkit_hasCaseInsensitiveSubstring:query])
+ matched = YES;
+ else if ([node nodeType] == DOM_TEXT_NODE && [[node nodeValue] _webkit_hasCaseInsensitiveSubstring:query])
+ matched = YES;
+ else if ([node isKindOfClass:[DOMHTMLElement class]] && [[(DOMHTMLElement *)node idName] _webkit_hasCaseInsensitiveSubstring:query])
+ matched = YES;
+ else if ([node isKindOfClass:[DOMHTMLElement class]] && [[(DOMHTMLElement *)node className] _webkit_hasCaseInsensitiveSubstring:query])
+ matched = YES;
+ if (matched) {
+ [_private->searchResults addObject:node];
+ count++;
+ matched = NO;
+ }
+ }
+
+ DOMHTMLElement *searchCount = (DOMHTMLElement *)[_private->domDocument getElementById:@"searchCount"];
+ if (count == 1)
+ [searchCount setInnerText:[NSString stringWithFormat:UI_STRING("1 node", "web inspector node search results count with 1 result"), count]];
+ else
+ [searchCount setInnerText:[NSString stringWithFormat:UI_STRING("%u nodes", "web inspector node search results count with zero or more than 1 results"), count]];
+
+ [_private->treeOutlineView reloadData];
+}
+
+- (void)_update
+{
+ if (!_private->webViewLoaded || !_private->focusedNode)
+ return;
+
+ [[_private->webView windowScriptObject] callWebScriptMethod:@"updatePanes" withArguments:nil];
+
+ DOMNode *node = _private->focusedNode;
+ DOMHTMLButtonElement *back = (DOMHTMLButtonElement *)[_private->domDocument getElementById:@"traverseUp"];
+ DOMHTMLButtonElement *forward = (DOMHTMLButtonElement *)[_private->domDocument getElementById:@"traverseDown"];
+ if (_private->searchResultsVisible) {
+ int row = [_private->treeOutlineView selectedRow];
+ if (row != -1) {
+ [forward setDisabled:!((row + 1) < (int)[_private->searchResults count])];
+ [back setDisabled:!((row - 1) >= 0)];
+ }
+ } else {
+ if (!_private->ignoreWhitespace)
+ node = [_private->focusedNode _traverseNextNodeStayingWithin:nil];
+ else
+ node = [_private->focusedNode _traverseNextNodeSkippingWhitespaceStayingWithin:nil];
+ [forward setDisabled:!node];
+
+ if (!_private->ignoreWhitespace)
+ node = [_private->focusedNode _traversePreviousNode];
+ else
+ node = [_private->focusedNode _traversePreviousNodeSkippingWhitespace];
+ [back setDisabled:!node];
+ }
+}
+
+- (void)_updateRoot
+{
+ if (!_private->webViewLoaded || !_private->rootNode || _private->searchResultsVisible)
+ return;
+
+ DOMHTMLElement *titleArea = (DOMHTMLElement *)[_private->domDocument getElementById:@"treePopupTitleArea"];
+ [titleArea setTextContent:[_private->rootNode _displayName]];
+
+ DOMHTMLElement *popup = (DOMHTMLElement *)[_private->domDocument getElementById:@"realTreePopup"];
+ [popup setInnerHTML:@""]; // reset the list
+
+ DOMNode *currentNode = _private->rootNode;
+ while (currentNode) {
+ DOMHTMLOptionElement *option = (DOMHTMLOptionElement *)[_private->domDocument createElement:@"option"];
+ [option setTextContent:[currentNode _displayName]];
+ [popup appendChild:option];
+ currentNode = [currentNode parentNode];
+ }
+
+ _private->preventSelectionRefocus = YES;
+ DOMNode *focusedNode = [_private->focusedNode retain];
+ [_private->treeOutlineView reloadData];
+ [_private->treeOutlineView expandItem:_private->rootNode];
+ [self _revealAndSelectNodeInTree:focusedNode];
+ [focusedNode release];
+ _private->preventSelectionRefocus = NO;
+}
+
+- (void)_updateTreeScrollbar
+{
+ [[_private->webView windowScriptObject] evaluateWebScript:@"treeScrollbar.refresh()"];
+}
+
+#pragma mark -
+
++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
+{
+ return NO;
+}
+
++ (NSString *)webScriptNameForSelector:(SEL)aSelector {
+ NSMutableString *name = [[NSStringFromSelector(aSelector) mutableCopy] autorelease];
+ [name replaceOccurrencesOfString:@":" withString:@"_" options:NSLiteralSearch range:NSMakeRange(0, [name length])];
+ if ([name hasSuffix:@"_"])
+ return [name substringToIndex:[name length] - 1];
+ return name;
+}
+
++ (BOOL)isKeyExcludedFromWebScript:(const char *)name
+{
+ return NO;
+}
+
+#pragma mark -
+
+- (void)handleEvent:(DOMEvent *)event
+{
+ if ([[event type] isEqualToString:@"search"] && [[event target] isKindOfClass:[DOMHTMLInputElement class]]) {
+ DOMHTMLInputElement *search = (DOMHTMLInputElement *)[event target];
+ [self setSearchQuery:[search value]];
+ }
+}
+
+- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
+{
+ _private->webViewLoaded = YES;
+ [[sender windowScriptObject] setValue:self forKey:@"Inspector"];
+
+ [_private->domDocument release];
+ _private->domDocument = (DOMHTMLDocument *)[[[sender mainFrame] DOMDocument] retain];
+
+ [_private->domDocument addEventListener:@"search" :self :NO];
+
+ if ([[self searchQuery] length]) {
+ DOMHTMLInputElement *search = (DOMHTMLInputElement *)[_private->domDocument getElementById:@"search"];
+ [search setValue:[self searchQuery]];
+ [self _showSearchResults:YES];
+ [self _refreshSearch];
+ }
+
+ [self _update];
+ [self _updateRoot];
+ [[self window] invalidateShadow];
+}
+
+- (NSView *)webView:(WebView *)sender plugInViewWithArguments:(NSDictionary *)arguments
+{
+ NSDictionary *attributes = [arguments objectForKey:@"WebPlugInAttributesKey"];
+ if ([[attributes objectForKey:@"type"] isEqualToString:@"application/x-inspector-tree"]) {
+ if (!_private->treeOutlineView) {
+ _private->nodeCache = [[NSMutableSet alloc] init];
+ _private->treeScrollView = [[NSScrollView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 250.0, 100.0)];
+ [_private->treeScrollView setDrawsBackground:NO];
+ [_private->treeScrollView setBorderType:NSNoBorder];
+ [_private->treeScrollView setVerticalScroller:NO];
+ [_private->treeScrollView setHasHorizontalScroller:NO];
+ [_private->treeScrollView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
+ [_private->treeScrollView setFocusRingType:NSFocusRingTypeNone];
+
+ _private->treeOutlineView = [[WebInspectorOutlineView alloc] initWithFrame:[_private->treeScrollView frame]];
+ [_private->treeOutlineView setHeaderView:nil];
+ [_private->treeOutlineView setAllowsMultipleSelection:NO];
+ [_private->treeOutlineView setAllowsEmptySelection:NO];
+ [_private->treeOutlineView setDelegate:self];
+ [_private->treeOutlineView setDataSource:self];
+ [_private->treeOutlineView sendActionOn:(NSLeftMouseUpMask | NSLeftMouseDownMask | NSLeftMouseDraggedMask)];
+ [_private->treeOutlineView setFocusRingType:NSFocusRingTypeNone];
+ [_private->treeOutlineView setAutoresizesOutlineColumn:NO];
+ [_private->treeOutlineView setRowHeight:15.0];
+ [_private->treeOutlineView setTarget:self];
+ [_private->treeOutlineView setDoubleAction:@selector(_focusRootNode:)];
+ [_private->treeOutlineView setIndentationPerLevel:12.0];
+ [_private->treeScrollView setDocumentView:_private->treeOutlineView];
+
+ NSCell *headerCell = [[NSCell alloc] initTextCell:@""];
+ NSCell *dataCell = [[NSCell alloc] initTextCell:@""];
+ [dataCell setFont:[NSFont systemFontOfSize:11.0]];
+
+ NSTableColumn *tableColumn = [[NSTableColumn alloc] initWithIdentifier:@"node"];
+ [tableColumn setHeaderCell:headerCell];
+ [tableColumn setDataCell:dataCell];
+ [tableColumn setMinWidth:50];
+ [tableColumn setWidth:300];
+ [tableColumn setEditable:NO];
+ [_private->treeOutlineView addTableColumn:tableColumn];
+ [_private->treeOutlineView setOutlineTableColumn:tableColumn];
+
+ [_private->treeOutlineView sizeLastColumnToFit];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_updateTreeScrollbar) name:NSViewFrameDidChangeNotification object:_private->treeOutlineView];
+ }
+
+ return [_private->treeOutlineView enclosingScrollView];
+ }
+
+ return nil;
+}
+
+#pragma mark -
+
+- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
+{
+ if (outlineView == _private->treeOutlineView) {
+ if (!item && _private->searchResultsVisible)
+ return [_private->searchResults count];
+ if (!item)
+ return 1;
+ if (!_private->ignoreWhitespace)
+ return [[item childNodes] length];
+ return [item _lengthOfChildNodesIgnoringWhitespace];
+ }
+ return 0;
+}
+
+- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
+{
+ if (outlineView == _private->treeOutlineView) {
+ if (!_private->ignoreWhitespace)
+ return [item hasChildNodes];
+ return ([item _firstChildSkippingWhitespace] ? YES : NO);
+ }
+ return NO;
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
+{
+ if (outlineView == _private->treeOutlineView) {
+ if (!item && _private->searchResultsVisible)
+ return [_private->searchResults objectAtIndex:index];
+ if (!item)
+ return _private->rootNode;
+ id node = nil;
+ if (!_private->ignoreWhitespace)
+ node = [[item childNodes] item:index];
+ else
+ node = [item _childNodeAtIndexIgnoringWhitespace:index];
+ if (!node)
+ return nil;
+ // cache the node because NSOutlineView assumes we hold on to it
+ // if we don't hold on to the node it will be released before the next time the NSOutlineView needs it
+ [_private->nodeCache addObject:node];
+ return node;
+ }
+ return nil;
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
+{
+ if (outlineView == _private->treeOutlineView && item) {
+ NSShadow *shadow = [[NSShadow alloc] init];
+ [shadow setShadowColor:[NSColor blackColor]];
+ [shadow setShadowBlurRadius:2.0];
+ [shadow setShadowOffset:NSMakeSize(2.0,-2.0)];
+ NSMutableParagraphStyle *para = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
+ [para setLineBreakMode:NSLineBreakByTruncatingTail];
+
+ NSDictionary *attrs = [[NSDictionary alloc] initWithObjectsAndKeys:[NSColor whiteColor], NSForegroundColorAttributeName, shadow, NSShadowAttributeName, para, NSParagraphStyleAttributeName, nil];
+ NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:[item _displayName] attributes:attrs];
+ [attrs release];
+
+ if ([item hasChildNodes] && ![outlineView isItemExpanded:item]) {
+ attrs = [[NSDictionary alloc] initWithObjectsAndKeys:[NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:0.5], NSForegroundColorAttributeName, shadow, NSShadowAttributeName, para, NSParagraphStyleAttributeName, nil];
+ NSAttributedString *preview = [[NSAttributedString alloc] initWithString:[item _contentPreview] attributes:attrs];
+ [string appendAttributedString:preview];
+ [attrs release];
+ [preview release];
+ }
+
+ [para release];
+ [shadow release];
+ return [string autorelease];
+ }
+
+ return nil;
+}
+
+- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ if (outlineView == _private->treeOutlineView)
+ [cell setImage:([cell state] ? _private->downArrowImage : _private->rightArrowImage)];
+}
+
+- (void)outlineViewItemDidCollapse:(NSNotification *)notification
+{
+ if ([notification object] == _private->treeOutlineView) {
+ DOMNode *node = [[notification userInfo] objectForKey:@"NSObject"];
+ if (!node)
+ return;
+
+ // remove all child nodes from the node cache when the parent collapses
+ node = [node firstChild];
+ while (node) {
+ NSMapRemove(lengthIgnoringWhitespaceCache, node);
+ NSMapRemove(lastChildIndexIgnoringWhitespaceCache, node);
+ NSMapRemove(lastChildIgnoringWhitespaceCache, node);
+ [_private->nodeCache removeObject:node];
+ node = [node nextSibling];
+ }
+ }
+}
+
+- (void)outlineViewSelectionDidChange:(NSNotification *)notification
+{
+ if ([notification object] == _private->treeOutlineView && !_private->preventSelectionRefocus) {
+ int index = [_private->treeOutlineView selectedRow];
+ if (index == -1)
+ return;
+ [self setFocusedDOMNode:[_private->treeOutlineView itemAtRow:index]];
+ [self _updateTreeScrollbar];
+ }
+}
+@end
+
+#pragma mark -
+
+@implementation WebInspectorPrivate
+- (void)dealloc
+{
+ [webView release];
+ [domDocument release];
+ [webFrame release];
+ [rootNode release];
+ [focusedNode release];
+ [searchQuery release];
+ [nodeCache release];
+ [treeScrollView release];
+ [treeOutlineView release];
+ [matchedRules release];
+ [currentHighlight release];
+ [rightArrowImage release];
+ [downArrowImage release];
+ [super dealloc];
+}
+@end
+
+#pragma mark -
+
+@implementation DOMHTMLElement (DOMHTMLElementInspectorAdditions)
+- (void)_addClassName:(NSString *)name
+{
+ NSString *class = [self className];
+ if ([class rangeOfString:name].location != NSNotFound)
+ return;
+ if ([class length]) {
+ NSString *new = [[NSString alloc] initWithFormat:@"%@ %@", class, name];
+ [self setClassName:new];
+ [new release];
+ } else {
+ [self setClassName:name];
+ }
+}
+
+- (void)_removeClassName:(NSString *)name
+{
+ if ([[self className] isEqualToString:name]) {
+ [self setClassName:@""];
+ } else if ([[self className] rangeOfString:name].location != NSNotFound) {
+ NSMutableString *class = [[self className] mutableCopy];
+ [class replaceOccurrencesOfString:name withString:@"" options:NSLiteralSearch range:NSMakeRange(0, [class length])];
+ [self setClassName:[class _webkit_stringByCollapsingWhitespaceCharacters]];
+ [class release];
+ }
+}
+@end
+
+#pragma mark -
+
+@implementation DOMNode (DOMNodeInspectorAdditions)
+- (NSString *)_contentPreview
+{
+ if (![self hasChildNodes])
+ return @"";
+
+ unsigned limit = 0;
+ NSMutableString *preview = [[NSMutableString alloc] initWithCapacity:100];
+ [preview appendString:@" "];
+
+ // always skip whitespace here
+ DOMNode *currentNode = [self _traverseNextNodeSkippingWhitespaceStayingWithin:self];
+ while (currentNode) {
+ if ([currentNode nodeType] == DOM_TEXT_NODE)
+ [preview appendString:[[currentNode nodeValue] _webkit_stringByCollapsingWhitespaceCharacters]];
+ else
+ [preview appendString:[currentNode _displayName]];
+ currentNode = [currentNode _traverseNextNodeStayingWithin:self];
+ if (++limit > 4) {
+ unichar ellipsis = 0x2026;
+ [preview appendString:[NSString stringWithCharacters:&ellipsis length:1]];
+ break;
+ }
+ }
+
+ return [preview autorelease];
+}
+
+#pragma mark -
+
+- (BOOL)_isAncestorOfNode:(DOMNode *)node
+{
+ DOMNode *currentNode = [node parentNode];
+ while (currentNode) {
+ if ([self isSameNode:currentNode])
+ return YES;
+ currentNode = [currentNode parentNode];
+ }
+ return NO;
+}
+
+- (BOOL)_isDescendantOfNode:(DOMNode *)node
+{
+ return [node _isAncestorOfNode:self];
+}
+
+#pragma mark -
+
+- (BOOL)_isWhitespace
+{
+ static NSCharacterSet *characters = nil;
+ if (!characters)
+ characters = [[[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet] retain];
+ NSRange range = [[self nodeValue] rangeOfCharacterFromSet:characters];
+ return (range.location == NSNotFound);
+}
+
+- (unsigned long)_lengthOfChildNodesIgnoringWhitespace
+{
+ if (!lengthIgnoringWhitespaceCache)
+ lengthIgnoringWhitespaceCache = NSCreateMapTable(NSObjectMapKeyCallBacks, NSIntMapValueCallBacks, 300);
+
+ void *lookup = NSMapGet(lengthIgnoringWhitespaceCache, self);
+ if (lookup)
+ return (unsigned long)lookup;
+
+ unsigned long count = 0;
+ DOMNode *node = [self _firstChildSkippingWhitespace];
+ while (node) {
+ node = [node _nextSiblingSkippingWhitespace];
+ count++;
+ }
+
+ NSMapInsert(lengthIgnoringWhitespaceCache, self, (void *)count);
+ return count;
+}
+
+- (DOMNode *)_childNodeAtIndexIgnoringWhitespace:(unsigned long)nodeIndex
+{
+ unsigned long count = 0;
+ DOMNode *node = nil;
+
+ if (!lastChildIndexIgnoringWhitespaceCache)
+ lastChildIndexIgnoringWhitespaceCache = NSCreateMapTable(NSObjectMapKeyCallBacks, NSIntMapValueCallBacks, 300);
+ if (!lastChildIgnoringWhitespaceCache)
+ lastChildIgnoringWhitespaceCache = NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 300);
+
+ void *cachedLastIndex = NSMapGet(lastChildIndexIgnoringWhitespaceCache, self);
+ if (cachedLastIndex) {
+ DOMNode *lastChild = (DOMNode *)NSMapGet(lastChildIgnoringWhitespaceCache, self);
+ if (lastChild) {
+ unsigned long cachedIndex = (unsigned long)cachedLastIndex;
+ if (nodeIndex == cachedIndex) {
+ return lastChild;
+ } else if (nodeIndex > cachedIndex) {
+ node = lastChild;
+ count = cachedIndex;
+ }
+ }
+ }
+
+ if (!node)
+ node = [self _firstChildSkippingWhitespace];
+
+ while (node && count < nodeIndex) {
+ node = [node _nextSiblingSkippingWhitespace];
+ count++;
+ }
+
+ if (node) {
+ NSMapInsert(lastChildIndexIgnoringWhitespaceCache, self, (void *)count);
+ NSMapInsert(lastChildIgnoringWhitespaceCache, self, node);
+ } else {
+ NSMapRemove(lastChildIndexIgnoringWhitespaceCache, self);
+ NSMapRemove(lastChildIgnoringWhitespaceCache, self);
+ }
+
+ return node;
+}
+
+#pragma mark -
+
+- (DOMNode *)_nextSiblingSkippingWhitespace
+{
+ DOMNode *node = [self nextSibling];
+ while ([node nodeType] == DOM_TEXT_NODE && [node _isWhitespace])
+ node = [node nextSibling];
+ return node;
+}
+
+- (DOMNode *)_previousSiblingSkippingWhitespace
+{
+ DOMNode *node = [self previousSibling];
+ while ([node nodeType] == DOM_TEXT_NODE && [node _isWhitespace])
+ node = [node previousSibling];
+ return node;
+}
+
+- (DOMNode *)_firstChildSkippingWhitespace
+{
+ DOMNode *node = [self firstChild];
+ while ([node nodeType] == DOM_TEXT_NODE && [node _isWhitespace])
+ node = [node _nextSiblingSkippingWhitespace];
+ return node;
+}
+
+- (DOMNode *)_lastChildSkippingWhitespace
+{
+ DOMNode *node = [self lastChild];
+ while ([node nodeType] == DOM_TEXT_NODE && [node _isWhitespace])
+ node = [node _previousSiblingSkippingWhitespace];
+ return node;
+}
+
+#pragma mark -
+
+- (DOMNode *)_firstAncestorCommonWithNode:(DOMNode *)node
+{
+ if ([[self parentNode] isSameNode:[node parentNode]])
+ return [self parentNode];
+
+ NSMutableArray *ancestorsOne = [[[NSMutableArray alloc] init] autorelease];
+ NSMutableArray *ancestorsTwo = [[[NSMutableArray alloc] init] autorelease];
+
+ DOMNode *currentNode = [self parentNode];
+ while (currentNode) {
+ [ancestorsOne addObject:currentNode];
+ currentNode = [currentNode parentNode];
+ }
+
+ currentNode = [node parentNode];
+ while (currentNode) {
+ [ancestorsTwo addObject:currentNode];
+ currentNode = [currentNode parentNode];
+ }
+
+ return [ancestorsOne firstObjectCommonWithArray:ancestorsTwo];
+}
+
+#pragma mark -
+
+- (DOMNode *)_traverseNextNodeStayingWithin:(DOMNode *)stayWithin
+{
+ DOMNode *node = [self firstChild];
+ if (node)
+ return node;
+
+ if ([self isSameNode:stayWithin])
+ return 0;
+
+ node = [self nextSibling];
+ if (node)
+ return node;
+
+ node = self;
+ while (node && ![node nextSibling] && (!stayWithin || ![[node parentNode] isSameNode:stayWithin]))
+ node = [node parentNode];
+
+ return [node nextSibling];
+}
+
+- (DOMNode *)_traverseNextNodeSkippingWhitespaceStayingWithin:(DOMNode *)stayWithin
+{
+ DOMNode *node = [self _firstChildSkippingWhitespace];
+ if (node)
+ return node;
+
+ if ([self isSameNode:stayWithin])
+ return 0;
+
+ node = [self _nextSiblingSkippingWhitespace];
+ if (node)
+ return node;
+
+ node = self;
+ while (node && ![node _nextSiblingSkippingWhitespace] && (!stayWithin || ![[node parentNode] isSameNode:stayWithin]))
+ node = [node parentNode];
+
+ return [node _nextSiblingSkippingWhitespace];
+}
+
+- (DOMNode *)_traversePreviousNode
+{
+ DOMNode *node = [self previousSibling];
+ while ([node lastChild])
+ node = [node lastChild];
+ if (node)
+ return node;
+ return [self parentNode];
+}
+
+- (DOMNode *)_traversePreviousNodeSkippingWhitespace
+{
+ DOMNode *node = [self _previousSiblingSkippingWhitespace];
+ while ([node _lastChildSkippingWhitespace])
+ node = [node _lastChildSkippingWhitespace];
+ if (node)
+ return node;
+ return [self parentNode];
+}
+
+#pragma mark -
+
+- (NSString *)_nodeTypeName
+{
+ switch([self nodeType]) {
+ case DOM_ELEMENT_NODE: return UI_STRING("Element", "element node type name");
+ case DOM_ATTRIBUTE_NODE: return UI_STRING("Attribute", "attribute node type name");
+ case DOM_TEXT_NODE: return UI_STRING("Text", "text node type name");
+ case DOM_CDATA_SECTION_NODE: return UI_STRING("Character Data", "character data node type name");
+ case DOM_ENTITY_REFERENCE_NODE: return UI_STRING("Entity Reference", "entity reference node type name");
+ case DOM_ENTITY_NODE: return UI_STRING("Entity", "entity node type name");
+ case DOM_PROCESSING_INSTRUCTION_NODE: return UI_STRING("Processing Instruction", "processing instruction node type name");
+ case DOM_COMMENT_NODE: return UI_STRING("Comment", "comment node type name");
+ case DOM_DOCUMENT_NODE: return UI_STRING("Document", "document node type name");
+ case DOM_DOCUMENT_TYPE_NODE: return UI_STRING("Document Type", "document type node type name");
+ case DOM_DOCUMENT_FRAGMENT_NODE: return UI_STRING("Document Fragment", "document fragment node type name");
+ case DOM_NOTATION_NODE: return UI_STRING("Notation", "notation node type name");
+ }
+ return nil;
+}
+
+- (NSString *)_shortDisplayName
+{
+ if ([self nodeType] == DOM_TEXT_NODE)
+ return UI_STRING("Text", "text node type name");
+ return [self _displayName];
+}
+
+- (NSString *)_displayName
+{
+ switch([self nodeType]) {
+ case DOM_DOCUMENT_NODE:
+ return UI_STRING("Document", "document node name");
+ case DOM_ELEMENT_NODE: {
+ if ([self hasAttributes]) {
+ NSMutableString *name = [NSMutableString stringWithFormat:@"<%@", [[self nodeName] lowercaseString]];
+ NSString *value = [(DOMElement *)self getAttribute:@"id"];
+ if ([value length])
+ [name appendFormat:@" id=\"%@\"", value];
+ value = [(DOMElement *)self getAttribute:@"class"];
+ if ([value length])
+ [name appendFormat:@" class=\"%@\"", value];
+ if ([[self nodeName] caseInsensitiveCompare:@"a"] == NSOrderedSame) {
+ value = [(DOMElement *)self getAttribute:@"name"];
+ if ([value length])
+ [name appendFormat:@" name=\"%@\"", value];
+ value = [(DOMElement *)self getAttribute:@"href"];
+ if ([value length])
+ [name appendFormat:@" href=\"%@\"", value];
+ } else if ([[self nodeName] caseInsensitiveCompare:@"img"] == NSOrderedSame) {
+ value = [(DOMElement *)self getAttribute:@"src"];
+ if ([value length])
+ [name appendFormat:@" src=\"%@\"", value];
+ } else if ([[self nodeName] caseInsensitiveCompare:@"iframe"] == NSOrderedSame) {
+ value = [(DOMElement *)self getAttribute:@"src"];
+ if ([value length])
+ [name appendFormat:@" src=\"%@\"", value];
+ } else if ([[self nodeName] caseInsensitiveCompare:@"input"] == NSOrderedSame) {
+ value = [(DOMElement *)self getAttribute:@"name"];
+ if ([value length])
+ [name appendFormat:@" name=\"%@\"", value];
+ value = [(DOMElement *)self getAttribute:@"type"];
+ if ([value length])
+ [name appendFormat:@" type=\"%@\"", value];
+ } else if ([[self nodeName] caseInsensitiveCompare:@"form"] == NSOrderedSame) {
+ value = [(DOMElement *)self getAttribute:@"action"];
+ if ([value length])
+ [name appendFormat:@" action=\"%@\"", value];
+ }
+ [name appendString:@">"];
+ return name;
+ }
+ return [NSString stringWithFormat:@"<%@>", [[self nodeName] lowercaseString]];
+ }
+ case DOM_TEXT_NODE: {
+ if ([self _isWhitespace])
+ return UI_STRING("(whitespace)", "whitespace text node name");
+ NSString *value = [[self nodeValue] _webkit_stringByCollapsingWhitespaceCharacters];
+ CFStringTrimWhitespace((CFMutableStringRef)value);
+ return [NSString stringWithFormat:UI_STRING("\"%@\"", "text node name with text included"), value];
+ }
+ }
+ return [[self nodeName] lowercaseString];
+}
+@end
--- /dev/null
+/*
+ * 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.
+ * 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.
+ */
+
+// This header contains WebInspector declarations that can be used anywhere in the Web Kit, but are neither SPI nor API.
+
+#import <Foundation/NSObject.h>
+#import <WebKit/WebInspector.h>
+#import <WebKit/DOMCore.h>
+
+@class WebFrame;
+@class NSView;
+@class NSPopUpButton;
+@class NSSegmentedControl;
+@class NSSearchField;
+@class NSTabView;
+@class NSTextField;
+@class NSTableView;
+@class NSOutlineView;
+@class NSTextView;
+@class NSWindow;
+@class NSNotification;
+@class WebNodeHighlight;
+@class DOMCSSRuleList;
+
+@interface WebInspectorPrivate : NSObject
+{
+@public
+ WebView *webView;
+ DOMHTMLDocument *domDocument;
+ WebFrame *webFrame;
+ DOMNode *rootNode;
+ DOMNode *focusedNode;
+ NSString *searchQuery;
+ NSMutableSet *nodeCache;
+ NSMutableArray *searchResults;
+ NSScrollView *treeScrollView;
+ NSOutlineView *treeOutlineView;
+ DOMCSSRuleList *matchedRules;
+ WebNodeHighlight *currentHighlight;
+ NSImage *rightArrowImage;
+ NSImage *downArrowImage;
+ BOOL ignoreWhitespace;
+ BOOL windowLoaded;
+ BOOL webViewLoaded;
+ BOOL searchResultsVisible;
+ BOOL preventHighlight;
+ BOOL preventRevealOnFocus;
+ BOOL preventSelectionRefocus;
+ BOOL isSharedInspector;
+}
+@end
+
+@interface WebInspector (WebInspectorPrivate) <DOMEventListener>
+- (void)_highlightNode:(DOMNode *)node;
+- (void)_revealAndSelectNodeInTree:(DOMNode *)node;
+- (void)_update;
+- (void)_updateRoot;
+- (void)_updateTreeScrollbar;
+- (void)_showSearchResults:(BOOL)show;
+- (void)_refreshSearch;
+@end
+
+@interface DOMHTMLElement (DOMHTMLElementInspectorAdditions)
+- (void)_addClassName:(NSString *)name;
+- (void)_removeClassName:(NSString *)name;
+@end
+
+@interface DOMNode (DOMNodeInspectorAdditions)
+- (NSString *)_contentPreview;
+
+- (BOOL)_isAncestorOfNode:(DOMNode *)node;
+- (BOOL)_isDescendantOfNode:(DOMNode *)node;
+
+- (BOOL)_isWhitespace;
+
+- (unsigned long)_lengthOfChildNodesIgnoringWhitespace;
+- (DOMNode *)_childNodeAtIndexIgnoringWhitespace:(unsigned long)nodeIndex;
+
+- (DOMNode *)_nextSiblingSkippingWhitespace;
+- (DOMNode *)_previousSiblingSkippingWhitespace;
+
+- (DOMNode *)_firstChildSkippingWhitespace;
+- (DOMNode *)_lastChildSkippingWhitespace;
+
+- (DOMNode *)_firstAncestorCommonWithNode:(DOMNode *)node;
+
+- (DOMNode *)_traverseNextNodeStayingWithin:(DOMNode *)stayWithin;
+- (DOMNode *)_traverseNextNodeSkippingWhitespaceStayingWithin:(DOMNode *)stayWithin;
+- (DOMNode *)_traversePreviousNode;
+- (DOMNode *)_traversePreviousNodeSkippingWhitespace;
+
+- (NSString *)_nodeTypeName;
+- (NSString *)_shortDisplayName;
+- (NSString *)_displayName;
+@end
+
+@interface NSWindow (NSWindowContentShadow)
+- (void)_setContentHasShadow:(BOOL)hasShadow;
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 <AppKit/NSOutlineView.h>
+
+@interface WebInspectorOutlineView : NSOutlineView
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 "WebInspectorOutlineView.h"
+
+@implementation WebInspectorOutlineView
+- (BOOL)isOpaque
+{
+ return NO;
+}
+
+- (NSColor *)_highlightColorForCell:(NSCell *)cell
+{
+ // return nil to prevent normal selection drawing
+ return nil;
+}
+
+- (void)_highlightRow:(int)row clipRect:(NSRect)clip {
+ NSColor *selectionColor = nil;
+ if ([[self window] firstResponder] == self && [[self window] isKeyWindow])
+ selectionColor = [[NSColor alternateSelectedControlColor] colorWithAlphaComponent:0.333];
+ selectionColor = [[NSColor alternateSelectedControlColor] colorWithAlphaComponent:0.45];
+
+ if ([self numberOfColumns] > 0) {
+ NSRect highlight = [self rectOfRow:row];
+ NSRect drawRect = NSIntersectionRect(clip, highlight);
+ BOOL solid = ([[self window] firstResponder] == self && [[self window] isKeyWindow]);
+ if (NSContainsRect(drawRect,clip) && !NSEqualRects(drawRect,highlight)) {
+ // draw a solid rect when the outline view wants to draw inside the row rect
+ if ([NSGraphicsContext currentContextDrawingToScreen] && solid) {
+ [selectionColor set];
+ NSRectFillUsingOperation(drawRect, NSCompositeSourceOver);
+ }
+ } else {
+ // draw a rounded rect for the full row
+ if ([NSGraphicsContext currentContextDrawingToScreen]) {
+ NSBezierPath *path = [[NSBezierPath alloc] init];
+
+ float radius = 6.0;
+ NSRect irect = NSInsetRect(highlight, radius, radius);
+ if (!solid)
+ irect = NSInsetRect(irect, 1.0, 1.0);
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(irect), NSMinY(irect)) radius:radius startAngle:180. endAngle:270.];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(irect), NSMinY(irect)) radius:radius startAngle:270. endAngle:360.];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(irect), NSMaxY(irect)) radius:radius startAngle:0. endAngle:90.];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(irect), NSMaxY(irect)) radius:radius startAngle:90. endAngle:180.];
+ [path closePath];
+
+ NSRectClip(clip);
+ [selectionColor set];
+ if (solid) {
+ [path fill];
+ } else {
+ [path setLineWidth:2.0];
+ [path stroke];
+ }
+
+ [path release];
+ }
+ }
+ }
+}
+
+- (void)drawBackgroundInClipRect:(NSRect)clipRect
+{
+ // don't draw a background
+}
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 <AppKit/NSPanel.h>
+
+@interface WebInspectorPanel : NSPanel {
+ BOOL _mouseInRegion;
+}
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 "WebInspectorPanel.h"
+
+#import <WebKit/WebView.h>
+#import <WebKit/WebViewPrivate.h>
+#import <WebKit/WebDashboardRegion.h>
+#import <WebKit/DOMCore.h>
+#import <WebKit/DOMHTML.h>
+#import <WebKit/DOMCSS.h>
+
+@implementation WebInspectorPanel
+- (BOOL)canBecomeKeyWindow
+{
+ return YES;
+}
+
+- (BOOL)canBecomeMainWindow
+{
+ return YES;
+}
+
+- (void)moveWindow:(NSEvent *)event
+{
+ NSPoint startLocation = [event locationInWindow];
+ NSPoint lastLocation = startLocation;
+ BOOL mouseUpOccurred = NO;
+
+ while (!mouseUpOccurred) {
+ // set mouseUp flag here, but process location of event before exiting from loop, leave mouseUp in queue
+ event = [self nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask) untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:YES];
+
+ if ([event type] == NSLeftMouseUp)
+ mouseUpOccurred = YES;
+
+ NSPoint newLocation = [event locationInWindow];
+ if (NSEqualPoints(newLocation, lastLocation))
+ continue;
+
+ NSPoint origin = [self frame].origin;
+ [self setFrameOrigin:NSMakePoint(origin.x + newLocation.x - startLocation.x, origin.y + newLocation.y - startLocation.y)];
+ lastLocation = newLocation;
+ }
+}
+
+- (void)resizeWindow:(NSEvent *)event
+{
+ NSRect startFrame = [self frame];
+ NSPoint startLocation = [self convertBaseToScreen:[event locationInWindow]];
+ NSPoint lastLocation = startLocation;
+ NSSize minSize = [self minSize];
+ NSSize maxSize = [self maxSize];
+ BOOL mouseUpOccurred = NO;
+
+ while (!mouseUpOccurred) {
+ // set mouseUp flag here, but process location of event before exiting from loop, leave mouseUp in queue
+ event = [self nextEventMatchingMask:(NSLeftMouseDraggedMask | NSLeftMouseUpMask) untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:YES];
+
+ if ([event type] == NSLeftMouseUp)
+ mouseUpOccurred = YES;
+
+ NSPoint newLocation = [self convertBaseToScreen:[event locationInWindow]];
+ if (NSEqualPoints(newLocation, lastLocation))
+ continue;
+
+ NSRect proposedRect = startFrame;
+ proposedRect.size.width += newLocation.x - startLocation.x;;
+ proposedRect.size.height -= newLocation.y - startLocation.y;
+ proposedRect.origin.y += newLocation.y - startLocation.y;
+
+ if (proposedRect.size.width < minSize.width) {
+ proposedRect.size.width = minSize.width;
+ } else if (proposedRect.size.width > maxSize.width) {
+ proposedRect.size.width = maxSize.width;
+ }
+
+ if (proposedRect.size.height < minSize.height) {
+ proposedRect.origin.y -= minSize.height - proposedRect.size.height;
+ proposedRect.size.height = minSize.height;
+ } else if (proposedRect.size.height > maxSize.height) {
+ proposedRect.origin.y -= maxSize.height - proposedRect.size.height;
+ proposedRect.size.height = maxSize.height;
+ }
+
+ [self setFrame:proposedRect display:YES];
+ lastLocation = newLocation;
+ }
+}
+
+- (void)sendEvent:(NSEvent *)event
+{
+ if (_mouseInRegion && [event type] == NSLeftMouseUp)
+ _mouseInRegion = NO;
+
+ if (([event type] == NSLeftMouseDown || [event type] == NSLeftMouseDragged) && !_mouseInRegion) {
+ NSPoint pointInView = [[[[(WebView *)[self contentView] mainFrame] frameView] documentView] convertPoint:[event locationInWindow] fromView:nil];
+ NSDictionary *regions = [(WebView *)[self contentView] _dashboardRegions];
+
+ WebDashboardRegion *region = [[regions objectForKey:@"resizeTop"] lastObject];
+ region = [[regions objectForKey:@"resize"] lastObject];
+ if (region) {
+ if (NSPointInRect(pointInView, [region dashboardRegionClip])) {
+ // we are in a resize control region, resize the window now and eat the event
+ [self resizeWindow:event];
+ return;
+ }
+ }
+
+ NSArray *controlRegions = [regions objectForKey:@"control"];
+ NSEnumerator *enumerator = [controlRegions objectEnumerator];
+ while ((region = [enumerator nextObject])) {
+ if (NSPointInRect(pointInView, [region dashboardRegionClip])) {
+ // we are in a control region, lets pass the event down
+ _mouseInRegion = YES;
+ [super sendEvent:event];
+ return;
+ }
+ }
+
+ // if we are dragging and the mouse isn't in a control region move the window
+ if ([event type] == NSLeftMouseDragged) {
+ [self moveWindow:event];
+ return;
+ }
+ }
+
+ [super sendEvent:event];
+}
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 WebNodeHighlightView;
+
+@interface WebNodeHighlight : NSObject
+{
+ NSWindow *_webNodeHighlightWindow;
+ WebNodeHighlightView *_webNodeHighlightView;
+ NSTimer *_timer;
+ CFAbsoluteTime _startTime;
+ CFTimeInterval _duration;
+}
+- (id)initWithBounds:(NSRect)bounds andRects:(NSArray *)rects forView:(NSView *)view;
+- (double)fractionComplete;
+- (void)expire;
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 <mach/mach_time.h>
+#import "WebNodeHighlightView.h"
+#import "WebNodeHighlight.h"
+
+@implementation WebNodeHighlight
+- (id)initWithBounds:(NSRect)bounds andRects:(NSArray *)rects forView:(NSView *)view
+{
+ if (![self init])
+ return nil;
+
+ _startTime = 0.0;
+ _duration = 3.0;
+
+ if ([[[NSApplication sharedApplication] currentEvent] modifierFlags] & NSShiftKeyMask)
+ _duration = 6.0;
+
+ if (!rects)
+ rects = [NSArray arrayWithObject:[NSValue valueWithRect:bounds]];
+
+ _webNodeHighlightView = [[WebNodeHighlightView alloc] initWithHighlight:self andRects:rects forView:view];
+ if (!_webNodeHighlightView) {
+ [self release];
+ return nil;
+ }
+
+ // adjust size and position for rect padding that the view adds
+ bounds.origin.y -= 3.0;
+ bounds.origin.x -= 3.0;
+ bounds.size = [_webNodeHighlightView frame].size;
+
+ NSRect windowBounds = [view convertRect:bounds toView:nil];
+ windowBounds.origin = [[view window] convertBaseToScreen:windowBounds.origin]; // adjust for screen coords
+
+ _webNodeHighlightWindow = [[NSWindow alloc] initWithContentRect:windowBounds styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
+ [_webNodeHighlightWindow setBackgroundColor:[NSColor clearColor]];
+ [_webNodeHighlightWindow setOpaque:NO];
+ [_webNodeHighlightWindow setHasShadow:NO];
+ [_webNodeHighlightWindow setIgnoresMouseEvents:YES];
+ [_webNodeHighlightWindow setReleasedWhenClosed:YES];
+ [_webNodeHighlightWindow setLevel:[[view window] level] + 1];
+ [_webNodeHighlightWindow setContentView:_webNodeHighlightView];
+ [_webNodeHighlightView release];
+
+ [_webNodeHighlightWindow orderFront:self];
+
+ // 30 frames per second time interval will play well with the CPU and still look smooth
+ _timer = [[NSTimer scheduledTimerWithTimeInterval:(1.0 / 30.0) target:self selector:@selector(redraw:) userInfo:nil repeats:YES] retain];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(expire) name:NSViewBoundsDidChangeNotification object:view];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(expire) name:NSViewBoundsDidChangeNotification object:[view superview]];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(expire) name:NSWindowWillMoveNotification object:[view window]];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(expire) name:NSWindowWillCloseNotification object:[view window]];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewBoundsDidChangeNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillMoveNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:nil];
+
+ [_timer invalidate];
+ [_timer release];
+
+ [super dealloc];
+}
+
+- (double)fractionComplete
+{
+ if (_startTime == 0.0)
+ _startTime = CFAbsoluteTimeGetCurrent();
+
+ return ((CFAbsoluteTimeGetCurrent() - _startTime) / _duration);
+}
+
+- (void)expire
+{
+ [_timer invalidate];
+ [_timer release];
+ _timer = nil;
+
+ [_webNodeHighlightWindow close];
+ _webNodeHighlightWindow = nil;
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"WebNodeHighlightExpired" object:self userInfo:nil];
+}
+
+- (void)redraw:(NSTimer *)timer
+{
+ [_webNodeHighlightView setNeedsDisplay:YES];
+
+ if (_startTime == 0.0)
+ _startTime = CFAbsoluteTimeGetCurrent();
+
+ if ((CFAbsoluteTimeGetCurrent() - _startTime) > _duration)
+ [self expire];
+}
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 WebNodeHighlight;
+
+@interface WebNodeHighlightView : NSView
+{
+ WebNodeHighlight *_highlight;
+ NSImage *_highlightRingImage;
+}
+- (id)initWithHighlight:(WebNodeHighlight *)hightlight andRects:(NSArray *)rects forView:(NSView *)view;
+@end
--- /dev/null
+/*
+ * 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.
+ * 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 "WebNodeHighlight.h"
+#import "WebNodeHighlightView.h"
+#import <WebKitSystemInterface.h>
+
+@implementation WebNodeHighlightView
+- (NSBezierPath *)roundedRect:(NSRect)rect withRadius:(float)radius
+{
+ NSBezierPath *path = [[NSBezierPath alloc] init];
+
+ NSRect irect = NSInsetRect( rect, radius, radius );
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(irect), NSMinY(irect)) radius:radius startAngle:180. endAngle:270.];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(irect), NSMinY(irect)) radius:radius startAngle:270. endAngle:360.];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(irect), NSMaxY(irect)) radius:radius startAngle:0. endAngle:90.];
+ [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(irect), NSMaxY(irect)) radius:radius startAngle:90. endAngle:180.];
+ [path closePath];
+
+ return [path autorelease];
+}
+
+- (id)initWithHighlight:(WebNodeHighlight *)highlight andRects:(NSArray *)rects forView:(NSView *)view
+{
+ if (![self init])
+ return nil;
+
+ _highlight = highlight; // don't retain, would cause a circular retain
+
+ NSRect visibleRect = [view visibleRect];
+
+ NSRect rect = NSZeroRect;
+ NSBezierPath *path = nil;
+ NSBezierPath *straightPath = nil;
+
+ if([rects count] == 1) {
+ NSValue *value = (NSValue *)[rects objectAtIndex:0];
+ rect = NSInsetRect([value rectValue], -1.0, -1.0);
+ rect = NSIntersectionRect(rect, visibleRect);
+ if (!NSIsEmptyRect(rect))
+ path = [[self roundedRect:rect withRadius:3.0] retain];
+
+ // shift everything to the corner
+ NSAffineTransform *transform = [[NSAffineTransform alloc] init];
+ [transform translateXBy:(NSMinX(rect) * -1.0) + 2.5 yBy:(NSMinY(rect) * -1.0) + 2.5];
+ [path transformUsingAffineTransform:transform];
+ [straightPath transformUsingAffineTransform:transform];
+ [transform release];
+ } else if ([rects count] > 1) {
+ path = [[NSBezierPath alloc] init];
+ straightPath = [path copy];
+
+ // roundedRect: returns an autoreleased path, so release them soon with a pool
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ NSEnumerator *enumerator = [rects objectEnumerator];
+ NSValue *value = nil;
+ while ((value = [enumerator nextObject])) {
+ rect = NSIntersectionRect([value rectValue], visibleRect);
+ if (!NSIsEmptyRect(rect)) {
+ [straightPath appendBezierPathWithRect:rect];
+ [path appendBezierPath:[self roundedRect:rect withRadius:3.0]];
+ }
+ }
+
+ [pool drain];
+ [pool release];
+
+ rect = [path bounds];
+
+ [straightPath setWindingRule:NSNonZeroWindingRule];
+ [straightPath setLineJoinStyle:NSRoundLineJoinStyle];
+ [straightPath setLineCapStyle:NSRoundLineCapStyle];
+
+ // multiple rects we get from WebCore need flipped to show up correctly
+ NSAffineTransform *transform = [[NSAffineTransform alloc] init];
+ [transform scaleXBy:1.0 yBy:-1.0];
+ [path transformUsingAffineTransform:transform];
+ [straightPath transformUsingAffineTransform:transform];
+ [transform release];
+
+ // shift everything to the corner
+ transform = [[NSAffineTransform alloc] init];
+ [transform translateXBy:(NSMinX(rect) * -1.0) + 2.5 yBy:NSMaxY(rect) + 2.5];
+ [path transformUsingAffineTransform:transform];
+ [straightPath transformUsingAffineTransform:transform];
+ [transform release];
+ }
+
+ if (!path || [path isEmpty]) {
+ [self release];
+ return nil;
+ }
+
+ [path setWindingRule:NSNonZeroWindingRule];
+ [path setLineJoinStyle:NSRoundLineJoinStyle];
+ [path setLineCapStyle:NSRoundLineCapStyle];
+
+ // make the drawing area larger for the focus ring blur
+ rect = [path bounds];
+ rect.size.width += 5.0;
+ rect.size.height += 5.0;
+ [self setFrameSize:rect.size];
+
+ // draw into an image
+ _highlightRingImage = [[NSImage alloc] initWithSize:rect.size];
+ [_highlightRingImage lockFocus];
+ [NSGraphicsContext saveGraphicsState];
+
+ if (straightPath) {
+ [[NSColor redColor] set];
+ [path setLineWidth:4.0];
+ [path stroke];
+
+ // clear the center to eliminate thick inner strokes for overlapping rects
+ [[NSGraphicsContext currentContext] setCompositingOperation:NSCompositeClear];
+ [path fill];
+
+ // stroke the straight line path with a light color to show any inner rects
+ [[NSGraphicsContext currentContext] setCompositingOperation:NSCompositeDestinationOver];
+ [[[NSColor redColor] colorWithAlphaComponent:0.6] set];
+ [straightPath setLineWidth:1.0];
+ [straightPath stroke];
+ } else {
+ [[NSColor redColor] set];
+ [path setLineWidth:2.0];
+ [path stroke];
+ }
+
+ [NSGraphicsContext restoreGraphicsState];
+ [_highlightRingImage unlockFocus];
+
+ [path release];
+ [straightPath release];
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_highlightRingImage release];
+ [super dealloc];
+}
+
+- (BOOL)isOpaque
+{
+ return NO;
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ double alpha = 1.0 - [_highlight fractionComplete];
+ if (alpha > 1.0)
+ alpha = 1.0;
+ else if (alpha < 0.0)
+ alpha = 0.0;
+
+ [_highlightRingImage drawInRect:rect fromRect:rect operation:NSCompositeCopy fraction:alpha];
+}
+@end
--- /dev/null
+/*
+ * 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.
+ * 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.
+ */
+
+html {
+ height: 100%;
+}
+
+body {
+ overflow: hidden;
+ height: 100%;
+}
+
+* {
+ -apple-user-select: none;
+}
+
+a {
+ cursor: default;
+}
+
+button {
+ -apple-dashboard-region: dashboard-region(control rectangle);
+}
+
+.disabled {
+ color: rgba(255,255,255,0.5);
+}
+
+#body {
+ position: absolute;
+ background-color: rgba(20,20,20,0.9);
+ border-radius: 8px;
+ top: 0;
+ height: 100%;
+ left: 0;
+ right: 0;
+ color: white;
+ font-family: Lucida Grande, sans-serif;
+ font-size: 12px;
+ text-shadow: black 0px 1px 2px;
+}
+
+#title {
+ position: absolute;
+ background-color: rgba(255,255,255,0.1);
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+ height: 20px;
+ top: 0;
+ left: 0;
+ right: 0;
+ text-align: center;
+ font-size: 11px;
+ line-height: 20px;
+}
+
+#header {
+ position: absolute;
+ height: 26px;
+ top: 0;
+ left: 0;
+ right: 0;
+}
+
+#search {
+ font-size: 11px;
+ position: absolute;
+ top: 1px;
+ right: 10px;
+ width: 90px;
+}
+
+#searchCount {
+ font-size: 11px;
+ position: absolute;
+ top: 2px;
+ right: 110px;
+ width: 100px;
+ text-align: right;
+ display: none;
+}
+
+#treePopup {
+ position: absolute;
+ top: 4px;
+ left: 54px;
+ right: 106px;
+}
+
+#treePopupButton {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+}
+
+#realTreePopup {
+ opacity: 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ z-index: 10;
+ margin: 0;
+ padding: 0;
+}
+
+#treePopupTitleArea {
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+#traverse {
+ position: absolute;
+ top: 4px;
+ left: 8px;
+ width: 50px;
+}
+
+#traverseUp .middle {
+ right: 0;
+}
+
+#traverseUp .middle > img {
+ position: absolute;
+ top: 4px;
+ left: 1px;
+}
+
+#traverseDown .middle {
+ left: 1px;
+}
+
+#traverseDown .middle > img {
+ position: absolute;
+ top: 5px;
+ left: 5px;
+}
+
+#traverseUp {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 20px;
+}
+
+#traverseDown {
+ position: absolute;
+ top: 0;
+ left: 20px;
+ width: 21px;
+}
+
+#traverseDown > img {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+#tabs {
+ position: absolute;
+ top: 3px;
+ left: 50%;
+ margin-left: -130px;
+}
+
+#tabs .middle {
+ left: 0;
+ right: 0;
+ text-align: center;
+}
+
+#tabs button > img {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+#nodeButton {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 60px;
+}
+
+#nodeButton .middle {
+ padding-right: 6px;
+ left: 6px;
+ right: 0;
+}
+
+#metricsButton {
+ position: absolute;
+ top: 0;
+ left: 115px;
+ width: 65px;
+}
+
+#styleButton {
+ position: absolute;
+ top: 0;
+ left: 60px;
+ width: 55px;
+}
+
+#javascriptButton {
+ position: absolute;
+ top: 0;
+ left: 180px;
+ width: 80px;
+}
+
+#javascriptButton .middle {
+ padding-left: 6px;
+ left: 0;
+ right: 6px;
+}
+
+#title button.close {
+ position: absolute;
+ top: 4px;
+ left: 5px;
+ border: 0;
+ background-color: transparent;
+ background-image: url( Images/close.png );
+ background-repeat: no-repeat;
+ height: 13px;
+ width: 13px;
+}
+
+#title button.close[disabled] {
+ opacity: 0.5;
+}
+
+#title button.close:active {
+ background-image: url( Images/closePressed.png );
+}
+
+#title button.menu {
+ position: absolute;
+ top: 4px;
+ right: 5px;
+ border: 0;
+ background-color: transparent;
+ background-image: url( Images/menu.png );
+ background-repeat: no-repeat;
+ height: 13px;
+ width: 13px;
+}
+
+#title button.menu[disabled] {
+ opacity: 0.5;
+}
+
+#title button.menu:active {
+ background-image: url( Images/menuPressed.png );
+}
+
+button.popup {
+ height: 20px;
+ border: 0;
+ background-color: transparent;
+ color: white;
+ padding: 0;
+ margin: 0;
+}
+
+button.popup .left {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 20px;
+ width: 6px;
+ background-image: url( Images/popupLeft.png );
+ background-repeat: no-repeat;
+}
+
+button.popup .middle {
+ position: absolute;
+ top: 0;
+ left: 6px;
+ right: 19px;
+ height: 20px;
+ line-height: 1em;
+ background-image: url( Images/popupFill.png );
+ background-repeat: repeat-x;
+ line-height: 17px;
+ text-align: left;
+ padding: 0 2px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+button.popup .right {
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 20px;
+ width: 19px;
+ background-image: url( Images/popupRight.png );
+ background-repeat: no-repeat;
+}
+
+button.popup[disabled] {
+ opacity: 0.5;
+}
+
+button.popup[disabled] .left {
+ background-image: url( Images/popupLeft.png ) !important;
+}
+
+button.popup[disabled] .middle {
+ background-image: url( Images/popupFill.png ) !important;
+}
+
+button.popup[disabled] .right {
+ background-image: url( Images/popupRight.png ) !important;
+}
+
+button.popup:active .left {
+ background-image: url( Images/popupLeftPressed.png );
+}
+
+button.popup:active .middle {
+ background-image: url( Images/popupFillPressed.png );
+ text-shadow: black 1px -1px 3px;
+}
+
+button.popup:active .right {
+ background-image: url( Images/popupRightPressed.png );
+}
+
+button.square {
+ height: 20px;
+ border: 0;
+ background-color: transparent;
+ color: white;
+ padding: 0;
+ margin: 0;
+}
+
+button.square .left {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 20px;
+ width: 6px;
+ background-image: url( Images/popupLeft.png );
+ background-repeat: no-repeat;
+}
+
+button.square .middle {
+ position: absolute;
+ top: 0;
+ left: 6px;
+ right: 6px;
+ height: 20px;
+ line-height: 1em;
+ background-image: url( Images/popupFill.png );
+ background-repeat: repeat-x;
+ line-height: 17px;
+ text-align: left;
+ padding: 0 2px;
+}
+
+button.square .right {
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: 20px;
+ width: 6px;
+ background-image: url( Images/squareButtonRight.png );
+ background-repeat: no-repeat;
+}
+
+button.square[disabled] {
+ opacity: 0.5;
+}
+
+button.square[disabled] .left {
+ background-image: url( Images/popupLeft.png ) !important;
+}
+
+button.square[disabled] .middle {
+ background-image: url( Images/popupFill.png ) !important;
+}
+
+button.square[disabled] .right {
+ background-image: url( Images/squareButtonRight.png ) !important;
+}
+
+button.square:active .left, button.square.selected .left {
+ background-image: url( Images/popupLeftPressed.png );
+}
+
+button.square:active .middle, button.square.selected .middle {
+ background-image: url( Images/popupFillPressed.png );
+ text-shadow: black 1px -1px 3px;
+}
+
+button.square.selected .middle {
+ font-weight: bold;
+}
+
+button.square:active .right, button.square.selected .right {
+ background-image: url( Images/squareButtonRightPressed.png );
+}
+
+#resize {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ width: 16px;
+ height: 16px;
+ z-index: 10;
+ -apple-dashboard-region: dashboard-region(resize rectangle);
+ background-image: url( file:///System/Library/WidgetResources/resize.png );
+ background-repeat: no-repeat;
+}
+
+#top {
+ position: absolute;
+ top: 20px;
+ left: 0;
+ right: 0;
+ height: 170px;
+}
+
+#bottom {
+ position: absolute;
+ top: 200px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+#bottom .pane {
+ position: absolute;
+ top: 28px;
+ left: 10px;
+ right: 10px;
+ bottom: 12px;
+}
+
+#splitter {
+ position: absolute;
+ top: 190px;
+ left: 5px;
+ right: 5px;
+ height: 10px;
+ cursor: move;
+ z-index: 10;
+ -apple-dashboard-region: dashboard-region(control rectangle);
+}
+
+#splitter hr {
+ position: absolute;
+ top: 4px;
+ left: 0;
+ right: 0;
+ padding: 0;
+ margin: 0;
+ opacity: 0.85;
+}
+
+#tree {
+ background-color: rgba(0,0,0,0.2);
+ border: 1px solid rgba(255,255,255,0.4);
+ position: absolute;
+ top: 26px;
+ bottom: 4px;
+ left: 10px;
+ right: 10px;
+ -apple-dashboard-region: dashboard-region(control rectangle);
+}
+
+#treeScrollArea {
+ position: absolute;
+ top: 0;
+ left: 3px;
+ right: 23px;
+ bottom: 0;
+}
+
+#treeOutlineView {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+
+#treeScrollbar {
+ position: absolute;
+ top: 2px;
+ bottom: 2px;
+ right: 2px;
+ width: 19px;
+ display: block;
+}
+
+button.toggle {
+ width: 8px;
+ height: 8px;
+ border: 0;
+ margin: 0 2px;
+ padding: 0;
+ background-color: transparent;
+}
+
+button.toggle.down {
+ background-image: url( Images/downTriangle.png );
+}
+
+button.toggle.right {
+ background-image: url( Images/rightTriangle.png );
+}
+
+#nodePane {
+ display: -apple-box;
+ -apple-box-orient: vertical;
+}
+
+.infoRow {
+ display: -apple-box;
+ -apple-box-orient: horizontal;
+ margin-top: 2px;
+}
+
+.infoRow.header {
+ margin-top: 6px;
+ border-bottom: 1px solid rgba(255,255,255,0.4);
+ margin-bottom: 3px;
+}
+
+.infoRow .label {
+ display: block;
+ font-weight: bold;
+ text-align: right;
+ padding-right: 3px;
+ width: 100px;
+}
+
+.infoRow .value {
+ display: -apple-box;
+ -apple-box-flex: 1;
+ -apple-dashboard-region: dashboard-region(control rectangle);
+ -apple-user-select: text;
+ white-space: nowrap;
+}
+
+/* This is a workaround for text-overflow: ellipsis not working on boxes. */
+/* http://bugzilla.opendarwin.org/show_bug.cgi?id=5990 */
+.infoRow .value div {
+ width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -apple-user-select: text;
+}
+
+#elementAttributes {
+ display: -apple-box;
+ -apple-box-flex: 3;
+ -apple-box-orient: vertical;
+ min-height: 60px;
+ max-height: 160px;
+}
+
+#nodeContents {
+ display: -apple-box;
+ -apple-box-flex: 12;
+ -apple-box-orient: vertical;
+ overflow: hidden;
+}
+
+#nodeContentsScrollview {
+ -apple-dashboard-region: dashboard-region(control rectangle);
+ -apple-user-select: text;
+ overflow: hidden;
+ font-size: 11px;
+ word-wrap: break-word;
+}
+
+.scrollArea {
+ display: -apple-box;
+ -apple-box-orient: horizontal;
+ -apple-box-flex: 1;
+}
+
+.scrollArea > .view {
+ position: relative;
+ display: -apple-box;
+ -apple-box-flex: 1;
+}
+
+.scrollArea > .view > div {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ overflow: hidden;
+}
+
+.scrollArea > .scrollbar {
+ position: relative;
+ margin-left: 3px;
+ width: 19px;
+ -apple-dashboard-region: dashboard-region(control rectangle);
+}
+
+#stylePane {
+ display: -apple-box;
+ -apple-box-flex: 1;
+ -apple-box-orient: vertical;
+ overflow: hidden;
+}
+
+#styleRules {
+ background-color: rgba(0,0,0,0.2);
+ border: 1px solid rgba(255,255,255,0.4);
+ margin-bottom: 6px;
+}
+
+#styleRulesScrollview {
+ display: -apple-box;
+ -apple-box-orient: vertical;
+ -apple-dashboard-region: dashboard-region(control rectangle);
+ -apple-box-flex: 1;
+}
+
+#styleRulesScrollview > .row {
+ padding: 1px 6px;
+ margin: 2px 0 0 3px;
+}
+
+#styleRulesScrollview > .row.focused {
+ background-color: rgba(128,128,255,0.25);
+ border-radius: 6px;
+ text-shadow: black 1px 2px 3px;
+}
+
+#styleRulesScrollview > .row > .cell {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+#styleRulesScrollview .cell.stylesheet {
+ color: rgba(255,255,255,0.6);
+ font-size: 10px;
+ padding-bottom: 2px;
+}
+
+#styleProperties {
+ background-color: rgba(0,0,0,0.2);
+ border: 1px solid rgba(255,255,255,0.4);
+ -apple-box-flex: 1;
+ -apple-dashboard-region: dashboard-region(control rectangle);
+}
+
+.treeList {
+ padding: 3px 0;
+ margin: 0;
+ list-style-type: none;
+ white-space: nowrap;
+}
+
+.treeList ul {
+ list-style-type: none;
+ -apple-padding-start: 10px;
+}
+
+.treeList li {
+ line-height: 14px;
+ height: 16px;
+ padding-left: 14px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.treeList li button {
+ position: relative;
+ top: 4px;
+ left: 1px;
+ float: left;
+ background-image: url( Images/rightTriangle.png );
+ width: 8px;
+ height: 8px;
+ border: 0;
+ margin: 0 2px;
+ padding: 0;
+ background-color: transparent;
+}
+
+.treeList li.hasChildren {
+ padding-left: 2px;
+}
+
+.treeList li.focused {
+ background-color: rgba(128,128,255,0.25);
+ border-radius: 4px;
+ text-shadow: black 1px 2px 3px;
+}
+
+.treeList li.expanded button {
+ background-image: url( Images/downTriangle.png );
+}
+
+.treeList li span {
+ -apple-user-select: text;
+}
+
+.treeList li .property::after {
+ content: ": ";
+}
+
+.treeList li .value {
+ color: rgb(255,255,180);
+}
+
+.treeList li.implicit .value {
+ color: rgb(180,255,180);
+}
+
+.treeList li .value::after {
+ content: ";";
+}
+
+.treeList li.overloaded {
+ text-decoration: line-through;
+}
--- /dev/null
+<!--
+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.
+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" />
+ <title>Web Inspector</title>
+ <script type="text/javascript" src="inspector.js"></script>
+ <script type="text/javascript" src="file:///System/Library/WidgetResources/AppleClasses/AppleScrollbar.js"></script>
+ <script type="text/javascript" src="file:///System/Library/WidgetResources/AppleClasses/AppleScrollArea.js"></script>
+ <style type="text/css">
+ @import "inspector.css";
+ </style>
+</head>
+<body onload="loaded()">
+<div id="body">
+<div id="title"><button class="close" onclick="window.close()"></button>Web Inspector<button class="menu" onmousedown="Inspector.showOptionsMenu()"></button></div>
+<div id="top">
+ <div id="header">
+ <div id="traverse"><button class="square" id="traverseUp" onclick="Inspector.traverseTreeBackward()"><div class="left"></div><div class="middle"><img src="Images/upTriangle.png" /></div></button><button class="square" id="traverseDown" onclick="Inspector.traverseTreeForward()"><img src="Images/popupFillPressed.png" /><div class="middle"><img src="Images/downTriangle.png" /></div><div class="right"></div></button></div>
+ <div id="treePopup">
+ <select size="1" id="realTreePopup" onchange="Inspector.selectNewRoot(this)"></select>
+ <button id="treePopupButton" class="popup"><div class="left"></div><div id="treePopupTitleArea" class="middle"></div><div class="right"></div></button>
+ </div>
+ <div id="searchCount"></div>
+ <input id="search" type="search" autosave="nodeSearch" results="20" placeholder="Search" incremental="incremental" />
+ </div>
+ <div id="tree">
+ <div id="treeScrollArea"><object id="treeOutlineView"><param name="type" value="application/x-inspector-tree" /></object></div>
+ <div id="treeScrollbar"></div>
+ </div>
+</div>
+<div id="splitter" onmousedown="Inspector.resizeTopArea(event)"><hr size="2" /></div>
+<div id="bottom">
+ <div id="tabs">
+ <button class="square selected" id="nodeButton" onclick="switchPane('node')"><div class="left"></div><div class="middle">Node</div></button>
+ <button class="square" id="styleButton" onclick="switchPane('style')"><img src="Images/popupFillPressed.png" /><div class="middle">Style</div></button>
+ <button class="square" id="metricsButton" onclick="switchPane('metrics')"><img src="Images/popupFillPressed.png" /><div class="middle">Metrics</div></button>
+ <button class="square" id="javascriptButton" onclick="switchPane('javascript')"><img src="Images/popupFillPressed.png" /><div class="middle">Properties</div><div class="right"></div></button>
+ </div>
+ <div id="nodePane" class="pane">
+ <div class="infoRow">
+ <span class="label">Node Type:</span>
+ <span class="value" id="nodeType"></span>
+ </div>
+ <div class="infoRow">
+ <span class="label">Node Name:</span>
+ <span class="value" id="nodeName"></span>
+ </div>
+ <div class="infoRow" id="nodeNamespaceRow">
+ <span class="label">Namespace URI:</span>
+ <span class="value"><div id="nodeNamespace"></div></span>
+ </div>
+ <div id="elementAttributes">
+ <div class="infoRow header">
+ <span class="label" style="width: initial">Element Attributes</span>
+ </div>
+ <div class="scrollArea">
+ <div id="elementAttributesScrollview" class="view">(not finished)</div>
+ <div id="elementAttributesScrollbar" class="scrollbar"></div>
+ </div>
+ </div>
+ <div id="nodeContents">
+ <div class="infoRow header">
+ <span class="label" style="width: initial">Markup & Content</span>
+ </div>
+ <div class="scrollArea">
+ <div class="view"><div id="nodeContentsScrollview"></div></div>
+ <div id="nodeContentsScrollbar" class="scrollbar"></div>
+ </div>
+ </div>
+ </div>
+ <div id="metricsPane" class="pane" style="display: none; text-align: center"><br /><br /><br />(not finished)</div>
+ <div id="stylePane" class="pane" style="display: none">
+ <div id="styleRules" class="scrollArea">
+ <div class="view"><div id="styleRulesScrollview"></div></div>
+ <div id="styleRulesScrollbar" class="scrollbar"></div>
+ </div>
+ <div id="styleProperties" class="scrollArea">
+ <div class="view"><div id="stylePropertiesScrollview"><ul class="treeList" id="stylePropertiesTree"></ul></div></div>
+ <div id="stylePropertiesScrollbar" class="scrollbar"></div>
+ </div>
+ </div>
+ <div id="javascriptPane" class="pane" style="display: none; text-align: center"><br /><br /><br />(not finished)</div>
+</div>
+<div id="resize"></div>
+</div>
+</body>
+</html>
--- /dev/null
+/*
+ * 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.
+ * 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 loaded()
+{
+ treeScrollbar = new AppleVerticalScrollbar(document.getElementById("treeScrollbar"));
+
+ treeScrollbar.setTrackStart("Images/scrollTrackTop.png", 18);
+ treeScrollbar.setTrackMiddle("Images/scrollTrackMiddle.png");
+ treeScrollbar.setTrackEnd("Images/scrollTrackBottom.png", 18);
+ treeScrollbar.setThumbStart("Images/scrollThumbTop.png", 9);
+ treeScrollbar.setThumbMiddle("Images/scrollThumbMiddle.png");
+ treeScrollbar.setThumbEnd("Images/scrollThumbBottom.png", 9);
+
+ nodeContentsScrollbar = new AppleVerticalScrollbar(document.getElementById("nodeContentsScrollbar"));
+ nodeContentsScrollArea = new AppleScrollArea(document.getElementById("nodeContentsScrollview"), nodeContentsScrollbar);
+
+ nodeContentsScrollbar.setTrackStart("Images/scrollTrackTop.png", 18);
+ nodeContentsScrollbar.setTrackMiddle("Images/scrollTrackMiddle.png");
+ nodeContentsScrollbar.setTrackEnd("Images/scrollTrackBottom.png", 18);
+ nodeContentsScrollbar.setThumbStart("Images/scrollThumbTop.png", 9);
+ nodeContentsScrollbar.setThumbMiddle("Images/scrollThumbMiddle.png");
+ nodeContentsScrollbar.setThumbEnd("Images/scrollThumbBottom.png", 9);
+
+ styleRulesScrollbar = new AppleVerticalScrollbar(document.getElementById("styleRulesScrollbar"));
+ styleRulesScrollArea = new AppleScrollArea(document.getElementById("styleRulesScrollview"), styleRulesScrollbar);
+
+ styleRulesScrollbar.setTrackStart("Images/scrollTrackTop.png", 18);
+ styleRulesScrollbar.setTrackMiddle("Images/scrollTrackMiddle.png");
+ styleRulesScrollbar.setTrackEnd("Images/scrollTrackBottom.png", 18);
+ styleRulesScrollbar.setThumbStart("Images/scrollThumbTop.png", 9);
+ styleRulesScrollbar.setThumbMiddle("Images/scrollThumbMiddle.png");
+ styleRulesScrollbar.setThumbEnd("Images/scrollThumbBottom.png", 9);
+
+ stylePropertiesScrollbar = new AppleVerticalScrollbar(document.getElementById("stylePropertiesScrollbar"));
+ stylePropertiesScrollArea = new AppleScrollArea(document.getElementById("stylePropertiesScrollview"), stylePropertiesScrollbar);
+
+ stylePropertiesScrollbar.setTrackStart("Images/scrollTrackTop.png", 18);
+ stylePropertiesScrollbar.setTrackMiddle("Images/scrollTrackMiddle.png");
+ stylePropertiesScrollbar.setTrackEnd("Images/scrollTrackBottom.png", 18);
+ stylePropertiesScrollbar.setThumbStart("Images/scrollThumbTop.png", 9);
+ stylePropertiesScrollbar.setThumbMiddle("Images/scrollThumbMiddle.png");
+ stylePropertiesScrollbar.setThumbEnd("Images/scrollThumbBottom.png", 9);
+
+ treeScrollbar._getViewToContentRatio = function() {
+ var contentHeight = Inspector.treeViewScrollHeight();
+ var height = document.getElementById("treeScrollArea").offsetHeight;
+ if (contentHeight > height)
+ return height / contentHeight;
+ return 1.0;
+ }
+
+ treeScrollbar._computeTrackOffset = function() { return Inspector.treeViewOffsetTop(); }
+ treeScrollbar._getContentLength = function() { return Inspector.treeViewScrollHeight(); }
+ treeScrollbar._getViewLength = function() { return document.getElementById("treeScrollArea").offsetHeight; }
+ treeScrollbar._canScroll = function() { return true; }
+
+ treeScrollbar.scrollTo = function(pos) {
+ Inspector.treeViewScrollTo(pos);
+ this.verticalHasScrolled();
+ }
+
+ treeScrollbar.verticalHasScrolled = function() {
+ var new_thumb_pos = this._thumbPositionForContentPosition(Inspector.treeViewOffsetTop());
+ this._thumbStart = new_thumb_pos;
+ this._thumb.style.top = new_thumb_pos + "px";
+ }
+
+ // much better AppleScrollArea reveal
+ AppleScrollArea.prototype.reveal = function(node) {
+ var offsetY = 0;
+ var obj = node;
+ do {
+ offsetY += obj.offsetTop;
+ obj = obj.offsetParent;
+ } while (obj && obj != this.content);
+
+ var offsetX = 0;
+ obj = node;
+ do {
+ offsetX += obj.offsetLeft;
+ obj = obj.offsetParent;
+ } while (obj && obj != this.content);
+
+ var top = this.content.scrollTop;
+ var height = this.viewHeight;
+ if ((top + height) < (offsetY + node.clientHeight))
+ this.verticalScrollTo(offsetY - height + node.clientHeight);
+ else if (top > offsetY)
+ this.verticalScrollTo(offsetY);
+
+ var left = this.content.scrollLeft;
+ var width = this.viewWidth;
+ if ((left + width) < (offsetX + node.clientWidth))
+ this.horizontalScrollTo(offsetX - width + node.clientWidth);
+ else if (left > offsetX)
+ this.horizontalScrollTo(offsetX);
+ };
+
+ window.addEventListener("resize", refreshScrollbars, false);
+}
+
+function refreshScrollbars() {
+ nodeContentsScrollArea.refresh();
+ styleRulesScrollArea.refresh();
+ stylePropertiesScrollArea.refresh();
+}
+
+var tabNames = ["node","metrics","style","javascript"];
+var currentPane = "node";
+var paneUpdateState = new Array();
+
+function switchPane(pane) {
+ currentPane = pane;
+ for (var i = 0; i < tabNames.length; i++) {
+ if (pane == tabNames[i]) {
+ document.getElementById(tabNames[i] + "Pane").style.removeProperty("display");
+ document.getElementById(tabNames[i] + "Button").className = "square selected";
+ } else {
+ document.getElementById(tabNames[i] + "Pane").style.display = "none";
+ document.getElementById(tabNames[i] + "Button").className = "square";
+ }
+ }
+
+ if (!paneUpdateState[pane]) {
+ eval("update" + pane.charAt(0).toUpperCase() + pane.substr(1) + "Pane()");
+ paneUpdateState[pane] = true;
+ }
+}
+
+function nodeTypeName(node) {
+ switch(node.nodeType) {
+ case 1: return "Element";
+ case 2: return "Attribute";
+ case 3: return "Text";
+ case 4: return "Character Data";
+ case 5: return "Entity Reference";
+ case 6: return "Entity";
+ case 7: return "Processing Instruction";
+ case 8: return "Comment";
+ case 9: return "Document";
+ case 10: return "Document Type";
+ case 11: return "Document Fragment";
+ case 12: return "Notation";
+ }
+ return "(unknown)";
+}
+
+function updatePanes() {
+ for (var i = 0; i < tabNames.length; i++)
+ paneUpdateState[tabNames[i]] = false;
+ eval("update" + currentPane.charAt(0).toUpperCase() + currentPane.substr(1) + "Pane()");
+ paneUpdateState[currentPane] = true;
+}
+
+function updateNodePane() {
+ var focusedNode = Inspector.focusedDOMNode();
+
+ if (focusedNode.nodeType == 3) {
+ document.getElementById("nodeNamespaceRow").style.display = "none";
+ document.getElementById("elementAttributes").style.display = "none";
+ document.getElementById("nodeContents").style.display = null;
+
+ document.getElementById("nodeContentsScrollview").textContent = focusedNode.nodeValue;
+ nodeContentsScrollArea.refresh();
+ } else if (focusedNode.nodeType == 1) {
+ document.getElementById("elementAttributes").style.display = null;
+ document.getElementById("nodeContents").style.display = null;
+
+ if (focusedNode.namespaceURI.length > 0) {
+ document.getElementById("nodeNamespace").textContent = focusedNode.namespaceURI;
+ document.getElementById("nodeNamespaceRow").style.display = null;
+ } else {
+ document.getElementById("nodeNamespaceRow").style.display = "none";
+ }
+
+ document.getElementById("nodeContentsScrollview").innerHTML = "<span class=\"disabled\">Loading...</span>";
+ nodeContentsScrollArea.refresh();
+
+ clearTimeout(nodeUpdateTimeout);
+ nodeUpdateTimeout = setTimeout("delayedNodePaneUpdate()", 250);
+ } else if (focusedNode.nodeType == 9) {
+ document.getElementById("nodeNamespaceRow").style.display = "none";
+ document.getElementById("elementAttributes").style.display = "none";
+ document.getElementById("nodeContents").style.display = "none";
+ }
+
+ document.getElementById("nodeType").textContent = nodeTypeName(focusedNode);
+ document.getElementById("nodeName").textContent = focusedNode.nodeName;
+}
+
+var nodeUpdateTimeout = null;
+function delayedNodePaneUpdate() {
+ var focusedNode = Inspector.focusedDOMNode();
+ var serializer = new XMLSerializer();
+ document.getElementById("nodeContentsScrollview").textContent = serializer.serializeToString(focusedNode);
+ nodeContentsScrollArea.refresh();
+}
+
+var styleRules = null;
+var selectedStyleRuleIndex = 0;
+var styleProperties = null;
+var expandedStyleShorthands = new Array();
+
+function updateStylePane() {
+ var focusedNode = Inspector.focusedDOMNode();
+ var rulesArea = document.getElementById("styleRulesScrollview");
+ var propertiesArea = document.getElementById("stylePropertiesTree");
+
+ rulesArea.innerHTML = "";
+ propertiesArea.innerHTML = "";
+
+ var propertyCount = new Array();
+
+ styleRules = focusedNode.ownerDocument.defaultView.getMatchedCSSRules(focusedNode, "");
+ styleProperties = new Array();
+
+ if (selectedStyleRuleIndex >= styleRules.length)
+ selectedStyleRuleIndex = (styleRules.length - 1);
+
+ for (var i = (styleRules.length - 1); i >= 0; --i) {
+ styleProperties[i] = new Array();
+
+ var row = document.createElement("div");
+ row.className = "row";
+ if (i == selectedStyleRuleIndex)
+ row.className += " focused";
+
+ var cell = document.createElement("div");
+ cell.className = "cell selector";
+ cell.title = styleRules[i].selectorText;
+ cell.textContent = cell.title;
+ row.appendChild(cell);
+
+ cell = document.createElement("div");
+ cell.className = "cell stylesheet";
+ cell.title = styleRules[i].parentStyleSheet.href;
+ cell.textContent = cell.title;
+ row.appendChild(cell);
+
+ row.styleRuleIndex = i;
+ row.addEventListener("click", styleRuleSelect, true);
+
+ var style = styleRules[i].style;
+ var stylePropertyLookup = new Array();
+ for (var j = 0; j < style.length; j++) {
+ var originalProperty = null;
+ var shorthand = style.getPropertyShorthand(style[j]);
+ if (shorthand != null)
+ originalProperty = stylePropertyLookup[shorthand];
+
+ if (originalProperty != null) {
+ originalProperty.subProperties.push(style[j]);
+ } else {
+ originalProperty = new Object();
+ originalProperty.style = style;
+ originalProperty.subProperties = new Array(style[j]);
+ originalProperty.unusedProperties = new Array();
+ if (shorthand != null && propertyCount[shorthand] > 0)
+ originalProperty.unusedProperties[shorthand] = true;
+ if (shorthand != null) {
+ if (propertyCount[shorthand] == null)
+ propertyCount[shorthand] = 1;
+ else
+ propertyCount[shorthand]++;
+ }
+ originalProperty.name = (shorthand != null ? shorthand : style[j]);
+ styleProperties[i].push(originalProperty);
+ if (shorthand != null)
+ stylePropertyLookup[originalProperty.name] = originalProperty;
+ }
+
+ if (propertyCount[style[j]] > 0)
+ originalProperty.unusedProperties[style[j]] = true;
+
+ if (propertyCount[style[j]] == null)
+ propertyCount[style[j]] = 1;
+ else
+ propertyCount[style[j]]++;
+ }
+
+ if (rulesArea.firstChild != null)
+ rulesArea.insertBefore(row, rulesArea.firstChild);
+ else
+ rulesArea.appendChild(row);
+ }
+
+ styleRulesScrollArea.refresh();
+ updateStyleProperties();
+}
+
+function styleRuleSelect(event) {
+ var row = document.getElementById("styleRulesScrollview").firstChild;
+ while (row) {
+ row.className = "row";
+ row = row.nextSibling;
+ }
+
+ row = event.currentTarget;
+ row.className = "row focused";
+
+ selectedStyleRuleIndex = row.styleRuleIndex;
+ updateStyleProperties();
+}
+
+function updateStyleProperties() {
+ var focusedNode = Inspector.focusedDOMNode();
+ var propertiesTree = document.getElementById("stylePropertiesTree");
+ propertiesTree.innerHTML = "";
+
+ for (var i = 0; i < styleProperties[selectedStyleRuleIndex].length; i++) {
+ var prop = styleProperties[selectedStyleRuleIndex][i];
+ var li = document.createElement("li");
+ if (prop.subProperties.length > 1) {
+ li.className = "hasChildren";
+ if (expandedStyleShorthands[prop.name])
+ li.className += " expanded";
+ li.shorthand = prop.name;
+ var button = document.createElement("button");
+ button.addEventListener("click", toggleStyleShorthand, false);
+ li.appendChild(button);
+ }
+
+ if (prop.unusedProperties[prop.name] == true)
+ li.className += " overloaded";
+
+ var span = document.createElement("span");
+ span.className = "property";
+ span.textContent = prop.name;
+ li.appendChild(span);
+
+ span = document.createElement("span");
+ span.className = "value";
+ span.title = prop.style.getPropertyValue(prop.name);
+ span.textContent = span.title;
+ li.appendChild(span);
+
+ propertiesTree.appendChild(li);
+
+ if (prop.subProperties.length > 1) {
+ var subTree = document.createElement("ul");
+ if (!expandedStyleShorthands[prop.name])
+ subTree.style.setProperty("display", "none", "");
+ for (var j = 0; j < prop.subProperties.length; j++) {
+ var li = document.createElement("li");
+ if (prop.style.isPropertyImplicit(prop.subProperties[j]) || prop.style.getPropertyValue(prop.subProperties[j]) == "initial")
+ li.className = "implicit";
+ if (prop.unusedProperties[prop.subProperties[j]] == true)
+ li.className += " overloaded";
+ var span = document.createElement("span");
+ span.className = "property";
+ span.textContent = prop.subProperties[j];
+ li.appendChild(span);
+
+ span = document.createElement("span");
+ span.className = "value";
+ span.title = prop.style.getPropertyValue(prop.subProperties[j]);
+ span.textContent = span.title;
+ li.appendChild(span);
+
+ subTree.appendChild(li);
+ }
+
+ propertiesTree.appendChild(subTree);
+ }
+ }
+
+ stylePropertiesScrollArea.refresh();
+}
+
+function toggleStyleShorthand(event) {
+ var li = event.currentTarget.parentNode;
+ if (li.className.indexOf("expanded") != -1) {
+ li.className = li.className.replace(/ expanded/, "");
+ li.nextSibling.style.setProperty("display", "none", "");
+ expandedStyleShorthands[li.shorthand] = false;
+ } else {
+ li.className += " expanded";
+ li.nextSibling.style.removeProperty("display");
+ expandedStyleShorthands[li.shorthand] = true;
+ }
+
+ stylePropertiesScrollArea.refresh();
+}
objects = {
/* Begin PBXBuildFile section */
+ 1C68F667095B5FC100C2984E /* webInspector in Resources */ = {isa = PBXBuildFile; fileRef = 1C68F640095B5FC100C2984E /* webInspector */; };
+ 1C68F668095B5FC100C2984E /* WebInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C68F65C095B5FC100C2984E /* WebInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 1C68F669095B5FC100C2984E /* WebInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C68F65D095B5FC100C2984E /* WebInspector.m */; };
+ 1C68F66A095B5FC100C2984E /* WebInspectorInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C68F65E095B5FC100C2984E /* WebInspectorInternal.h */; };
+ 1C68F66B095B5FC100C2984E /* WebInspectorOutlineView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C68F65F095B5FC100C2984E /* WebInspectorOutlineView.h */; };
+ 1C68F66C095B5FC100C2984E /* WebInspectorOutlineView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C68F660095B5FC100C2984E /* WebInspectorOutlineView.m */; };
+ 1C68F66D095B5FC100C2984E /* WebInspectorPanel.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C68F661095B5FC100C2984E /* WebInspectorPanel.h */; };
+ 1C68F66E095B5FC100C2984E /* WebInspectorPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C68F662095B5FC100C2984E /* WebInspectorPanel.m */; };
+ 1C68F66F095B5FC100C2984E /* WebNodeHighlight.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C68F663095B5FC100C2984E /* WebNodeHighlight.h */; };
+ 1C68F670095B5FC100C2984E /* WebNodeHighlight.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C68F664095B5FC100C2984E /* WebNodeHighlight.m */; };
+ 1C68F671095B5FC100C2984E /* WebNodeHighlightView.h in Headers */ = {isa = PBXBuildFile; fileRef = 1C68F665095B5FC100C2984E /* WebNodeHighlightView.h */; };
+ 1C68F672095B5FC100C2984E /* WebNodeHighlightView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C68F666095B5FC100C2984E /* WebNodeHighlightView.m */; };
220398770922D816009C3FFC /* WebBaseNetscapePluginViewInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2203984A0922D765009C3FFC /* WebBaseNetscapePluginViewInternal.h */; settings = {ATTRIBUTES = (); }; };
224100F3091818D900D2D266 /* WebPluginsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 224100F2091818D900D2D266 /* WebPluginsPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
224100F90918190100D2D266 /* WebPluginsPrivate.m in Sources */ = {isa = PBXBuildFile; fileRef = 224100F80918190100D2D266 /* WebPluginsPrivate.m */; };
/* End PBXBuildStyle section */
/* Begin PBXFileReference section */
+ 1C68F640095B5FC100C2984E /* webInspector */ = {isa = PBXFileReference; lastKnownFileType = folder; name = webInspector; path = WebInspector.subproj/webInspector; sourceTree = SOURCE_ROOT; };
+ 1C68F65C095B5FC100C2984E /* WebInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = WebInspector.h; path = WebInspector.subproj/WebInspector.h; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F65D095B5FC100C2984E /* WebInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = WebInspector.m; path = WebInspector.subproj/WebInspector.m; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F65E095B5FC100C2984E /* WebInspectorInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = WebInspectorInternal.h; path = WebInspector.subproj/WebInspectorInternal.h; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F65F095B5FC100C2984E /* WebInspectorOutlineView.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = WebInspectorOutlineView.h; path = WebInspector.subproj/WebInspectorOutlineView.h; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F660095B5FC100C2984E /* WebInspectorOutlineView.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = WebInspectorOutlineView.m; path = WebInspector.subproj/WebInspectorOutlineView.m; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F661095B5FC100C2984E /* WebInspectorPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = WebInspectorPanel.h; path = WebInspector.subproj/WebInspectorPanel.h; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F662095B5FC100C2984E /* WebInspectorPanel.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = WebInspectorPanel.m; path = WebInspector.subproj/WebInspectorPanel.m; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F663095B5FC100C2984E /* WebNodeHighlight.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = WebNodeHighlight.h; path = WebInspector.subproj/WebNodeHighlight.h; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; wrapsLines = 0; };
+ 1C68F664095B5FC100C2984E /* WebNodeHighlight.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = WebNodeHighlight.m; path = WebInspector.subproj/WebNodeHighlight.m; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F665095B5FC100C2984E /* WebNodeHighlightView.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; name = WebNodeHighlightView.h; path = WebInspector.subproj/WebNodeHighlightView.h; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
+ 1C68F666095B5FC100C2984E /* WebNodeHighlightView.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; name = WebNodeHighlightView.m; path = WebInspector.subproj/WebNodeHighlightView.m; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 0; };
2203984A0922D765009C3FFC /* WebBaseNetscapePluginViewInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebBaseNetscapePluginViewInternal.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
224100F2091818D900D2D266 /* WebPluginsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = WebPluginsPrivate.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
224100F80918190100D2D266 /* WebPluginsPrivate.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = WebPluginsPrivate.m; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
F5EBC45202134BB601CA1520 /* Plugins */,
F5B36B400281DE87018635CB /* WebCore Support */,
9C7CABBB0190A37C0ECA16EA /* WebView */,
+ 1C68F63F095B5F9C00C2984E /* Web Inspector */,
F7EBEE5903F9DB2203CA0DE6 /* Carbon Support */,
089C1665FE841158C02AAC07 /* Resources */,
0867D69AFE84028FC02AAC07 /* Frameworks and Libraries */,
name = Resources;
sourceTree = SOURCE_ROOT;
};
+ 1C68F63F095B5F9C00C2984E /* Web Inspector */ = {
+ isa = PBXGroup;
+ children = (
+ 1C68F640095B5FC100C2984E /* webInspector */,
+ 1C68F65C095B5FC100C2984E /* WebInspector.h */,
+ 1C68F65D095B5FC100C2984E /* WebInspector.m */,
+ 1C68F65E095B5FC100C2984E /* WebInspectorInternal.h */,
+ 1C68F65F095B5FC100C2984E /* WebInspectorOutlineView.h */,
+ 1C68F660095B5FC100C2984E /* WebInspectorOutlineView.m */,
+ 1C68F661095B5FC100C2984E /* WebInspectorPanel.h */,
+ 1C68F662095B5FC100C2984E /* WebInspectorPanel.m */,
+ 1C68F663095B5FC100C2984E /* WebNodeHighlight.h */,
+ 1C68F664095B5FC100C2984E /* WebNodeHighlight.m */,
+ 1C68F665095B5FC100C2984E /* WebNodeHighlightView.h */,
+ 1C68F666095B5FC100C2984E /* WebNodeHighlightView.m */,
+ );
+ name = "Web Inspector";
+ path = WebView.subproj;
+ sourceTree = "<group>";
+ };
254DC334016E1D3F0ECA149E /* Misc */ = {
isa = PBXGroup;
children = (
22F219CC08D236730030E078 /* WebBackForwardListPrivate.h in Headers */,
ABDDF20D08EB0DDC001E1241 /* WebDownloadInternal.h in Headers */,
224100F3091818D900D2D266 /* WebPluginsPrivate.h in Headers */,
+ 1C68F668095B5FC100C2984E /* WebInspector.h in Headers */,
+ 1C68F66A095B5FC100C2984E /* WebInspectorInternal.h in Headers */,
+ 1C68F66B095B5FC100C2984E /* WebInspectorOutlineView.h in Headers */,
+ 1C68F66D095B5FC100C2984E /* WebInspectorPanel.h in Headers */,
+ 1C68F66F095B5FC100C2984E /* WebNodeHighlight.h in Headers */,
+ 1C68F671095B5FC100C2984E /* WebNodeHighlightView.h in Headers */,
65C7F42C0979DE640022E453 /* WebPageBridge.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
939810B80824BF01008DF038 /* missing_image.tiff in Resources */,
939810B90824BF01008DF038 /* WebViewEditingContextMenu.nib in Resources */,
939810BA0824BF01008DF038 /* IDNScriptWhiteList.txt in Resources */,
+ 1C68F667095B5FC100C2984E /* webInspector in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
7E6FEF0208985A3E00C44C3F /* WebDefaultScriptDebugDelegate.m in Sources */,
7E6FEF0908985A7200C44C3F /* WebScriptDebugDelegate.m in Sources */,
224100F90918190100D2D266 /* WebPluginsPrivate.m in Sources */,
+ 1C68F669095B5FC100C2984E /* WebInspector.m in Sources */,
+ 1C68F66C095B5FC100C2984E /* WebInspectorOutlineView.m in Sources */,
+ 1C68F66E095B5FC100C2984E /* WebInspectorPanel.m in Sources */,
+ 1C68F670095B5FC100C2984E /* WebNodeHighlight.m in Sources */,
+ 1C68F672095B5FC100C2984E /* WebNodeHighlightView.m in Sources */,
65C7F42D0979DE640022E453 /* WebPageBridge.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
- (void)webView:(WebView *)sender drawFooterInRect:(NSRect)rect;
- (void)webView:(WebView *)webView addMessageToConsole:(NSDictionary *)message;
+- (NSView *)webView:(WebView *)webView plugInViewWithArguments:(NSDictionary *)arguments;
+
// regions is an dictionary whose keys are regions label and values are arrays of WebDashboardRegions.
- (void)webView:(WebView *)webView dashboardRegionsChanged:(NSDictionary *)regions;
#import <WebKit/WebIconDatabase.h>
#import <WebKit/WebKitErrors.h>
#import <WebKit/WebKitLogging.h>
+#import <WebKit/WebLocalizableStrings.h>
#import <WebKit/WebKitNSStringExtras.h>
#import <WebKit/WebKitStatisticsPrivate.h>
#import <WebKit/WebNSDataExtras.h>
#import <WebKit/WebTextRenderer.h>
#import <WebKit/WebUIDelegate.h>
#import <WebKit/WebUIDelegatePrivate.h>
+#import <WebKit/WebInspector.h>
#import <WebKitSystemInterface.h>
#import <WebCore/WebCoreEncodings.h>
}
}
+#ifdef NDEBUG
+ BOOL enableInspectElement = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitEnableInspectElementContextMenuItem"];
+#else
+ BOOL enableInspectElement = YES; // always enable in debug builds
+#endif
+
+ // optionally add the Inspect Element menu item it if preference is set or in debug builds
+ // and only showing the menu item if we are working with a WebHTMLView
+ WebFrame *webFrame = [element objectForKey:WebElementFrameKey];
+ if (enableInspectElement && [[[webFrame frameView] documentView] isKindOfClass:[WebHTMLView class]]) {
+ if (!menu)
+ menu = [[[NSMenu alloc] init] autorelease];
+ else if ([menu numberOfItems])
+ [menu addItem:[NSMenuItem separatorItem]];
+ NSMenuItem *menuItem = [[[NSMenuItem alloc] init] autorelease];
+ [menuItem setAction:@selector(_inspectElement:)];
+ [menuItem setTitle:UI_STRING("Inspect Element", "Inspect Element context menu item")];
+ [menuItem setRepresentedObject:element];
+ [menu addItem:menuItem];
+ }
+
return menu;
}
_private->dragCaretBridge = nil;
}
+- (void)_inspectElement:(id)sender
+{
+ NSDictionary *element = [sender representedObject];
+ WebFrame *frame = [element objectForKey:WebElementFrameKey];
+ DOMNode *node = [element objectForKey:WebElementDOMNodeKey];
+ if (!node || !frame)
+ return;
+
+ if ([node nodeType] != DOM_ELEMENT_NODE || [node nodeType] != DOM_DOCUMENT_NODE)
+ node = [node parentNode];
+
+ WebInspector *inspector = [WebInspector sharedWebInspector];
+ [inspector setWebFrame:frame];
+ [inspector setFocusedDOMNode:node];
+
+ node = [node parentNode];
+ node = [node parentNode];
+ if (node) // set the root node to something retivally close to the focused node
+ [inspector setRootDOMNode:node];
+
+ [inspector showWindow:nil];
+}
@end
@implementation WebView (WebIBActions)