Home | History | Annotate | Download | only in WebCoreSupport
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #import "WebInspectorClient.h"
     30 
     31 #import "DOMNodeInternal.h"
     32 #import "WebDelegateImplementationCaching.h"
     33 #import "WebFrameInternal.h"
     34 #import "WebFrameView.h"
     35 #import "WebInspector.h"
     36 #import "WebInspectorPrivate.h"
     37 #import "WebInspectorFrontend.h"
     38 #import "WebLocalizableStringsInternal.h"
     39 #import "WebNodeHighlighter.h"
     40 #import "WebUIDelegate.h"
     41 #import "WebViewInternal.h"
     42 #import <WebCore/InspectorController.h>
     43 #import <WebCore/Page.h>
     44 #import <WebKit/DOMExtensions.h>
     45 #import <WebKitSystemInterface.h>
     46 #import <wtf/PassOwnPtr.h>
     47 
     48 using namespace WebCore;
     49 
     50 @interface WebInspectorWindowController : NSWindowController <NSWindowDelegate> {
     51 @private
     52     WebView *_inspectedWebView;
     53     WebView *_webView;
     54     WebInspectorFrontendClient* _frontendClient;
     55     WebInspectorClient* _inspectorClient;
     56     BOOL _attachedToInspectedWebView;
     57     BOOL _shouldAttach;
     58     BOOL _visible;
     59     BOOL _destroyingInspectorView;
     60 }
     61 - (id)initWithInspectedWebView:(WebView *)webView;
     62 - (WebView *)webView;
     63 - (void)attach;
     64 - (void)detach;
     65 - (BOOL)attached;
     66 - (void)setFrontendClient:(WebInspectorFrontendClient*)frontendClient;
     67 - (void)setInspectorClient:(WebInspectorClient*)inspectorClient;
     68 - (WebInspectorClient*)inspectorClient;
     69 - (void)setAttachedWindowHeight:(unsigned)height;
     70 - (void)destroyInspectorView:(bool)notifyInspectorController;
     71 @end
     72 
     73 
     74 // MARK: -
     75 
     76 WebInspectorClient::WebInspectorClient(WebView *webView)
     77     : m_webView(webView)
     78     , m_highlighter(AdoptNS, [[WebNodeHighlighter alloc] initWithInspectedWebView:webView])
     79     , m_frontendPage(0)
     80 {
     81 }
     82 
     83 void WebInspectorClient::inspectorDestroyed()
     84 {
     85     delete this;
     86 }
     87 
     88 void WebInspectorClient::openInspectorFrontend(InspectorController* inspectorController)
     89 {
     90     RetainPtr<WebInspectorWindowController> windowController(AdoptNS, [[WebInspectorWindowController alloc] initWithInspectedWebView:m_webView]);
     91     [windowController.get() setInspectorClient:this];
     92 
     93     m_frontendPage = core([windowController.get() webView]);
     94     OwnPtr<WebInspectorFrontendClient> frontendClient = adoptPtr(new WebInspectorFrontendClient(m_webView, windowController.get(), inspectorController, m_frontendPage, createFrontendSettings()));
     95     RetainPtr<WebInspectorFrontend> webInspectorFrontend(AdoptNS, [[WebInspectorFrontend alloc] initWithFrontendClient:frontendClient.get()]);
     96     [[m_webView inspector] setFrontend:webInspectorFrontend.get()];
     97     m_frontendPage->inspectorController()->setInspectorFrontendClient(frontendClient.release());
     98 }
     99 
    100 void WebInspectorClient::highlight(Node* node)
    101 {
    102     [m_highlighter.get() highlightNode:kit(node)];
    103 }
    104 
    105 void WebInspectorClient::hideHighlight()
    106 {
    107     [m_highlighter.get() hideHighlight];
    108 }
    109 
    110 WebInspectorFrontendClient::WebInspectorFrontendClient(WebView* inspectedWebView, WebInspectorWindowController* windowController, InspectorController* inspectorController, Page* frontendPage, WTF::PassOwnPtr<Settings> settings)
    111     : InspectorFrontendClientLocal(inspectorController,  frontendPage, settings)
    112     , m_inspectedWebView(inspectedWebView)
    113     , m_windowController(windowController)
    114 {
    115     [windowController setFrontendClient:this];
    116 }
    117 
    118 void WebInspectorFrontendClient::frontendLoaded()
    119 {
    120     [m_windowController.get() showWindow:nil];
    121     if ([m_windowController.get() attached])
    122         restoreAttachedWindowHeight();
    123 
    124     InspectorFrontendClientLocal::frontendLoaded();
    125 
    126     WebFrame *frame = [m_inspectedWebView mainFrame];
    127 
    128     WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(m_inspectedWebView);
    129     if (implementations->didClearInspectorWindowObjectForFrameFunc)
    130         CallFrameLoadDelegate(implementations->didClearInspectorWindowObjectForFrameFunc, m_inspectedWebView,
    131                               @selector(webView:didClearInspectorWindowObject:forFrame:), [frame windowObject], frame);
    132 
    133     bool attached = [m_windowController.get() attached];
    134     setAttachedWindow(attached);
    135 }
    136 
    137 String WebInspectorFrontendClient::localizedStringsURL()
    138 {
    139     NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"localizedStrings" ofType:@"js"];
    140     if (path)
    141         return [[NSURL fileURLWithPath:path] absoluteString];
    142     return String();
    143 }
    144 
    145 String WebInspectorFrontendClient::hiddenPanels()
    146 {
    147     NSString *hiddenPanels = [[NSUserDefaults standardUserDefaults] stringForKey:@"WebKitInspectorHiddenPanels"];
    148     if (hiddenPanels)
    149         return hiddenPanels;
    150     return String();
    151 }
    152 
    153 void WebInspectorFrontendClient::bringToFront()
    154 {
    155     updateWindowTitle();
    156     [m_windowController.get() showWindow:nil];
    157 }
    158 
    159 void WebInspectorFrontendClient::closeWindow()
    160 {
    161     [m_windowController.get() destroyInspectorView:true];
    162 }
    163 
    164 void WebInspectorFrontendClient::disconnectFromBackend()
    165 {
    166     [m_windowController.get() destroyInspectorView:false];
    167 }
    168 
    169 void WebInspectorFrontendClient::attachWindow()
    170 {
    171     if ([m_windowController.get() attached])
    172         return;
    173     [m_windowController.get() attach];
    174     restoreAttachedWindowHeight();
    175 }
    176 
    177 void WebInspectorFrontendClient::detachWindow()
    178 {
    179     [m_windowController.get() detach];
    180 }
    181 
    182 void WebInspectorFrontendClient::setAttachedWindowHeight(unsigned height)
    183 {
    184     [m_windowController.get() setAttachedWindowHeight:height];
    185 }
    186 
    187 void WebInspectorFrontendClient::inspectedURLChanged(const String& newURL)
    188 {
    189     m_inspectedURL = newURL;
    190     updateWindowTitle();
    191 }
    192 
    193 void WebInspectorFrontendClient::saveSessionSetting(const String& key, const String& value)
    194 {
    195     WebInspectorClient* client = [m_windowController.get() inspectorClient];
    196     if (client)
    197         client->saveSessionSetting(key, value);
    198 }
    199 
    200 void WebInspectorFrontendClient::loadSessionSetting(const String& key, String* value)
    201 {
    202     WebInspectorClient* client = [m_windowController.get() inspectorClient];
    203     if (client)
    204         client->loadSessionSetting(key, value);
    205 }
    206 
    207 void WebInspectorFrontendClient::updateWindowTitle() const
    208 {
    209     NSString *title = [NSString stringWithFormat:UI_STRING_INTERNAL("Web Inspector  %@", "Web Inspector window title"), (NSString *)m_inspectedURL];
    210     [[m_windowController.get() window] setTitle:title];
    211 }
    212 
    213 
    214 // MARK: -
    215 
    216 @implementation WebInspectorWindowController
    217 - (id)init
    218 {
    219     if (!(self = [super initWithWindow:nil]))
    220         return nil;
    221 
    222     // Keep preferences separate from the rest of the client, making sure we are using expected preference values.
    223 
    224     WebPreferences *preferences = [[WebPreferences alloc] init];
    225     [preferences setAutosaves:NO];
    226     [preferences setLoadsImagesAutomatically:YES];
    227     [preferences setAuthorAndUserStylesEnabled:YES];
    228     [preferences setJavaScriptEnabled:YES];
    229     [preferences setAllowsAnimatedImages:YES];
    230     [preferences setPlugInsEnabled:NO];
    231     [preferences setJavaEnabled:NO];
    232     [preferences setUserStyleSheetEnabled:NO];
    233     [preferences setTabsToLinks:NO];
    234     [preferences setMinimumFontSize:0];
    235     [preferences setMinimumLogicalFontSize:9];
    236 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
    237     [preferences setFixedFontFamily:@"Menlo"];
    238     [preferences setDefaultFixedFontSize:11];
    239 #else
    240     [preferences setFixedFontFamily:@"Monaco"];
    241     [preferences setDefaultFixedFontSize:10];
    242 #endif
    243 
    244     _webView = [[WebView alloc] init];
    245     [_webView setPreferences:preferences];
    246     [_webView setDrawsBackground:NO];
    247     [_webView setProhibitsMainFrameScrolling:YES];
    248     [_webView setUIDelegate:self];
    249 
    250     [preferences release];
    251 
    252     NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"];
    253     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path]];
    254     [[_webView mainFrame] loadRequest:request];
    255     [request release];
    256 
    257     [self setWindowFrameAutosaveName:@"Web Inspector 2"];
    258     return self;
    259 }
    260 
    261 - (id)initWithInspectedWebView:(WebView *)webView
    262 {
    263     if (!(self = [self init]))
    264         return nil;
    265 
    266     // Don't retain to avoid a circular reference.
    267     _inspectedWebView = webView;
    268     return self;
    269 }
    270 
    271 - (void)dealloc
    272 {
    273     [_webView release];
    274     [super dealloc];
    275 }
    276 
    277 // MARK: -
    278 
    279 - (WebView *)webView
    280 {
    281     return _webView;
    282 }
    283 
    284 - (NSWindow *)window
    285 {
    286     NSWindow *window = [super window];
    287     if (window)
    288         return window;
    289 
    290     NSUInteger styleMask = (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask);
    291 
    292 #ifndef BUILDING_ON_TIGER
    293     styleMask |= NSTexturedBackgroundWindowMask;
    294 #endif
    295 
    296     window = [[NSWindow alloc] initWithContentRect:NSMakeRect(60.0, 200.0, 750.0, 650.0) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO];
    297     [window setDelegate:self];
    298     [window setMinSize:NSMakeSize(400.0, 400.0)];
    299 
    300 #ifndef BUILDING_ON_TIGER
    301     [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
    302     [window setContentBorderThickness:55. forEdge:NSMaxYEdge];
    303 
    304     WKNSWindowMakeBottomCornersSquare(window);
    305 #endif
    306 
    307     [self setWindow:window];
    308     [window release];
    309 
    310     return window;
    311 }
    312 
    313 // MARK: -
    314 
    315 - (BOOL)windowShouldClose:(id)sender
    316 {
    317     [self destroyInspectorView:true];
    318 
    319     return YES;
    320 }
    321 
    322 - (void)close
    323 {
    324     if (!_visible)
    325         return;
    326 
    327     _visible = NO;
    328 
    329     if (_attachedToInspectedWebView) {
    330         if ([_inspectedWebView _isClosed])
    331             return;
    332 
    333         [_webView removeFromSuperview];
    334 
    335         WebFrameView *frameView = [[_inspectedWebView mainFrame] frameView];
    336         NSRect frameViewRect = [frameView frame];
    337 
    338         // Setting the height based on the previous height is done to work with
    339         // Safari's find banner. This assumes the previous height is the Y origin.
    340         frameViewRect.size.height += NSMinY(frameViewRect);
    341         frameViewRect.origin.y = 0.0;
    342 
    343         [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
    344         [frameView setFrame:frameViewRect];
    345 
    346         [_inspectedWebView displayIfNeeded];
    347     } else
    348         [super close];
    349 }
    350 
    351 - (IBAction)showWindow:(id)sender
    352 {
    353     if (_visible) {
    354         if (!_attachedToInspectedWebView)
    355             [super showWindow:sender]; // call super so the window will be ordered front if needed
    356         return;
    357     }
    358 
    359     _visible = YES;
    360 
    361     _shouldAttach = _inspectorClient->inspectorStartsAttached();
    362 
    363     if (_shouldAttach && !_frontendClient->canAttachWindow())
    364         _shouldAttach = NO;
    365 
    366     if (_shouldAttach) {
    367         WebFrameView *frameView = [[_inspectedWebView mainFrame] frameView];
    368 
    369         [_webView removeFromSuperview];
    370         [_inspectedWebView addSubview:_webView positioned:NSWindowBelow relativeTo:(NSView *)frameView];
    371 
    372         [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMaxYMargin)];
    373         [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMinYMargin)];
    374 
    375         _attachedToInspectedWebView = YES;
    376     } else {
    377         _attachedToInspectedWebView = NO;
    378 
    379         NSView *contentView = [[self window] contentView];
    380         [_webView setFrame:[contentView frame]];
    381         [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
    382         [_webView removeFromSuperview];
    383         [contentView addSubview:_webView];
    384 
    385         [super showWindow:nil];
    386     }
    387 }
    388 
    389 // MARK: -
    390 
    391 - (void)attach
    392 {
    393     if (_attachedToInspectedWebView)
    394         return;
    395 
    396     _inspectorClient->setInspectorStartsAttached(true);
    397 
    398     [self close];
    399     [self showWindow:nil];
    400 }
    401 
    402 - (void)detach
    403 {
    404     if (!_attachedToInspectedWebView)
    405         return;
    406 
    407     _inspectorClient->setInspectorStartsAttached(false);
    408 
    409     [self close];
    410     [self showWindow:nil];
    411 }
    412 
    413 - (BOOL)attached
    414 {
    415     return _attachedToInspectedWebView;
    416 }
    417 
    418 - (void)setFrontendClient:(WebInspectorFrontendClient*)frontendClient
    419 {
    420     _frontendClient = frontendClient;
    421 }
    422 
    423 - (void)setInspectorClient:(WebInspectorClient*)inspectorClient
    424 {
    425     _inspectorClient = inspectorClient;
    426 }
    427 
    428 - (WebInspectorClient*)inspectorClient
    429 {
    430     return _inspectorClient;
    431 }
    432 
    433 - (void)setAttachedWindowHeight:(unsigned)height
    434 {
    435     if (!_attachedToInspectedWebView)
    436         return;
    437 
    438     WebFrameView *frameView = [[_inspectedWebView mainFrame] frameView];
    439     NSRect frameViewRect = [frameView frame];
    440 
    441     // Setting the height based on the difference is done to work with
    442     // Safari's find banner. This assumes the previous height is the Y origin.
    443     CGFloat heightDifference = (NSMinY(frameViewRect) - height);
    444     frameViewRect.size.height += heightDifference;
    445     frameViewRect.origin.y = height;
    446 
    447     [_webView setFrame:NSMakeRect(0.0, 0.0, NSWidth(frameViewRect), height)];
    448     [frameView setFrame:frameViewRect];
    449 }
    450 
    451 - (void)destroyInspectorView:(bool)notifyInspectorController
    452 {
    453     if (_destroyingInspectorView)
    454         return;
    455     _destroyingInspectorView = YES;
    456 
    457     if (_attachedToInspectedWebView)
    458         [self close];
    459 
    460     _visible = NO;
    461 
    462     if (notifyInspectorController) {
    463         if (Page* inspectedPage = [_inspectedWebView page])
    464             inspectedPage->inspectorController()->disconnectFrontend();
    465 
    466         _inspectorClient->releaseFrontendPage();
    467     }
    468 
    469     [_webView close];
    470 }
    471 
    472 // MARK: -
    473 // MARK: UI delegate
    474 
    475 - (NSUInteger)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
    476 {
    477     return WebDragDestinationActionNone;
    478 }
    479 
    480 // MARK: -
    481 
    482 // These methods can be used by UI elements such as menu items and toolbar buttons when the inspector is the key window.
    483 
    484 // This method is really only implemented to keep any UI elements enabled.
    485 - (void)showWebInspector:(id)sender
    486 {
    487     [[_inspectedWebView inspector] show:sender];
    488 }
    489 
    490 - (void)showErrorConsole:(id)sender
    491 {
    492     [[_inspectedWebView inspector] showConsole:sender];
    493 }
    494 
    495 - (void)toggleDebuggingJavaScript:(id)sender
    496 {
    497     [[_inspectedWebView inspector] toggleDebuggingJavaScript:sender];
    498 }
    499 
    500 - (void)toggleProfilingJavaScript:(id)sender
    501 {
    502     [[_inspectedWebView inspector] toggleProfilingJavaScript:sender];
    503 }
    504 
    505 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
    506 {
    507     BOOL isMenuItem = [(id)item isKindOfClass:[NSMenuItem class]];
    508     if ([item action] == @selector(toggleDebuggingJavaScript:) && isMenuItem) {
    509         NSMenuItem *menuItem = (NSMenuItem *)item;
    510         if ([[_inspectedWebView inspector] isDebuggingJavaScript])
    511             [menuItem setTitle:UI_STRING_INTERNAL("Stop Debugging JavaScript", "title for Stop Debugging JavaScript menu item")];
    512         else
    513             [menuItem setTitle:UI_STRING_INTERNAL("Start Debugging JavaScript", "title for Start Debugging JavaScript menu item")];
    514     } else if ([item action] == @selector(toggleProfilingJavaScript:) && isMenuItem) {
    515         NSMenuItem *menuItem = (NSMenuItem *)item;
    516         if ([[_inspectedWebView inspector] isProfilingJavaScript])
    517             [menuItem setTitle:UI_STRING_INTERNAL("Stop Profiling JavaScript", "title for Stop Profiling JavaScript menu item")];
    518         else
    519             [menuItem setTitle:UI_STRING_INTERNAL("Start Profiling JavaScript", "title for Start Profiling JavaScript menu item")];
    520     }
    521 
    522     return YES;
    523 }
    524 
    525 @end
    526