Move the legacy WebKit API into WebKitLegacy.framework and move it inside WebKit...
[WebKit-https.git] / Source / WebKit / mac / WebCoreSupport / WebInspectorClient.mm
index edee884..73ce2a3 100644 (file)
@@ -6,13 +6,13 @@
  * are met:
  *
  * 1.  Redistributions of source code must retain the above copyright
- *     notice, this list of conditions and the following disclaimer. 
+ *     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
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple 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. 
+ *     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
 #import "WebFrameInternal.h"
 #import "WebFrameView.h"
 #import "WebInspector.h"
-#import "WebInspectorPrivate.h"
 #import "WebInspectorFrontend.h"
+#import "WebInspectorPrivate.h"
 #import "WebLocalizableStringsInternal.h"
 #import "WebNodeHighlighter.h"
-#import "WebUIDelegate.h"
 #import "WebPolicyDelegate.h"
+#import "WebQuotaManager.h"
+#import "WebSecurityOriginPrivate.h"
+#import "WebUIDelegate.h"
 #import "WebViewInternal.h"
+#import <algorithm>
+#import <bindings/ScriptValue.h>
+#import <inspector/InspectorAgentBase.h>
 #import <WebCore/InspectorController.h>
+#import <WebCore/InspectorFrontendClient.h>
+#import <WebCore/MainFrame.h>
 #import <WebCore/Page.h>
+#import <WebCore/ScriptController.h>
 #import <WebCore/SoftLinking.h>
-#import <WebKit/DOMExtensions.h>
+#import <WebKitLegacy/DOMExtensions.h>
 #import <WebKitSystemInterface.h>
-#import <wtf/PassOwnPtr.h>
+#import <wtf/text/Base64.h>
 
-SOFT_LINK_STAGED_FRAMEWORK_OPTIONAL(WebInspector, PrivateFrameworks)
+SOFT_LINK_STAGED_FRAMEWORK(WebInspectorUI, PrivateFrameworks, A)
+
+// The margin from the top and right of the dock button (same as the full screen button).
+static const CGFloat dockButtonMargin = 3;
 
 using namespace WebCore;
 
+@interface NSWindow (AppKitDetails)
+- (NSCursor *)_cursorForResizeDirection:(NSInteger)direction;
+- (NSRect)_customTitleFrame;
+@end
+
+@interface WebInspectorWindow : NSWindow {
+@public
+    RetainPtr<NSButton> _dockButton;
+}
+@end
+
+@implementation WebInspectorWindow
+
+- (NSCursor *)_cursorForResizeDirection:(NSInteger)direction
+{
+    // Don't show a resize cursor for the northeast (top right) direction if the dock button is visible.
+    // This matches what happens when the full screen button is visible.
+    if (direction == 1 && ![_dockButton isHidden])
+        return nil;
+    return [super _cursorForResizeDirection:direction];
+}
+
+- (NSRect)_customTitleFrame
+{
+    // Adjust the title frame if needed to prevent it from intersecting the dock button.
+    NSRect titleFrame = [super _customTitleFrame];
+    NSRect dockButtonFrame = _dockButton.get().frame;
+    if (NSMaxX(titleFrame) > NSMinX(dockButtonFrame) - dockButtonMargin)
+        titleFrame.size.width -= (NSMaxX(titleFrame) - NSMinX(dockButtonFrame)) + dockButtonMargin;
+    return titleFrame;
+}
+
+@end
+
 @interface WebInspectorWindowController : NSWindowController <NSWindowDelegate> {
 @private
     RetainPtr<WebView> _inspectedWebView;
+    RetainPtr<NSButton> _dockButton;
     WebView *_webView;
     WebInspectorFrontendClient* _frontendClient;
     WebInspectorClient* _inspectorClient;
@@ -62,8 +108,9 @@ using namespace WebCore;
     BOOL _visible;
     BOOL _destroyingInspectorView;
 }
-- (id)initWithInspectedWebView:(WebView *)webView;
+- (id)initWithInspectedWebView:(WebView *)webView isUnderTest:(BOOL)isUnderTest;
 - (NSString *)inspectorPagePath;
+- (NSString *)inspectorTestPagePath;
 - (WebView *)webView;
 - (void)attach;
 - (void)detach;
@@ -72,6 +119,7 @@ using namespace WebCore;
 - (void)setInspectorClient:(WebInspectorClient*)inspectorClient;
 - (WebInspectorClient*)inspectorClient;
 - (void)setAttachedWindowHeight:(unsigned)height;
+- (void)setDockingUnavailable:(BOOL)unavailable;
 - (void)destroyInspectorView:(bool)notifyInspectorController;
 @end
 
@@ -80,7 +128,7 @@ using namespace WebCore;
 
 WebInspectorClient::WebInspectorClient(WebView *webView)
     : m_webView(webView)
-    , m_highlighter(AdoptNS, [[WebNodeHighlighter alloc] initWithInspectedWebView:webView])
+    , m_highlighter(adoptNS([[WebNodeHighlighter alloc] initWithInspectedWebView:webView]))
     , m_frontendPage(0)
     , m_frontendClient(0)
 {
@@ -92,17 +140,18 @@ void WebInspectorClient::inspectorDestroyed()
     delete this;
 }
 
-void WebInspectorClient::openInspectorFrontend(InspectorController* inspectorController)
+InspectorFrontendChannel* WebInspectorClient::openInspectorFrontend(InspectorController* inspectorController)
 {
-    RetainPtr<WebInspectorWindowController> windowController(AdoptNS, [[WebInspectorWindowController alloc] initWithInspectedWebView:m_webView]);
+    RetainPtr<WebInspectorWindowController> windowController = adoptNS([[WebInspectorWindowController alloc] initWithInspectedWebView:m_webView isUnderTest:inspectorController->isUnderTest()]);
     [windowController.get() setInspectorClient:this];
 
     m_frontendPage = core([windowController.get() webView]);
-    OwnPtr<WebInspectorFrontendClient> frontendClient = adoptPtr(new WebInspectorFrontendClient(m_webView, windowController.get(), inspectorController, m_frontendPage, createFrontendSettings()));
+    auto frontendClient = std::make_unique<WebInspectorFrontendClient>(m_webView, windowController.get(), inspectorController, m_frontendPage, createFrontendSettings());
     m_frontendClient = frontendClient.get();
-    RetainPtr<WebInspectorFrontend> webInspectorFrontend(AdoptNS, [[WebInspectorFrontend alloc] initWithFrontendClient:frontendClient.get()]);
+    RetainPtr<WebInspectorFrontend> webInspectorFrontend = adoptNS([[WebInspectorFrontend alloc] initWithFrontendClient:frontendClient.get()]);
     [[m_webView inspector] setFrontend:webInspectorFrontend.get()];
-    m_frontendPage->inspectorController()->setInspectorFrontendClient(frontendClient.release());
+    m_frontendPage->inspectorController().setInspectorFrontendClient(std::move(frontendClient));
+    return this;
 }
 
 void WebInspectorClient::closeInspectorFrontend()
@@ -119,7 +168,7 @@ void WebInspectorClient::bringFrontendToFront()
 void WebInspectorClient::didResizeMainFrame(Frame*)
 {
     if (m_frontendClient)
-        m_frontendClient->setDockingUnavailable(!m_frontendClient->canAttachWindow());
+        m_frontendClient->attachAvailabilityChanged(m_frontendClient->canAttachWindow() && !inspectorAttachDisabled());
 }
 
 void WebInspectorClient::highlight()
@@ -132,20 +181,39 @@ void WebInspectorClient::hideHighlight()
     [m_highlighter.get() hideHighlight];
 }
 
+void WebInspectorClient::didSetSearchingForNode(bool enabled)
+{
+    WebInspector *inspector = [m_webView inspector];
+
+    ASSERT(isMainThread());
+
+    if (enabled)
+        [[NSNotificationCenter defaultCenter] postNotificationName:WebInspectorDidStartSearchingForNode object:inspector];
+    else
+        [[NSNotificationCenter defaultCenter] postNotificationName:WebInspectorDidStopSearchingForNode object:inspector];
+}
+
 void WebInspectorClient::releaseFrontend()
 {
     m_frontendClient = 0;
     m_frontendPage = 0;
 }
 
-WebInspectorFrontendClient::WebInspectorFrontendClient(WebView* inspectedWebView, WebInspectorWindowController* windowController, InspectorController* inspectorController, Page* frontendPage, WTF::PassOwnPtr<Settings> settings)
-    : InspectorFrontendClientLocal(inspectorController,  frontendPage, settings)
+
+WebInspectorFrontendClient::WebInspectorFrontendClient(WebView* inspectedWebView, WebInspectorWindowController* windowController, InspectorController* inspectorController, Page* frontendPage, std::unique_ptr<Settings> settings)
+    : InspectorFrontendClientLocal(inspectorController,  frontendPage, std::move(settings))
     , m_inspectedWebView(inspectedWebView)
     , m_windowController(windowController)
 {
     [windowController setFrontendClient:this];
 }
 
+void WebInspectorFrontendClient::attachAvailabilityChanged(bool available)
+{
+    setDockingUnavailable(!available);
+    [m_windowController.get() setDockingUnavailable:!available];
+}
+
 void WebInspectorFrontendClient::frontendLoaded()
 {
     [m_windowController.get() showWindow:nil];
@@ -155,51 +223,37 @@ void WebInspectorFrontendClient::frontendLoaded()
     InspectorFrontendClientLocal::frontendLoaded();
 
     WebFrame *frame = [m_inspectedWebView mainFrame];
-    
+
     WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(m_inspectedWebView);
     if (implementations->didClearInspectorWindowObjectForFrameFunc)
         CallFrameLoadDelegate(implementations->didClearInspectorWindowObjectForFrameFunc, m_inspectedWebView,
                               @selector(webView:didClearInspectorWindowObject:forFrame:), [frame windowObject], frame);
 
     bool attached = [m_windowController.get() attached];
-    setAttachedWindow(attached);
+    setAttachedWindow(attached ? DOCKED_TO_BOTTOM : UNDOCKED);
 }
 
-static bool useWebKitWebInspector()
+String WebInspectorFrontendClient::localizedStringsURL()
 {
     // Call the soft link framework function to dlopen it, then [NSBundle bundleWithIdentifier:] will work.
-    WebInspectorLibrary();
-
-    if (![[NSBundle bundleWithIdentifier:@"com.apple.WebInspector"] pathForResource:@"Main" ofType:@"html"])
-        return true;
-
-    if (![[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"])
-        return false;
-
-    return [[NSUserDefaults standardUserDefaults] boolForKey:@"UseWebKitWebInspector"];
-}
+    WebInspectorUILibrary();
 
-String WebInspectorFrontendClient::localizedStringsURL()
-{
-    NSBundle *bundle = useWebKitWebInspector() ? [NSBundle bundleWithIdentifier:@"com.apple.WebCore"] : [NSBundle bundleWithIdentifier:@"com.apple.WebInspector"]; 
-    NSString *path = [bundle pathForResource:@"localizedStrings" ofType:@"js"];
+    NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspectorUI"] pathForResource:@"localizedStrings" ofType:@"js"];
     if ([path length])
         return [[NSURL fileURLWithPath:path] absoluteString];
     return String();
 }
 
-String WebInspectorFrontendClient::hiddenPanels()
-{
-    NSString *hiddenPanels = [[NSUserDefaults standardUserDefaults] stringForKey:@"WebKitInspectorHiddenPanels"];
-    if (hiddenPanels)
-        return hiddenPanels;
-    return String();
-}
-
 void WebInspectorFrontendClient::bringToFront()
 {
     updateWindowTitle();
+
     [m_windowController.get() showWindow:nil];
+
+    // Use the window from the WebView since m_windowController's window
+    // is not the same when the Inspector is docked.
+    WebView *webView = [m_windowController.get() webView];
+    [[webView window] makeFirstResponder:webView];
 }
 
 void WebInspectorFrontendClient::closeWindow()
@@ -212,7 +266,7 @@ void WebInspectorFrontendClient::disconnectFromBackend()
     [m_windowController.get() destroyInspectorView:false];
 }
 
-void WebInspectorFrontendClient::attachWindow()
+void WebInspectorFrontendClient::attachWindow(DockSide)
 {
     if ([m_windowController.get() attached])
         return;
@@ -230,6 +284,16 @@ void WebInspectorFrontendClient::setAttachedWindowHeight(unsigned height)
     [m_windowController.get() setAttachedWindowHeight:height];
 }
 
+void WebInspectorFrontendClient::setAttachedWindowWidth(unsigned)
+{
+    // Dock to right is not implemented in WebKit 1.
+}
+
+void WebInspectorFrontendClient::setToolbarHeight(unsigned height)
+{
+    [[m_windowController window] setContentBorderThickness:height forEdge:NSMaxYEdge];
+}
+
 void WebInspectorFrontendClient::inspectedURLChanged(const String& newURL)
 {
     m_inspectedURL = newURL;
@@ -242,6 +306,75 @@ void WebInspectorFrontendClient::updateWindowTitle() const
     [[m_windowController.get() window] setTitle:title];
 }
 
+void WebInspectorFrontendClient::save(const String& suggestedURL, const String& content, bool base64Encoded, bool forceSaveDialog)
+{
+    ASSERT(!suggestedURL.isEmpty());
+
+    NSURL *platformURL = m_suggestedToActualURLMap.get(suggestedURL).get();
+    if (!platformURL) {
+        platformURL = [NSURL URLWithString:suggestedURL];
+        // The user must confirm new filenames before we can save to them.
+        forceSaveDialog = true;
+    }
+
+    ASSERT(platformURL);
+    if (!platformURL)
+        return;
+
+    // Necessary for the block below.
+    String suggestedURLCopy = suggestedURL;
+    String contentCopy = content;
+
+    auto saveToURL = ^(NSURL *actualURL) {
+        ASSERT(actualURL);
+
+        m_suggestedToActualURLMap.set(suggestedURLCopy, actualURL);
+
+        if (base64Encoded) {
+            Vector<char> out;
+            if (!base64Decode(contentCopy, out, Base64FailOnInvalidCharacterOrExcessPadding))
+                return;
+            RetainPtr<NSData> dataContent = adoptNS([[NSData alloc] initWithBytes:out.data() length:out.size()]);
+            [dataContent writeToURL:actualURL atomically:YES];
+        } else
+            [contentCopy writeToURL:actualURL atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+
+        core([m_windowController webView])->mainFrame().script().executeScript([NSString stringWithFormat:@"InspectorFrontendAPI.savedURL(\"%@\")", actualURL.absoluteString]);
+    };
+
+    if (!forceSaveDialog) {
+        saveToURL(platformURL);
+        return;
+    }
+
+    NSSavePanel *panel = [NSSavePanel savePanel];
+    panel.nameFieldStringValue = platformURL.lastPathComponent;
+    panel.directoryURL = [platformURL URLByDeletingLastPathComponent];
+
+    [panel beginSheetModalForWindow:[[m_windowController webView] window] completionHandler:^(NSInteger result) {
+        if (result == NSFileHandlingPanelCancelButton)
+            return;
+        ASSERT(result == NSFileHandlingPanelOKButton);
+        saveToURL(panel.URL);
+    }];
+}
+
+void WebInspectorFrontendClient::append(const String& suggestedURL, const String& content)
+{
+    ASSERT(!suggestedURL.isEmpty());
+
+    RetainPtr<NSURL> actualURL = m_suggestedToActualURLMap.get(suggestedURL);
+    // do not append unless the user has already confirmed this filename in save().
+    if (!actualURL)
+        return;
+
+    NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:actualURL.get() error:NULL];
+    [handle seekToEndOfFile];
+    [handle writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
+    [handle closeFile];
+
+    core([m_windowController webView])->mainFrame().script().executeScript([NSString stringWithFormat:@"InspectorFrontendAPI.appendedToURL(\"%@\")", [actualURL absoluteString]]);
+}
 
 // MARK: -
 
@@ -266,7 +399,6 @@ void WebInspectorFrontendClient::updateWindowTitle() const
     [preferences setMinimumFontSize:0];
     [preferences setMinimumLogicalFontSize:9];
     [preferences setPlugInsEnabled:NO];
-    [preferences setSuppressesIncrementalRendering:YES];
     [preferences setTabsToLinks:NO];
     [preferences setUserStyleSheetEnabled:NO];
 
@@ -279,20 +411,22 @@ void WebInspectorFrontendClient::updateWindowTitle() const
 
     [preferences release];
 
-    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:[self inspectorPagePath]]];
-    [[_webView mainFrame] loadRequest:request];
-    [request release];
-
     [self setWindowFrameAutosaveName:@"Web Inspector 2"];
     return self;
 }
 
-- (id)initWithInspectedWebView:(WebView *)webView
+- (id)initWithInspectedWebView:(WebView *)webView isUnderTest:(BOOL)isUnderTest
 {
     if (!(self = [self init]))
         return nil;
 
     _inspectedWebView = webView;
+
+    NSString *pagePath = isUnderTest ? [self inspectorTestPagePath] : [self inspectorPagePath];
+    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath: pagePath]];
+    [[_webView mainFrame] loadRequest:request];
+    [request release];
+
     return self;
 }
 
@@ -306,16 +440,28 @@ void WebInspectorFrontendClient::updateWindowTitle() const
 
 - (NSString *)inspectorPagePath
 {
-    NSString *path;
-    if (useWebKitWebInspector())
-        path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"];
-    else
-        path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspector"] pathForResource:@"Main" ofType:@"html"];
+    // Call the soft link framework function to dlopen it, then [NSBundle bundleWithIdentifier:] will work.
+    WebInspectorUILibrary();
 
+    NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspectorUI"] pathForResource:@"Main" ofType:@"html"];
     ASSERT([path length]);
     return path;
 }
 
+- (NSString *)inspectorTestPagePath
+{
+    // Call the soft link framework function to dlopen it, then [NSBundle bundleWithIdentifier:] will work.
+    WebInspectorUILibrary();
+
+    NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspectorUI"] pathForResource:@"Test" ofType:@"html"];
+
+    // We might not have a Test.html in Production builds.
+    if (!path)
+        return nil;
+
+    return path;
+}
+
 // MARK: -
 
 - (WebView *)webView
@@ -325,26 +471,56 @@ void WebInspectorFrontendClient::updateWindowTitle() const
 
 - (NSWindow *)window
 {
-    NSWindow *window = [super window];
+    WebInspectorWindow *window = (WebInspectorWindow *)[super window];
     if (window)
         return window;
 
-    bool useTexturedWindow = useWebKitWebInspector();
+    NSUInteger styleMask = (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask | NSTexturedBackgroundWindowMask);
+    window = [[WebInspectorWindow 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)];
+    [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
+    [window setContentBorderThickness:55. forEdge:NSMaxYEdge];
+    WKNSWindowMakeBottomCornersSquare(window);
+
+    // Create a full screen button so we can turn it into a dock button.
+    _dockButton = [NSWindow standardWindowButton:NSWindowFullScreenButton forStyleMask:styleMask];
+    _dockButton.get().target = self;
+    _dockButton.get().action = @selector(attachWindow:);
 
-    NSUInteger styleMask = (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask);
+    // Store the dock button on the window too so it can check its visibility.
+    window->_dockButton = _dockButton;
 
-    if (useTexturedWindow)
-        styleMask |= NSTexturedBackgroundWindowMask;
+    // Get the dock image and make it a template so the button cell effects will apply.
+    NSImage *dockImage = [[NSBundle bundleForClass:[self class]] imageForResource:@"Dock"];
+    [dockImage setTemplate:YES];
 
-    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)];
+    // Set the dock image on the button cell.
+    NSCell *dockButtonCell = _dockButton.get().cell;
+    dockButtonCell.image = dockImage;
 
-    if (useTexturedWindow) {
-        [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
-        [window setContentBorderThickness:55. forEdge:NSMaxYEdge];
-        WKNSWindowMakeBottomCornersSquare(window);
-    }
+    // Get the frame view, the superview of the content view, and its frame.
+    // This will be the superview of the dock button too.
+    NSView *contentView = window.contentView;
+    NSView *frameView = contentView.superview;
+    NSRect frameViewBounds = frameView.bounds;
+    NSSize dockButtonSize = _dockButton.get().frame.size;
+
+    ASSERT(!frameView.isFlipped);
+
+    // Position the dock button in the corner to match where the full screen button is normally.
+    NSPoint dockButtonOrigin;
+    dockButtonOrigin.x = NSMaxX(frameViewBounds) - dockButtonSize.width - dockButtonMargin;
+    dockButtonOrigin.y = NSMaxY(frameViewBounds) - dockButtonSize.height - dockButtonMargin;
+    _dockButton.get().frameOrigin = dockButtonOrigin;
+
+    // Set the autoresizing mask to keep the dock button pinned to the top right corner.
+    _dockButton.get().autoresizingMask = NSViewMinXMargin | NSViewMinYMargin;
+
+    [frameView addSubview:_dockButton.get()];
+
+    // Hide the dock button if we can't attach.
+    _dockButton.get().hidden = !_frontendClient->canAttachWindow() || _inspectorClient->inspectorAttachDisabled();
 
     [self setWindow:window];
     [window release];
@@ -354,6 +530,14 @@ void WebInspectorFrontendClient::updateWindowTitle() const
 
 // MARK: -
 
+- (NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet usingRect:(NSRect)rect
+{
+    // AppKit doesn't know about our HTML toolbar, and places the sheet just a little bit too high.
+    // FIXME: It would be better to get the height of the toolbar and use it in this calculation.
+    rect.origin.y -= 1;
+    return rect;
+}
+
 - (BOOL)windowShouldClose:(id)sender
 {
     [self destroyInspectorView:true];
@@ -390,6 +574,11 @@ void WebInspectorFrontendClient::updateWindowTitle() const
         [super close];
 }
 
+- (IBAction)attachWindow:(id)sender
+{
+    _frontendClient->attachWindow(InspectorFrontendClient::DOCKED_TO_BOTTOM);
+}
+
 - (IBAction)showWindow:(id)sender
 {
     if (_visible) {
@@ -399,11 +588,8 @@ void WebInspectorFrontendClient::updateWindowTitle() const
     }
 
     _visible = YES;
-    
-    _shouldAttach = _inspectorClient->inspectorStartsAttached();
-    
-    if (_shouldAttach && !_frontendClient->canAttachWindow())
-        _shouldAttach = NO;
+
+    _shouldAttach = _inspectorClient->inspectorStartsAttached() && _frontendClient->canAttachWindow() && !_inspectorClient->inspectorAttachDisabled();
 
     if (_shouldAttach) {
         WebFrameView *frameView = [[_inspectedWebView.get() mainFrame] frameView];
@@ -437,6 +623,7 @@ void WebInspectorFrontendClient::updateWindowTitle() const
         return;
 
     _inspectorClient->setInspectorStartsAttached(true);
+    _frontendClient->setAttachedWindow(InspectorFrontendClient::DOCKED_TO_BOTTOM);
 
     [self close];
     [self showWindow:nil];
@@ -448,6 +635,7 @@ void WebInspectorFrontendClient::updateWindowTitle() const
         return;
 
     _inspectorClient->setInspectorStartsAttached(false);
+    _frontendClient->setAttachedWindow(InspectorFrontendClient::UNDOCKED);
 
     [self close];
     [self showWindow:nil];
@@ -491,6 +679,11 @@ void WebInspectorFrontendClient::updateWindowTitle() const
     [frameView setFrame:frameViewRect];
 }
 
+- (void)setDockingUnavailable:(BOOL)unavailable
+{
+    _dockButton.get().hidden = unavailable;
+}
+
 - (void)destroyInspectorView:(bool)notifyInspectorController
 {
     [[_inspectedWebView.get() inspector] releaseFrontend];
@@ -507,18 +700,43 @@ void WebInspectorFrontendClient::updateWindowTitle() const
 
     if (notifyInspectorController) {
         if (Page* inspectedPage = [_inspectedWebView.get() page])
-            inspectedPage->inspectorController()->disconnectFrontend();
+            inspectedPage->inspectorController().disconnectFrontend(Inspector::InspectorDisconnectReason::InspectorDestroyed);
     }
 
+    RetainPtr<WebInspectorWindowController> protect(self);
     [_webView close];
 }
 
 // MARK: -
 // MARK: UI delegate
 
-- (NSUInteger)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
+- (void)webView:(WebView *)sender runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)resultListener allowMultipleFiles:(BOOL)allowMultipleFiles
 {
-    return WebDragDestinationActionNone;
+    NSOpenPanel *panel = [NSOpenPanel openPanel];
+    panel.canChooseDirectories = NO;
+    panel.canChooseFiles = YES;
+    panel.allowsMultipleSelection = allowMultipleFiles;
+
+    [panel beginSheetModalForWindow:_webView.window completionHandler:^(NSInteger result) {
+        if (result == NSFileHandlingPanelCancelButton) {
+            [resultListener cancel];
+            return;
+        }
+        ASSERT(result == NSFileHandlingPanelOKButton);
+
+        NSArray *URLs = panel.URLs;
+        NSMutableArray *filenames = [NSMutableArray arrayWithCapacity:URLs.count];
+        for (NSURL *URL in URLs) {
+            [filenames addObject:URL.path];
+        }
+        [resultListener chooseFilenames:filenames];
+    }];
+}
+
+- (void)webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(WebSecurityOrigin *)origin database:(NSString *)databaseIdentifier
+{
+    id <WebQuotaManager> databaseQuotaManager = origin.databaseQuotaManager;
+    databaseQuotaManager.quota = std::max<unsigned long long>(5 * 1024 * 1024, databaseQuotaManager.usage * 1.25);
 }
 
 // MARK: -
@@ -538,6 +756,13 @@ void WebInspectorFrontendClient::updateWindowTitle() const
         return;
     }
 
+    // Allow loading of the test inspector file.
+    NSString *testPagePath = [self inspectorTestPagePath];
+    if (testPagePath && [[request URL] isFileURL] && [[[request URL] path] isEqualToString:testPagePath]) {
+        [listener use];
+        return;
+    }
+
     // Prevent everything else from loading in the inspector's page.
     [listener ignore];
 
@@ -589,5 +814,4 @@ void WebInspectorFrontendClient::updateWindowTitle() const
     return YES;
 }
 
-
 @end