diff options
Diffstat (limited to 'WebKit/mac/WebCoreSupport/WebInspectorClient.mm')
-rw-r--r-- | WebKit/mac/WebCoreSupport/WebInspectorClient.mm | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/WebKit/mac/WebCoreSupport/WebInspectorClient.mm b/WebKit/mac/WebCoreSupport/WebInspectorClient.mm new file mode 100644 index 0000000..03d85ae --- /dev/null +++ b/WebKit/mac/WebCoreSupport/WebInspectorClient.mm @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 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 "WebInspectorClient.h" + +#import "WebFrameInternal.h" +#import "WebFrameView.h" +#import "WebInspector.h" +#import "WebLocalizableStrings.h" +#import "WebNodeHighlight.h" +#import "WebUIDelegate.h" +#import "WebViewInternal.h" + +#import <WebCore/InspectorController.h> +#import <WebCore/Page.h> + +#import <WebKit/DOMExtensions.h> + +#import <WebKitSystemInterface.h> + +using namespace WebCore; + +@interface WebInspectorWindowController : NSWindowController <NSWindowDelegate> { +@private + WebView *_inspectedWebView; + WebView *_webView; + WebNodeHighlight *_currentHighlight; + BOOL _attachedToInspectedWebView; + BOOL _shouldAttach; + BOOL _visible; + BOOL _movingWindows; +} +- (id)initWithInspectedWebView:(WebView *)webView; +- (BOOL)inspectorVisible; +- (WebView *)webView; +- (void)attach; +- (void)detach; +- (void)setAttachedWindowHeight:(unsigned)height; +- (void)highlightNode:(DOMNode *)node; +- (void)hideHighlight; +@end + +#pragma mark - + +WebInspectorClient::WebInspectorClient(WebView *webView) +: m_webView(webView) +{ +} + +void WebInspectorClient::inspectorDestroyed() +{ + [[m_windowController.get() webView] close]; + delete this; +} + +Page* WebInspectorClient::createPage() +{ + if (!m_windowController) + m_windowController.adoptNS([[WebInspectorWindowController alloc] initWithInspectedWebView:m_webView]); + + return core([m_windowController.get() webView]); +} + +String WebInspectorClient::localizedStringsURL() +{ + NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"localizedStrings" ofType:@"js"]; + if (path) + return [[NSURL fileURLWithPath:path] absoluteString]; + return String(); +} + +void WebInspectorClient::showWindow() +{ + updateWindowTitle(); + [m_windowController.get() showWindow:nil]; +} + +void WebInspectorClient::closeWindow() +{ + [m_windowController.get() close]; +} + +void WebInspectorClient::attachWindow() +{ + [m_windowController.get() attach]; +} + +void WebInspectorClient::detachWindow() +{ + [m_windowController.get() detach]; +} + +void WebInspectorClient::setAttachedWindowHeight(unsigned height) +{ + [m_windowController.get() setAttachedWindowHeight:height]; +} + +void WebInspectorClient::highlight(Node* node) +{ + [m_windowController.get() highlightNode:kit(node)]; +} + +void WebInspectorClient::hideHighlight() +{ + [m_windowController.get() hideHighlight]; +} + +void WebInspectorClient::inspectedURLChanged(const String& newURL) +{ + m_inspectedURL = newURL; + updateWindowTitle(); +} + +void WebInspectorClient::updateWindowTitle() const +{ + NSString *title = [NSString stringWithFormat:UI_STRING("Web Inspector — %@", "Web Inspector window title"), (NSString *)m_inspectedURL]; + [[m_windowController.get() window] setTitle:title]; +} + +#pragma mark - + +#define WebKitInspectorAttachedKey @"WebKitInspectorAttached" +#define WebKitInspectorAttachedViewHeightKey @"WebKitInspectorAttachedViewHeight" + +@implementation WebInspectorWindowController +- (id)init +{ + if (![super initWithWindow:nil]) + return nil; + + // Keep preferences separate from the rest of the client, making sure we are using expected preference values. + // One reason this is good is that it keeps the inspector out of history via "private browsing". + + WebPreferences *preferences = [[WebPreferences alloc] init]; + [preferences setAutosaves:NO]; + [preferences setPrivateBrowsingEnabled:YES]; + [preferences setLoadsImagesAutomatically:YES]; + [preferences setAuthorAndUserStylesEnabled:YES]; + [preferences setJavaScriptEnabled:YES]; + [preferences setAllowsAnimatedImages:YES]; + [preferences setPlugInsEnabled:NO]; + [preferences setJavaEnabled:NO]; + [preferences setUserStyleSheetEnabled:NO]; + [preferences setTabsToLinks:NO]; + [preferences setMinimumFontSize:0]; + [preferences setMinimumLogicalFontSize:9]; + + _webView = [[WebView alloc] init]; + [_webView setPreferences:preferences]; + [_webView setDrawsBackground:NO]; + [_webView setProhibitsMainFrameScrolling:YES]; + [_webView setUIDelegate:self]; + + [preferences release]; + + NSNumber *attached = [[NSUserDefaults standardUserDefaults] objectForKey:WebKitInspectorAttachedKey]; + ASSERT(!attached || [attached isKindOfClass:[NSNumber class]]); + _shouldAttach = attached ? [attached boolValue] : YES; + + NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"]; + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path]]; + [[_webView mainFrame] loadRequest:request]; + [request release]; + + [self setWindowFrameAutosaveName:@"Web Inspector 2"]; + return self; +} + +- (id)initWithInspectedWebView:(WebView *)webView +{ + if (![self init]) + return nil; + + // Don't retain to avoid a circular reference + _inspectedWebView = webView; + return self; +} + +- (void)dealloc +{ + ASSERT(!_currentHighlight); + [_webView release]; + [super dealloc]; +} + +#pragma mark - + +- (BOOL)inspectorVisible +{ + return _visible; +} + +- (WebView *)webView +{ + return _webView; +} + +- (NSWindow *)window +{ + NSWindow *window = [super window]; + if (window) + return window; + + NSUInteger styleMask = (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask); + +#ifndef BUILDING_ON_TIGER + styleMask |= NSTexturedBackgroundWindowMask; +#endif + + window = [[NSWindow alloc] initWithContentRect:NSMakeRect(60.0, 200.0, 750.0, 650.0) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; + [window setDelegate:self]; + [window setMinSize:NSMakeSize(400.0, 400.0)]; + +#ifndef BUILDING_ON_TIGER + [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; + [window setContentBorderThickness:55. forEdge:NSMaxYEdge]; + + WKNSWindowMakeBottomCornersSquare(window); +#endif + + [self setWindow:window]; + [window release]; + + return window; +} + +#pragma mark - + +- (BOOL)windowShouldClose:(id)sender +{ + _visible = NO; + + [_inspectedWebView page]->inspectorController()->setWindowVisible(false); + + [self hideHighlight]; + + return YES; +} + +- (void)close +{ + if (!_visible) + return; + + _visible = NO; + + if (!_movingWindows) + [_inspectedWebView page]->inspectorController()->setWindowVisible(false); + + [self hideHighlight]; + + if (_attachedToInspectedWebView) { + if ([_inspectedWebView _isClosed]) + return; + + [_webView removeFromSuperview]; + + WebFrameView *frameView = [[_inspectedWebView mainFrame] frameView]; + NSRect frameViewRect = [frameView frame]; + + // Setting the height based on the previous height is done to work with + // Safari's find banner. This assumes the previous height is the Y origin. + frameViewRect.size.height += NSMinY(frameViewRect); + frameViewRect.origin.y = 0.0; + + [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [frameView setFrame:frameViewRect]; + + [_inspectedWebView displayIfNeeded]; + } else + [super close]; +} + +- (IBAction)showWindow:(id)sender +{ + if (_visible) { + if (!_attachedToInspectedWebView) + [super showWindow:sender]; // call super so the window will be ordered front if needed + return; + } + + _visible = YES; + + if (_shouldAttach) { + WebFrameView *frameView = [[_inspectedWebView mainFrame] frameView]; + + [_webView removeFromSuperview]; + [_inspectedWebView addSubview:_webView positioned:NSWindowBelow relativeTo:(NSView *)frameView]; + + [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMaxYMargin)]; + [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMinYMargin)]; + + _attachedToInspectedWebView = YES; + + [self setAttachedWindowHeight:[[NSUserDefaults standardUserDefaults] integerForKey:WebKitInspectorAttachedViewHeightKey]]; + } else { + _attachedToInspectedWebView = NO; + + NSView *contentView = [[self window] contentView]; + [_webView setFrame:[contentView frame]]; + [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [_webView removeFromSuperview]; + [contentView addSubview:_webView]; + + [super showWindow:nil]; + } + + [_inspectedWebView page]->inspectorController()->setWindowVisible(true, _shouldAttach); +} + +#pragma mark - + +- (void)attach +{ + if (_attachedToInspectedWebView) + return; + + _shouldAttach = YES; + _movingWindows = YES; + + [self close]; + [self showWindow:nil]; + + _movingWindows = NO; + + [[NSUserDefaults standardUserDefaults] setBool:YES forKey:WebKitInspectorAttachedKey]; +} + +- (void)detach +{ + if (!_attachedToInspectedWebView) + return; + + _shouldAttach = NO; + _movingWindows = YES; + + [self close]; + [self showWindow:nil]; + + _movingWindows = NO; + + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:WebKitInspectorAttachedKey]; +} + +- (void)setAttachedWindowHeight:(unsigned)height +{ + [[NSUserDefaults standardUserDefaults] setInteger:height forKey:WebKitInspectorAttachedViewHeightKey]; + + if (!_attachedToInspectedWebView) + return; + + WebFrameView *frameView = [[_inspectedWebView mainFrame] frameView]; + NSRect frameViewRect = [frameView frame]; + + CGFloat attachedHeight = round(MAX(250.0, MIN(height, (NSHeight([_inspectedWebView frame]) * 0.75)))); + + // Setting the height based on the difference is done to work with + // Safari's find banner. This assumes the previous height is the Y origin. + CGFloat heightDifference = (NSMinY(frameViewRect) - attachedHeight); + frameViewRect.size.height += heightDifference; + frameViewRect.origin.y = attachedHeight; + + [_webView setFrame:NSMakeRect(0.0, 0.0, NSWidth(frameViewRect), attachedHeight)]; + [frameView setFrame:frameViewRect]; +} + +#pragma mark - + +- (void)highlightNode:(DOMNode *)node +{ + // The scrollview's content view stays around between page navigations, so target it + NSView *view = [[[[[_inspectedWebView mainFrame] frameView] documentView] enclosingScrollView] contentView]; + if (![view window]) + return; // skip the highlight if we have no window (e.g. hidden tab) + + if (!_currentHighlight) { + _currentHighlight = [[WebNodeHighlight alloc] initWithTargetView:view inspectorController:[_inspectedWebView page]->inspectorController()]; + [_currentHighlight setDelegate:self]; + [_currentHighlight attach]; + } else + [[_currentHighlight highlightView] setNeedsDisplay:YES]; +} + +- (void)hideHighlight +{ + [_currentHighlight detach]; + [_currentHighlight setDelegate:nil]; + [_currentHighlight release]; + _currentHighlight = nil; +} + +#pragma mark - +#pragma mark WebNodeHighlight delegate + +- (void)didAttachWebNodeHighlight:(WebNodeHighlight *)highlight +{ + [_inspectedWebView setCurrentNodeHighlight:highlight]; +} + +- (void)willDetachWebNodeHighlight:(WebNodeHighlight *)highlight +{ + [_inspectedWebView setCurrentNodeHighlight:nil]; +} + +#pragma mark - +#pragma mark UI delegate + +- (NSUInteger)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo +{ + return WebDragDestinationActionNone; +} + +#pragma mark - + +// These methods can be used by UI elements such as menu items and toolbar buttons when the inspector is the key window. + +// This method is really only implemented to keep any UI elements enabled. +- (void)showWebInspector:(id)sender +{ + [[_inspectedWebView inspector] show:sender]; +} + +- (void)showErrorConsole:(id)sender +{ + [[_inspectedWebView inspector] showConsole:sender]; +} + +- (void)toggleDebuggingJavaScript:(id)sender +{ + [[_inspectedWebView inspector] toggleDebuggingJavaScript:sender]; +} + +- (void)toggleProfilingJavaScript:(id)sender +{ + [[_inspectedWebView inspector] toggleProfilingJavaScript:sender]; +} + +- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item +{ + BOOL isMenuItem = [(id)item isKindOfClass:[NSMenuItem class]]; + if ([item action] == @selector(toggleDebuggingJavaScript:) && isMenuItem) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([[_inspectedWebView inspector] isDebuggingJavaScript]) + [menuItem setTitle:UI_STRING("Stop Debugging JavaScript", "title for Stop Debugging JavaScript menu item")]; + else + [menuItem setTitle:UI_STRING("Start Debugging JavaScript", "title for Start Debugging JavaScript menu item")]; + } else if ([item action] == @selector(toggleProfilingJavaScript:) && isMenuItem) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([[_inspectedWebView inspector] isProfilingJavaScript]) + [menuItem setTitle:UI_STRING("Stop Profiling JavaScript", "title for Stop Profiling JavaScript menu item")]; + else + [menuItem setTitle:UI_STRING("Start Profiling JavaScript", "title for Start Profiling JavaScript menu item")]; + } + + return YES; +} + +@end |