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