Web Inspector: consolidate code that hosts the Inspector page inside a WKWebView
[WebKit-https.git] / Source / WebKit / UIProcess / mac / RemoteWebInspectorProxyMac.mm
1 /*
2  * Copyright (C) 2010-2016 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 "RemoteWebInspectorProxy.h"
28
29 #if PLATFORM(MAC) && WK_API_ENABLED
30
31 #import "RemoteWebInspectorProxyMessages.h"
32 #import "RemoteWebInspectorUIMessages.h"
33 #import "WKFrameInfo.h"
34 #import "WKInspectorViewController.h"
35 #import "WKNavigationAction.h"
36 #import "WKNavigationDelegate.h"
37 #import "WKWebViewInternal.h"
38 #import "WebInspectorProxy.h"
39 #import "WebPageGroup.h"
40 #import "WebPageProxy.h"
41 #import <wtf/text/Base64.h>
42
43 using namespace WebKit;
44
45 @interface WKRemoteWebInspectorProxyObjCAdapter : NSObject <WKInspectorViewControllerDelegate> {
46     RemoteWebInspectorProxy* _inspectorProxy;
47 }
48 - (instancetype)initWithRemoteWebInspectorProxy:(RemoteWebInspectorProxy*)inspectorProxy;
49 @end
50
51 @implementation WKRemoteWebInspectorProxyObjCAdapter
52
53 - (instancetype)initWithRemoteWebInspectorProxy:(RemoteWebInspectorProxy*)inspectorProxy
54 {
55     if (!(self = [super init]))
56         return nil;
57
58     _inspectorProxy = inspectorProxy;
59
60     return self;
61 }
62
63 - (void)inspectorViewControllerInspectorDidCrash:(WKInspectorViewController *)inspectorViewController
64 {
65     _inspectorProxy->closeFromCrash();
66 }
67
68 - (BOOL)inspectorViewControllerInspectorIsUnderTest:(WKInspectorViewController *)inspectorViewController
69 {
70     return _inspectorProxy->isUnderTest();
71 }
72
73 @end
74
75 namespace WebKit {
76
77 WKWebView *RemoteWebInspectorProxy::webView() const
78 {
79     return m_inspectorView.get().webView;
80 }
81
82 WebPageProxy* RemoteWebInspectorProxy::platformCreateFrontendPageAndWindow()
83 {
84     m_objCAdapter = adoptNS([[WKRemoteWebInspectorProxyObjCAdapter alloc] initWithRemoteWebInspectorProxy:this]);
85
86     m_inspectorView = adoptNS([[WKInspectorViewController alloc] initWithInspectedPage:nil]);
87     [m_inspectorView.get() setDelegate:m_objCAdapter.get()];
88
89     m_window = WebInspectorProxy::createFrontendWindow(NSZeroRect);
90
91     NSView *contentView = m_window.get().contentView;
92     [webView() setFrame:contentView.bounds];
93     [contentView addSubview:webView()];
94
95     return webView()->_page.get();
96 }
97
98 void RemoteWebInspectorProxy::platformCloseFrontendPageAndWindow()
99 {
100     if (m_window) {
101         [m_window setDelegate:nil];
102         [m_window close];
103         m_window = nil;
104     }
105
106     if (m_inspectorView) {
107         [m_inspectorView.get() setDelegate:nil];
108         m_inspectorView = nil;
109     }
110
111     if (m_objCAdapter)
112         m_objCAdapter = nil;
113 }
114
115 void RemoteWebInspectorProxy::platformBringToFront()
116 {
117     [m_window makeKeyAndOrderFront:nil];
118     [m_window makeFirstResponder:webView()];
119 }
120
121 void RemoteWebInspectorProxy::platformSave(const String& suggestedURL, const String& content, bool base64Encoded, bool forceSaveDialog)
122 {
123     // FIXME: Share with WebInspectorProxyMac.
124
125     ASSERT(!suggestedURL.isEmpty());
126     
127     NSURL *platformURL = m_suggestedToActualURLMap.get(suggestedURL).get();
128     if (!platformURL) {
129         platformURL = [NSURL URLWithString:suggestedURL];
130         // The user must confirm new filenames before we can save to them.
131         forceSaveDialog = true;
132     }
133     
134     ASSERT(platformURL);
135     if (!platformURL)
136         return;
137
138     // Necessary for the block below.
139     String suggestedURLCopy = suggestedURL;
140     String contentCopy = content;
141
142     auto saveToURL = ^(NSURL *actualURL) {
143         ASSERT(actualURL);
144
145         m_suggestedToActualURLMap.set(suggestedURLCopy, actualURL);
146
147         if (base64Encoded) {
148             Vector<char> out;
149             if (!base64Decode(contentCopy, out, Base64ValidatePadding))
150                 return;
151             RetainPtr<NSData> dataContent = adoptNS([[NSData alloc] initWithBytes:out.data() length:out.size()]);
152             [dataContent writeToURL:actualURL atomically:YES];
153         } else
154             [contentCopy writeToURL:actualURL atomically:YES encoding:NSUTF8StringEncoding error:NULL];
155
156         m_inspectorPage->process().send(Messages::RemoteWebInspectorUI::DidSave([actualURL absoluteString]), m_inspectorPage->pageID());
157     };
158
159     if (!forceSaveDialog) {
160         saveToURL(platformURL);
161         return;
162     }
163
164     NSSavePanel *panel = [NSSavePanel savePanel];
165     panel.nameFieldStringValue = platformURL.lastPathComponent;
166
167     // If we have a file URL we've already saved this file to a path and
168     // can provide a good directory to show. Otherwise, use the system's
169     // default behavior for the initial directory to show in the dialog.
170     if (platformURL.isFileURL)
171         panel.directoryURL = [platformURL URLByDeletingLastPathComponent];
172
173     auto completionHandler = ^(NSInteger result) {
174         if (result == NSModalResponseCancel)
175             return;
176         ASSERT(result == NSModalResponseOK);
177         saveToURL(panel.URL);
178     };
179
180     if (m_window)
181         [panel beginSheetModalForWindow:m_window.get() completionHandler:completionHandler];
182     else
183         completionHandler([panel runModal]);
184 }
185
186 void RemoteWebInspectorProxy::platformAppend(const String& suggestedURL, const String& content)
187 {
188     // FIXME: Share with WebInspectorProxyMac.
189
190     ASSERT(!suggestedURL.isEmpty());
191     
192     RetainPtr<NSURL> actualURL = m_suggestedToActualURLMap.get(suggestedURL);
193     // Do not append unless the user has already confirmed this filename in save().
194     if (!actualURL)
195         return;
196
197     NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:actualURL.get() error:NULL];
198     [handle seekToEndOfFile];
199     [handle writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
200     [handle closeFile];
201
202     WebPageProxy* inspectorPage = webView()->_page.get();
203     inspectorPage->process().send(Messages::RemoteWebInspectorUI::DidAppend([actualURL absoluteString]), inspectorPage->pageID());
204 }
205
206 void RemoteWebInspectorProxy::platformStartWindowDrag()
207 {
208     webView()->_page->startWindowDrag();
209 }
210
211 void RemoteWebInspectorProxy::platformOpenInNewTab(const String& url)
212 {
213     [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
214 }
215
216 } // namespace WebKit
217
218 #endif