Provide a user default that can be used to disable docking of the Web Inspector.
[WebKit-https.git] / Source / WebKit / mac / WebCoreSupport / WebInspectorClient.mm
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 "WebPolicyDelegate.h"
42 #import "WebViewInternal.h"
43 #import <WebCore/Frame.h>
44 #import <WebCore/InspectorController.h>
45 #import <WebCore/InspectorFrontendClient.h>
46 #import <WebCore/Page.h>
47 #import <WebCore/ScriptValue.h>
48 #import <WebCore/SoftLinking.h>
49 #import <WebKit/DOMExtensions.h>
50 #import <WebKitSystemInterface.h>
51 #import <wtf/PassOwnPtr.h>
52
53 SOFT_LINK_STAGED_FRAMEWORK_OPTIONAL(WebInspector, PrivateFrameworks, A)
54
55 // The margin from the top and right of the dock button (same as the full screen button).
56 static const CGFloat dockButtonMargin = 3;
57
58 using namespace WebCore;
59
60 @interface NSWindow (AppKitDetails)
61 - (NSCursor *)_cursorForResizeDirection:(NSInteger)direction;
62 - (NSRect)_customTitleFrame;
63 @end
64
65 @interface WebInspectorWindow : NSWindow {
66 @public
67     RetainPtr<NSButton> _dockButton;
68 }
69 @end
70
71 @implementation WebInspectorWindow
72
73 - (NSCursor *)_cursorForResizeDirection:(NSInteger)direction
74 {
75     // Don't show a resize cursor for the northeast (top right) direction if the dock button is visible.
76     // This matches what happens when the full screen button is visible.
77     if (direction == 1 && ![_dockButton isHidden])
78         return nil;
79     return [super _cursorForResizeDirection:direction];
80 }
81
82 - (NSRect)_customTitleFrame
83 {
84     // Adjust the title frame if needed to prevent it from intersecting the dock button.
85     NSRect titleFrame = [super _customTitleFrame];
86     NSRect dockButtonFrame = _dockButton.get().frame;
87     if (NSMaxX(titleFrame) > NSMinX(dockButtonFrame) - dockButtonMargin)
88         titleFrame.size.width -= (NSMaxX(titleFrame) - NSMinX(dockButtonFrame)) + dockButtonMargin;
89     return titleFrame;
90 }
91
92 @end
93
94 @interface WebInspectorWindowController : NSWindowController <NSWindowDelegate> {
95 @private
96     RetainPtr<WebView> _inspectedWebView;
97     RetainPtr<NSButton> _dockButton;
98     WebView *_webView;
99     WebInspectorFrontendClient* _frontendClient;
100     WebInspectorClient* _inspectorClient;
101     BOOL _attachedToInspectedWebView;
102     BOOL _shouldAttach;
103     BOOL _visible;
104     BOOL _destroyingInspectorView;
105 }
106 - (id)initWithInspectedWebView:(WebView *)webView;
107 - (NSString *)inspectorPagePath;
108 - (WebView *)webView;
109 - (void)attach;
110 - (void)detach;
111 - (BOOL)attached;
112 - (void)setFrontendClient:(WebInspectorFrontendClient*)frontendClient;
113 - (void)setInspectorClient:(WebInspectorClient*)inspectorClient;
114 - (WebInspectorClient*)inspectorClient;
115 - (void)setAttachedWindowHeight:(unsigned)height;
116 - (void)setDockingUnavailable:(BOOL)unavailable;
117 - (void)destroyInspectorView:(bool)notifyInspectorController;
118 @end
119
120
121 // MARK: -
122
123 WebInspectorClient::WebInspectorClient(WebView *webView)
124     : m_webView(webView)
125     , m_highlighter(AdoptNS, [[WebNodeHighlighter alloc] initWithInspectedWebView:webView])
126     , m_frontendPage(0)
127     , m_frontendClient(0)
128 {
129 }
130
131 void WebInspectorClient::inspectorDestroyed()
132 {
133     closeInspectorFrontend();
134     delete this;
135 }
136
137 InspectorFrontendChannel* WebInspectorClient::openInspectorFrontend(InspectorController* inspectorController)
138 {
139     RetainPtr<WebInspectorWindowController> windowController(AdoptNS, [[WebInspectorWindowController alloc] initWithInspectedWebView:m_webView]);
140     [windowController.get() setInspectorClient:this];
141
142     m_frontendPage = core([windowController.get() webView]);
143     OwnPtr<WebInspectorFrontendClient> frontendClient = adoptPtr(new WebInspectorFrontendClient(m_webView, windowController.get(), inspectorController, m_frontendPage, createFrontendSettings()));
144     m_frontendClient = frontendClient.get();
145     RetainPtr<WebInspectorFrontend> webInspectorFrontend(AdoptNS, [[WebInspectorFrontend alloc] initWithFrontendClient:frontendClient.get()]);
146     [[m_webView inspector] setFrontend:webInspectorFrontend.get()];
147     m_frontendPage->inspectorController()->setInspectorFrontendClient(frontendClient.release());
148     return this;
149 }
150
151 void WebInspectorClient::closeInspectorFrontend()
152 {
153     if (m_frontendClient)
154         m_frontendClient->disconnectFromBackend();
155 }
156
157 void WebInspectorClient::bringFrontendToFront()
158 {
159     m_frontendClient->bringToFront();
160 }
161
162 void WebInspectorClient::didResizeMainFrame(Frame*)
163 {
164     if (m_frontendClient)
165         m_frontendClient->attachAvailabilityChanged(m_frontendClient->canAttachWindow() && !inspectorAttachDisabled());
166 }
167
168 void WebInspectorClient::highlight()
169 {
170     [m_highlighter.get() highlight];
171 }
172
173 void WebInspectorClient::hideHighlight()
174 {
175     [m_highlighter.get() hideHighlight];
176 }
177
178 void WebInspectorClient::releaseFrontend()
179 {
180     m_frontendClient = 0;
181     m_frontendPage = 0;
182 }
183
184 WebInspectorFrontendClient::WebInspectorFrontendClient(WebView* inspectedWebView, WebInspectorWindowController* windowController, InspectorController* inspectorController, Page* frontendPage, WTF::PassOwnPtr<Settings> settings)
185     : InspectorFrontendClientLocal(inspectorController,  frontendPage, settings)
186     , m_inspectedWebView(inspectedWebView)
187     , m_windowController(windowController)
188 {
189     [windowController setFrontendClient:this];
190 }
191
192 void WebInspectorFrontendClient::attachAvailabilityChanged(bool available)
193 {
194     setDockingUnavailable(!available);
195     [m_windowController.get() setDockingUnavailable:!available];
196 }
197
198 void WebInspectorFrontendClient::frontendLoaded()
199 {
200     [m_windowController.get() showWindow:nil];
201     if ([m_windowController.get() attached])
202         restoreAttachedWindowHeight();
203
204     InspectorFrontendClientLocal::frontendLoaded();
205
206     WebFrame *frame = [m_inspectedWebView mainFrame];
207     
208     WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(m_inspectedWebView);
209     if (implementations->didClearInspectorWindowObjectForFrameFunc)
210         CallFrameLoadDelegate(implementations->didClearInspectorWindowObjectForFrameFunc, m_inspectedWebView,
211                               @selector(webView:didClearInspectorWindowObject:forFrame:), [frame windowObject], frame);
212
213     bool attached = [m_windowController.get() attached];
214     setAttachedWindow(attached ? DOCKED_TO_BOTTOM : UNDOCKED);
215 }
216
217 static bool useWebKitWebInspector()
218 {
219     // Call the soft link framework function to dlopen it, then [NSBundle bundleWithIdentifier:] will work.
220     WebInspectorLibrary();
221
222     if (![[NSBundle bundleWithIdentifier:@"com.apple.WebInspector"] pathForResource:@"Main" ofType:@"html"])
223         return true;
224
225     if (![[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"])
226         return false;
227
228     return [[NSUserDefaults standardUserDefaults] boolForKey:@"UseWebKitWebInspector"];
229 }
230
231 String WebInspectorFrontendClient::localizedStringsURL()
232 {
233     NSBundle *bundle = useWebKitWebInspector() ? [NSBundle bundleWithIdentifier:@"com.apple.WebCore"] : [NSBundle bundleWithIdentifier:@"com.apple.WebInspector"]; 
234     NSString *path = [bundle pathForResource:@"localizedStrings" ofType:@"js"];
235     if ([path length])
236         return [[NSURL fileURLWithPath:path] absoluteString];
237     return String();
238 }
239
240 void WebInspectorFrontendClient::bringToFront()
241 {
242     updateWindowTitle();
243
244     [m_windowController.get() showWindow:nil];
245
246     // Use the window from the WebView since m_windowController's window
247     // is not the same when the Inspector is docked.
248     WebView *webView = [m_windowController.get() webView];
249     [[webView window] makeFirstResponder:webView];
250 }
251
252 void WebInspectorFrontendClient::closeWindow()
253 {
254     [m_windowController.get() destroyInspectorView:true];
255 }
256
257 void WebInspectorFrontendClient::disconnectFromBackend()
258 {
259     [m_windowController.get() destroyInspectorView:false];
260 }
261
262 void WebInspectorFrontendClient::attachWindow(DockSide)
263 {
264     if ([m_windowController.get() attached])
265         return;
266     [m_windowController.get() attach];
267     restoreAttachedWindowHeight();
268 }
269
270 void WebInspectorFrontendClient::detachWindow()
271 {
272     [m_windowController.get() detach];
273 }
274
275 void WebInspectorFrontendClient::setAttachedWindowHeight(unsigned height)
276 {
277     [m_windowController.get() setAttachedWindowHeight:height];
278 }
279
280 void WebInspectorFrontendClient::setAttachedWindowWidth(unsigned)
281 {
282     // Dock to right is not implemented in WebKit 1.
283 }
284
285 void WebInspectorFrontendClient::inspectedURLChanged(const String& newURL)
286 {
287     m_inspectedURL = newURL;
288     updateWindowTitle();
289 }
290
291 void WebInspectorFrontendClient::updateWindowTitle() const
292 {
293     NSString *title = [NSString stringWithFormat:UI_STRING_INTERNAL("Web Inspector — %@", "Web Inspector window title"), (NSString *)m_inspectedURL];
294     [[m_windowController.get() window] setTitle:title];
295 }
296
297 void WebInspectorFrontendClient::save(const String& refURL, const String& refContent, bool forceSaveAs)
298 {
299     String url = refURL;
300     String content = refContent;
301     auto saveToURL = ^(NSURL *URL) {
302         m_saveURLs.set(url, URL);
303
304         [content writeToURL:URL atomically:YES encoding:NSUTF8StringEncoding error:NULL];
305         core([m_windowController webView])->mainFrame()->script()->executeScript([NSString stringWithFormat:@"InspectorFrontendAPI.savedURL(\"%@\")", URL.absoluteString]);
306     };
307
308     NSURL *URL = m_saveURLs.get(url).get();
309     if (!URL)
310         URL = [NSURL URLWithString:url];
311     else if (!forceSaveAs) {
312         saveToURL(URL);
313         return;
314     }
315
316     NSSavePanel *panel = [NSSavePanel savePanel];
317     panel.nameFieldStringValue = URL.lastPathComponent;
318     panel.directoryURL = [URL URLByDeletingLastPathComponent];
319
320     [panel beginSheetModalForWindow:[[m_windowController webView] window] completionHandler:^(NSInteger result) {
321         if (result == NSFileHandlingPanelCancelButton)
322             return;
323         ASSERT(result == NSFileHandlingPanelOKButton);
324         saveToURL(panel.URL);
325     }];
326 }
327
328 void WebInspectorFrontendClient::append(const String& url, const String& content)
329 {
330     RetainPtr<NSURL> URL = m_saveURLs.get(url);
331     if (!URL)
332         URL = [NSURL URLWithString:url];
333
334     NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:URL.get() error:NULL];
335     [handle seekToEndOfFile];
336     [handle writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
337     [handle closeFile];
338
339     core([m_windowController webView])->mainFrame()->script()->executeScript([NSString stringWithFormat:@"InspectorFrontendAPI.appendedToURL(\"%@\")", [URL absoluteString]]);
340 }
341
342 // MARK: -
343
344 @implementation WebInspectorWindowController
345 - (id)init
346 {
347     if (!(self = [super initWithWindow:nil]))
348         return nil;
349
350     // Keep preferences separate from the rest of the client, making sure we are using expected preference values.
351
352     WebPreferences *preferences = [[WebPreferences alloc] init];
353     [preferences setAllowsAnimatedImages:YES];
354     [preferences setApplicationChromeModeEnabled:YES];
355     [preferences setAuthorAndUserStylesEnabled:YES];
356     [preferences setAutosaves:NO];
357     [preferences setDefaultFixedFontSize:11];
358     [preferences setFixedFontFamily:@"Menlo"];
359     [preferences setJavaEnabled:NO];
360     [preferences setJavaScriptEnabled:YES];
361     [preferences setLoadsImagesAutomatically:YES];
362     [preferences setMinimumFontSize:0];
363     [preferences setMinimumLogicalFontSize:9];
364     [preferences setPlugInsEnabled:NO];
365     [preferences setTabsToLinks:NO];
366     [preferences setUserStyleSheetEnabled:NO];
367
368     _webView = [[WebView alloc] init];
369     [_webView setPreferences:preferences];
370     [_webView setDrawsBackground:NO];
371     [_webView setProhibitsMainFrameScrolling:YES];
372     [_webView setUIDelegate:self];
373     [_webView setPolicyDelegate:self];
374
375     [preferences release];
376
377     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:[self inspectorPagePath]]];
378     [[_webView mainFrame] loadRequest:request];
379     [request release];
380
381     [self setWindowFrameAutosaveName:@"Web Inspector 2"];
382     return self;
383 }
384
385 - (id)initWithInspectedWebView:(WebView *)webView
386 {
387     if (!(self = [self init]))
388         return nil;
389
390     _inspectedWebView = webView;
391     return self;
392 }
393
394 - (void)dealloc
395 {
396     [_webView release];
397     [super dealloc];
398 }
399
400 // MARK: -
401
402 - (NSString *)inspectorPagePath
403 {
404     NSString *path;
405     if (useWebKitWebInspector())
406         path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"];
407     else
408         path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspector"] pathForResource:@"Main" ofType:@"html"];
409
410     ASSERT([path length]);
411     return path;
412 }
413
414 // MARK: -
415
416 - (WebView *)webView
417 {
418     return _webView;
419 }
420
421 - (NSWindow *)window
422 {
423     WebInspectorWindow *window = (WebInspectorWindow *)[super window];
424     if (window)
425         return window;
426
427     NSUInteger styleMask = (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask | NSTexturedBackgroundWindowMask);
428     window = [[WebInspectorWindow alloc] initWithContentRect:NSMakeRect(60.0, 200.0, 750.0, 650.0) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO];
429     [window setDelegate:self];
430     [window setMinSize:NSMakeSize(400.0, 400.0)];
431     [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
432     [window setContentBorderThickness:55. forEdge:NSMaxYEdge];
433     WKNSWindowMakeBottomCornersSquare(window);
434
435     // Create a full screen button so we can turn it into a dock button.
436     _dockButton = [NSWindow standardWindowButton:NSWindowFullScreenButton forStyleMask:styleMask];
437     _dockButton.get().target = self;
438     _dockButton.get().action = @selector(attachWindow:);
439
440     // Store the dock button on the window too so it can check its visibility.
441     window->_dockButton = _dockButton;
442
443     // Get the dock image and make it a template so the button cell effects will apply.
444     NSImage *dockImage = [[NSBundle bundleForClass:[self class]] imageForResource:@"Dock"];
445     [dockImage setTemplate:YES];
446
447     // Set the dock image on the button cell.
448     NSCell *dockButtonCell = _dockButton.get().cell;
449     dockButtonCell.image = dockImage;
450
451     // Get the frame view, the superview of the content view, and its frame.
452     // This will be the superview of the dock button too.
453     NSView *contentView = window.contentView;
454     NSView *frameView = contentView.superview;
455     NSRect frameViewBounds = frameView.bounds;
456     NSSize dockButtonSize = _dockButton.get().frame.size;
457
458     ASSERT(!frameView.isFlipped);
459
460     // Position the dock button in the corner to match where the full screen button is normally.
461     NSPoint dockButtonOrigin;
462     dockButtonOrigin.x = NSMaxX(frameViewBounds) - dockButtonSize.width - dockButtonMargin;
463     dockButtonOrigin.y = NSMaxY(frameViewBounds) - dockButtonSize.height - dockButtonMargin;
464     _dockButton.get().frameOrigin = dockButtonOrigin;
465
466     // Set the autoresizing mask to keep the dock button pinned to the top right corner.
467     _dockButton.get().autoresizingMask = NSViewMinXMargin | NSViewMinYMargin;
468
469     [frameView addSubview:_dockButton.get()];
470
471     // Hide the dock button if we can't attach.
472     _dockButton.get().hidden = !_frontendClient->canAttachWindow() || _inspectorClient->inspectorAttachDisabled();
473
474     [self setWindow:window];
475     [window release];
476
477     return window;
478 }
479
480 // MARK: -
481
482 - (NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet usingRect:(NSRect)rect
483 {
484     // AppKit doesn't know about our HTML toolbar, and places the sheet just a little bit too high.
485     // FIXME: It would be better to get the height of the toolbar and use it in this calculation.
486     rect.origin.y -= 1;
487     return rect;
488 }
489
490 - (BOOL)windowShouldClose:(id)sender
491 {
492     [self destroyInspectorView:true];
493
494     return YES;
495 }
496
497 - (void)close
498 {
499     if (!_visible)
500         return;
501
502     _visible = NO;
503
504     if (_attachedToInspectedWebView) {
505         if ([_inspectedWebView.get() _isClosed])
506             return;
507
508         [_webView removeFromSuperview];
509
510         WebFrameView *frameView = [[_inspectedWebView.get() mainFrame] frameView];
511         NSRect frameViewRect = [frameView frame];
512
513         // Setting the height based on the previous height is done to work with
514         // Safari's find banner. This assumes the previous height is the Y origin.
515         frameViewRect.size.height += NSMinY(frameViewRect);
516         frameViewRect.origin.y = 0.0;
517
518         [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
519         [frameView setFrame:frameViewRect];
520
521         [_inspectedWebView.get() displayIfNeeded];
522     } else
523         [super close];
524 }
525
526 - (IBAction)attachWindow:(id)sender
527 {
528     _frontendClient->attachWindow(InspectorFrontendClient::DOCKED_TO_BOTTOM);
529 }
530
531 - (IBAction)showWindow:(id)sender
532 {
533     if (_visible) {
534         if (!_attachedToInspectedWebView)
535             [super showWindow:sender]; // call super so the window will be ordered front if needed
536         return;
537     }
538
539     _visible = YES;
540     
541     _shouldAttach = _inspectorClient->inspectorStartsAttached() && _frontendClient->canAttachWindow() && !_inspectorClient->inspectorAttachDisabled();
542
543     if (_shouldAttach) {
544         WebFrameView *frameView = [[_inspectedWebView.get() mainFrame] frameView];
545
546         [_webView removeFromSuperview];
547         [_inspectedWebView.get() addSubview:_webView positioned:NSWindowBelow relativeTo:(NSView *)frameView];
548         [[_inspectedWebView.get() window] makeFirstResponder:_webView];
549
550         [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMaxYMargin)];
551         [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMinYMargin)];
552
553         _attachedToInspectedWebView = YES;
554     } else {
555         _attachedToInspectedWebView = NO;
556
557         NSView *contentView = [[self window] contentView];
558         [_webView setFrame:[contentView frame]];
559         [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
560         [_webView removeFromSuperview];
561         [contentView addSubview:_webView];
562
563         [super showWindow:nil];
564     }
565 }
566
567 // MARK: -
568
569 - (void)attach
570 {
571     if (_attachedToInspectedWebView)
572         return;
573
574     _inspectorClient->setInspectorStartsAttached(true);
575     _frontendClient->setAttachedWindow(InspectorFrontendClient::DOCKED_TO_BOTTOM);
576
577     [self close];
578     [self showWindow:nil];
579 }
580
581 - (void)detach
582 {
583     if (!_attachedToInspectedWebView)
584         return;
585
586     _inspectorClient->setInspectorStartsAttached(false);
587     _frontendClient->setAttachedWindow(InspectorFrontendClient::UNDOCKED);
588
589     [self close];
590     [self showWindow:nil];
591 }
592
593 - (BOOL)attached
594 {
595     return _attachedToInspectedWebView;
596 }
597
598 - (void)setFrontendClient:(WebInspectorFrontendClient*)frontendClient
599 {
600     _frontendClient = frontendClient;
601 }
602
603 - (void)setInspectorClient:(WebInspectorClient*)inspectorClient
604 {
605     _inspectorClient = inspectorClient;
606 }
607
608 - (WebInspectorClient*)inspectorClient
609 {
610     return _inspectorClient;
611 }
612
613 - (void)setAttachedWindowHeight:(unsigned)height
614 {
615     if (!_attachedToInspectedWebView)
616         return;
617
618     WebFrameView *frameView = [[_inspectedWebView.get() mainFrame] frameView];
619     NSRect frameViewRect = [frameView frame];
620
621     // Setting the height based on the difference is done to work with
622     // Safari's find banner. This assumes the previous height is the Y origin.
623     CGFloat heightDifference = (NSMinY(frameViewRect) - height);
624     frameViewRect.size.height += heightDifference;
625     frameViewRect.origin.y = height;
626
627     [_webView setFrame:NSMakeRect(0.0, 0.0, NSWidth(frameViewRect), height)];
628     [frameView setFrame:frameViewRect];
629 }
630
631 - (void)setDockingUnavailable:(BOOL)unavailable
632 {
633     _dockButton.get().hidden = unavailable;
634 }
635
636 - (void)destroyInspectorView:(bool)notifyInspectorController
637 {
638     [[_inspectedWebView.get() inspector] releaseFrontend];
639     _inspectorClient->releaseFrontend();
640
641     if (_destroyingInspectorView)
642         return;
643     _destroyingInspectorView = YES;
644
645     if (_attachedToInspectedWebView)
646         [self close];
647
648     _visible = NO;
649
650     if (notifyInspectorController) {
651         if (Page* inspectedPage = [_inspectedWebView.get() page])
652             inspectedPage->inspectorController()->disconnectFrontend();
653     }
654
655     RetainPtr<WebInspectorWindowController> protect(self);
656     [_webView close];
657 }
658
659 // MARK: -
660 // MARK: UI delegate
661
662 - (NSUInteger)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
663 {
664     return WebDragDestinationActionNone;
665 }
666
667 - (void)webView:(WebView *)sender runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)resultListener allowMultipleFiles:(BOOL)allowMultipleFiles
668 {
669     NSOpenPanel *panel = [NSOpenPanel openPanel];
670     panel.canChooseDirectories = NO;
671     panel.canChooseFiles = YES;
672     panel.allowsMultipleSelection = allowMultipleFiles;
673
674     [panel beginSheetModalForWindow:_webView.window completionHandler:^(NSInteger result) {
675         if (result == NSFileHandlingPanelCancelButton) {
676             [resultListener cancel];
677             return;
678         }
679         ASSERT(result == NSFileHandlingPanelOKButton);
680
681         NSArray *URLs = panel.URLs;
682         NSMutableArray *filenames = [NSMutableArray arrayWithCapacity:URLs.count];
683         for (NSURL *URL in URLs) {
684             [filenames addObject:URL.path];
685         }
686         [resultListener chooseFilenames:filenames];
687     }];
688 }
689
690 // MARK: -
691 // MARK: Policy delegate
692
693 - (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener
694 {
695     // Allow non-main frames to navigate anywhere.
696     if (frame != [webView mainFrame]) {
697         [listener use];
698         return;
699     }
700
701     // Allow loading of the main inspector file.
702     if ([[request URL] isFileURL] && [[[request URL] path] isEqualToString:[self inspectorPagePath]]) {
703         [listener use];
704         return;
705     }
706
707     // Prevent everything else from loading in the inspector's page.
708     [listener ignore];
709
710     // And instead load it in the inspected page.
711     [[_inspectedWebView.get() mainFrame] loadRequest:request];
712 }
713
714 // MARK: -
715 // These methods can be used by UI elements such as menu items and toolbar buttons when the inspector is the key window.
716
717 // This method is really only implemented to keep any UI elements enabled.
718 - (void)showWebInspector:(id)sender
719 {
720     [[_inspectedWebView.get() inspector] show:sender];
721 }
722
723 - (void)showErrorConsole:(id)sender
724 {
725     [[_inspectedWebView.get() inspector] showConsole:sender];
726 }
727
728 - (void)toggleDebuggingJavaScript:(id)sender
729 {
730     [[_inspectedWebView.get() inspector] toggleDebuggingJavaScript:sender];
731 }
732
733 - (void)toggleProfilingJavaScript:(id)sender
734 {
735     [[_inspectedWebView.get() inspector] toggleProfilingJavaScript:sender];
736 }
737
738 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
739 {
740     BOOL isMenuItem = [(id)item isKindOfClass:[NSMenuItem class]];
741     if ([item action] == @selector(toggleDebuggingJavaScript:) && isMenuItem) {
742         NSMenuItem *menuItem = (NSMenuItem *)item;
743         if ([[_inspectedWebView.get() inspector] isDebuggingJavaScript])
744             [menuItem setTitle:UI_STRING_INTERNAL("Stop Debugging JavaScript", "title for Stop Debugging JavaScript menu item")];
745         else
746             [menuItem setTitle:UI_STRING_INTERNAL("Start Debugging JavaScript", "title for Start Debugging JavaScript menu item")];
747     } else if ([item action] == @selector(toggleProfilingJavaScript:) && isMenuItem) {
748         NSMenuItem *menuItem = (NSMenuItem *)item;
749         if ([[_inspectedWebView.get() inspector] isProfilingJavaScript])
750             [menuItem setTitle:UI_STRING_INTERNAL("Stop Profiling JavaScript", "title for Stop Profiling JavaScript menu item")];
751         else
752             [menuItem setTitle:UI_STRING_INTERNAL("Start Profiling JavaScript", "title for Start Profiling JavaScript menu item")];
753     }
754
755     return YES;
756 }
757
758
759 @end