Web Inspector: consolidate code that hosts the Inspector page inside a WKWebView
[WebKit-https.git] / Source / WebKit / UIProcess / mac / WKInspectorViewController.mm
1 /*
2  * Copyright (C) 2017 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKInspectorViewController.h"
28
29 #if PLATFORM(MAC) && WK_API_ENABLED
30
31 #import "WKFrameInfo.h"
32 #import "WKNavigationAction.h"
33 #import "WKNavigationDelegate.h"
34 #import "WKOpenPanelParameters.h"
35 #import "WKPreferencesPrivate.h"
36 #import "WKProcessPoolInternal.h"
37 #import "WKUIDelegatePrivate.h"
38 #import "WKWebView.h"
39 #import "WKWebViewConfigurationPrivate.h"
40 #import "WeakObjCPtr.h"
41 #import "WebInspectorProxy.h"
42 #import "WebInspectorUtilities.h"
43 #import "WebPageProxy.h"
44
45 // FIXME: this should be declared in the ObjC API; currently it's in the C SPI.
46 const NSInteger WKInspectorViewTag = 1000;
47
48 using namespace WebKit;
49
50 // Clients need to be able to tell whether a subview is a docked inspector view, so override the tag.
51 @interface WKInspectorWKWebView : WKWebView
52 @end
53
54 @implementation WKInspectorWKWebView
55 - (NSInteger)tag
56 {
57     return WKInspectorViewTag;
58 }
59 @end
60
61 @interface WKInspectorViewController () <WKUIDelegate, WKNavigationDelegate>
62 @end
63
64 @implementation WKInspectorViewController {
65     WebPageProxy* _inspectedPage;
66     RetainPtr<WKInspectorWKWebView> _webView;
67     WebKit::WeakObjCPtr<id <WKInspectorViewControllerDelegate>> _delegate;
68 }
69
70 - (instancetype)initWithInspectedPage:(WebKit::WebPageProxy* _Nullable)inspectedPage
71 {
72     if (!(self = [super init]))
73         return nil;
74
75     // The (local) inspected page is nil if the controller is hosting a Remote Web Inspector view.
76     _inspectedPage = inspectedPage;
77
78     return self;
79 }
80
81 - (void)dealloc
82 {
83     if (_webView) {
84         [_webView setUIDelegate:nil];
85         [_webView setNavigationDelegate:nil];
86         _webView = nil;
87     }
88
89     [super dealloc];
90 }
91
92 - (id <WKInspectorViewControllerDelegate>)delegate
93 {
94     return _delegate.getAutoreleased();
95 }
96
97 - (WKWebView *)webView
98 {
99     // Construct lazily so the client can set the delegate before the WebView is created.
100     if (!_webView) {
101         NSRect initialFrame = NSMakeRect(0, 0, WebInspectorProxy::initialWindowWidth, WebInspectorProxy::initialWindowHeight);
102         _webView = adoptNS([[WKInspectorWKWebView alloc] initWithFrame:initialFrame configuration:[self configuration]]);
103         [_webView setUIDelegate:self];
104         [_webView setNavigationDelegate:self];
105         [_webView _setAutomaticallyAdjustsContentInsets:NO];
106         [_webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
107     }
108
109     return _webView.get();
110 }
111
112 - (void)setDelegate:(id <WKInspectorViewControllerDelegate>)delegate
113 {
114     _delegate = delegate;
115 }
116
117 - (WKWebViewConfiguration *)configuration
118 {
119     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
120
121     WKPreferences *preferences = configuration.get().preferences;
122     preferences._allowFileAccessFromFileURLs = YES;
123     [configuration _setAllowUniversalAccessFromFileURLs:YES];
124     preferences._storageBlockingPolicy = _WKStorageBlockingPolicyAllowAll;
125     preferences._javaScriptRuntimeFlags = 0;
126
127 #ifndef NDEBUG
128     // Allow developers to inspect the Web Inspector in debug builds without changing settings.
129     preferences._developerExtrasEnabled = YES;
130     preferences._logsPageMessagesToSystemConsoleEnabled = YES;
131 #endif
132
133     if (!!_delegate && [_delegate respondsToSelector:@selector(inspectorViewControllerInspectorIsUnderTest:)]) {
134         if ([_delegate inspectorViewControllerInspectorIsUnderTest:self]) {
135             preferences._hiddenPageDOMTimerThrottlingEnabled = NO;
136             preferences._pageVisibilityBasedProcessSuppressionEnabled = NO;
137         }
138     }
139
140     [configuration setProcessPool: ::WebKit::wrapper(inspectorProcessPool(inspectorLevelForPage(_inspectedPage)))];
141     [configuration _setGroupIdentifier:inspectorPageGroupIdentifierForPage(_inspectedPage)];
142
143     return configuration.autorelease();
144 }
145
146 // MARK: WKUIDelegate methods
147
148 - (void)_webView:(WKWebView *)webView getWindowFrameWithCompletionHandler:(void (^)(CGRect))completionHandler
149 {
150     if (!_webView.get().window)
151         completionHandler(CGRectZero);
152     else
153         completionHandler(NSRectToCGRect([webView frame]));
154 }
155
156 - (void)_webView:(WKWebView *)webView setWindowFrame:(CGRect)frame
157 {
158     if (!_webView.get().window)
159         return;
160
161     [_webView.get().window setFrame:NSRectFromCGRect(frame) display:YES];
162 }
163
164 - (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler
165 {
166     NSOpenPanel *openPanel = [NSOpenPanel openPanel];
167     openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
168
169     auto reportSelectedFiles = ^(NSInteger result) {
170         if (result == NSModalResponseOK)
171             completionHandler(openPanel.URLs);
172         else
173             completionHandler(nil);
174     };
175
176     if (_webView.get().window)
177         [openPanel beginSheetModalForWindow:_webView.get().window completionHandler:reportSelectedFiles];
178     else
179         reportSelectedFiles([openPanel runModal]);
180 }
181
182 - (void)_webView:(WKWebView *)webView decideDatabaseQuotaForSecurityOrigin:(WKSecurityOrigin *)securityOrigin currentQuota:(unsigned long long)currentQuota currentOriginUsage:(unsigned long long)currentOriginUsage currentDatabaseUsage:(unsigned long long)currentUsage expectedUsage:(unsigned long long)expectedUsage decisionHandler:(void (^)(unsigned long long newQuota))decisionHandler
183 {
184     decisionHandler(std::max<unsigned long long>(expectedUsage, currentUsage * 1.25));
185 }
186
187 // MARK: WKNavigationDelegate methods
188
189 - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
190 {
191     if (!!_delegate && [_delegate respondsToSelector:@selector(inspectorViewControllerInspectorDidCrash:)])
192         [_delegate inspectorViewControllerInspectorDidCrash:self];
193 }
194
195 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
196 {
197     // Allow non-main frames to navigate anywhere.
198     if (!navigationAction.targetFrame.isMainFrame) {
199         decisionHandler(WKNavigationActionPolicyAllow);
200         return;
201     }
202
203     // Allow loading of the main inspector file.
204     if (WebInspectorProxy::isMainOrTestInspectorPage(navigationAction.request.URL)) {
205         decisionHandler(WKNavigationActionPolicyAllow);
206         return;
207     }
208
209     // Prevent everything else.
210     decisionHandler(WKNavigationActionPolicyCancel);
211 }
212
213 @end
214
215 #endif // PLATFORM(MAC) && WK_API_ENABLED