Web Inspector: remove Node parameter from the InspectorClient::highlight
[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 "WebViewInternal.h"
42 #import <WebCore/InspectorController.h>
43 #import <WebCore/Page.h>
44 #import <WebKit/DOMExtensions.h>
45 #import <WebKitSystemInterface.h>
46 #import <wtf/PassOwnPtr.h>
47
48 using namespace WebCore;
49
50 @interface WebInspectorWindowController : NSWindowController <NSWindowDelegate> {
51 @private
52     RetainPtr<WebView> _inspectedWebView;
53     WebView *_webView;
54     WebInspectorFrontendClient* _frontendClient;
55     WebInspectorClient* _inspectorClient;
56     BOOL _attachedToInspectedWebView;
57     BOOL _shouldAttach;
58     BOOL _visible;
59     BOOL _destroyingInspectorView;
60 }
61 - (id)initWithInspectedWebView:(WebView *)webView;
62 - (WebView *)webView;
63 - (void)attach;
64 - (void)detach;
65 - (BOOL)attached;
66 - (void)setFrontendClient:(WebInspectorFrontendClient*)frontendClient;
67 - (void)setInspectorClient:(WebInspectorClient*)inspectorClient;
68 - (WebInspectorClient*)inspectorClient;
69 - (void)setAttachedWindowHeight:(unsigned)height;
70 - (void)destroyInspectorView:(bool)notifyInspectorController;
71 @end
72
73
74 // MARK: -
75
76 WebInspectorClient::WebInspectorClient(WebView *webView)
77     : m_webView(webView)
78     , m_highlighter(AdoptNS, [[WebNodeHighlighter alloc] initWithInspectedWebView:webView])
79     , m_frontendPage(0)
80 {
81 }
82
83 void WebInspectorClient::inspectorDestroyed()
84 {
85     delete this;
86 }
87
88 void WebInspectorClient::openInspectorFrontend(InspectorController* inspectorController)
89 {
90     RetainPtr<WebInspectorWindowController> windowController(AdoptNS, [[WebInspectorWindowController alloc] initWithInspectedWebView:m_webView]);
91     [windowController.get() setInspectorClient:this];
92
93     m_frontendPage = core([windowController.get() webView]);
94     OwnPtr<WebInspectorFrontendClient> frontendClient = adoptPtr(new WebInspectorFrontendClient(m_webView, windowController.get(), inspectorController, m_frontendPage, createFrontendSettings()));
95     RetainPtr<WebInspectorFrontend> webInspectorFrontend(AdoptNS, [[WebInspectorFrontend alloc] initWithFrontendClient:frontendClient.get()]);
96     [[m_webView inspector] setFrontend:webInspectorFrontend.get()];
97     m_frontendPage->inspectorController()->setInspectorFrontendClient(frontendClient.release());
98 }
99
100 void WebInspectorClient::highlight()
101 {
102     [m_highlighter.get() highlight];
103 }
104
105 void WebInspectorClient::hideHighlight()
106 {
107     [m_highlighter.get() hideHighlight];
108 }
109
110 WebInspectorFrontendClient::WebInspectorFrontendClient(WebView* inspectedWebView, WebInspectorWindowController* windowController, InspectorController* inspectorController, Page* frontendPage, WTF::PassOwnPtr<Settings> settings)
111     : InspectorFrontendClientLocal(inspectorController,  frontendPage, settings)
112     , m_inspectedWebView(inspectedWebView)
113     , m_windowController(windowController)
114 {
115     [windowController setFrontendClient:this];
116 }
117
118 void WebInspectorFrontendClient::frontendLoaded()
119 {
120     [m_windowController.get() showWindow:nil];
121     if ([m_windowController.get() attached])
122         restoreAttachedWindowHeight();
123
124     InspectorFrontendClientLocal::frontendLoaded();
125
126     WebFrame *frame = [m_inspectedWebView mainFrame];
127     
128     WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(m_inspectedWebView);
129     if (implementations->didClearInspectorWindowObjectForFrameFunc)
130         CallFrameLoadDelegate(implementations->didClearInspectorWindowObjectForFrameFunc, m_inspectedWebView,
131                               @selector(webView:didClearInspectorWindowObject:forFrame:), [frame windowObject], frame);
132
133     bool attached = [m_windowController.get() attached];
134     setAttachedWindow(attached);
135 }
136
137 String WebInspectorFrontendClient::localizedStringsURL()
138 {
139     NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"localizedStrings" ofType:@"js"];
140     if (path)
141         return [[NSURL fileURLWithPath:path] absoluteString];
142     return String();
143 }
144
145 String WebInspectorFrontendClient::hiddenPanels()
146 {
147     NSString *hiddenPanels = [[NSUserDefaults standardUserDefaults] stringForKey:@"WebKitInspectorHiddenPanels"];
148     if (hiddenPanels)
149         return hiddenPanels;
150     return String();
151 }
152
153 void WebInspectorFrontendClient::bringToFront()
154 {
155     updateWindowTitle();
156     [m_windowController.get() showWindow:nil];
157 }
158
159 void WebInspectorFrontendClient::closeWindow()
160 {
161     [m_windowController.get() destroyInspectorView:true];
162 }
163
164 void WebInspectorFrontendClient::disconnectFromBackend()
165 {
166     [m_windowController.get() destroyInspectorView:false];
167 }
168
169 void WebInspectorFrontendClient::attachWindow()
170 {
171     if ([m_windowController.get() attached])
172         return;
173     [m_windowController.get() attach];
174     restoreAttachedWindowHeight();
175 }
176
177 void WebInspectorFrontendClient::detachWindow()
178 {
179     [m_windowController.get() detach];
180 }
181
182 void WebInspectorFrontendClient::setAttachedWindowHeight(unsigned height)
183 {
184     [m_windowController.get() setAttachedWindowHeight:height];
185 }
186
187 void WebInspectorFrontendClient::inspectedURLChanged(const String& newURL)
188 {
189     m_inspectedURL = newURL;
190     updateWindowTitle();
191 }
192
193 void WebInspectorFrontendClient::saveSessionSetting(const String& key, const String& value)
194 {
195     WebInspectorClient* client = [m_windowController.get() inspectorClient];
196     if (client)
197         client->saveSessionSetting(key, value);
198 }
199
200 void WebInspectorFrontendClient::loadSessionSetting(const String& key, String* value)
201 {
202     WebInspectorClient* client = [m_windowController.get() inspectorClient];
203     if (client)
204         client->loadSessionSetting(key, value);
205 }
206
207 void WebInspectorFrontendClient::updateWindowTitle() const
208 {
209     NSString *title = [NSString stringWithFormat:UI_STRING_INTERNAL("Web Inspector — %@", "Web Inspector window title"), (NSString *)m_inspectedURL];
210     [[m_windowController.get() window] setTitle:title];
211 }
212
213
214 // MARK: -
215
216 @implementation WebInspectorWindowController
217 - (id)init
218 {
219     if (!(self = [super initWithWindow:nil]))
220         return nil;
221
222     // Keep preferences separate from the rest of the client, making sure we are using expected preference values.
223
224     WebPreferences *preferences = [[WebPreferences alloc] init];
225     [preferences setAutosaves:NO];
226     [preferences setLoadsImagesAutomatically:YES];
227     [preferences setAuthorAndUserStylesEnabled:YES];
228     [preferences setJavaScriptEnabled:YES];
229     [preferences setAllowsAnimatedImages:YES];
230     [preferences setPlugInsEnabled:NO];
231     [preferences setJavaEnabled:NO];
232     [preferences setUserStyleSheetEnabled:NO];
233     [preferences setTabsToLinks:NO];
234     [preferences setMinimumFontSize:0];
235     [preferences setMinimumLogicalFontSize:9];
236 #ifndef BUILDING_ON_LEOPARD
237     [preferences setFixedFontFamily:@"Menlo"];
238     [preferences setDefaultFixedFontSize:11];
239 #else
240     [preferences setFixedFontFamily:@"Monaco"];
241     [preferences setDefaultFixedFontSize:10];
242 #endif
243
244     _webView = [[WebView alloc] init];
245     [_webView setPreferences:preferences];
246     [_webView setDrawsBackground:NO];
247     [_webView setProhibitsMainFrameScrolling:YES];
248     [_webView setUIDelegate:self];
249
250     [preferences release];
251
252     NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"];
253     NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path]];
254     [[_webView mainFrame] loadRequest:request];
255     [request release];
256
257     [self setWindowFrameAutosaveName:@"Web Inspector 2"];
258     return self;
259 }
260
261 - (id)initWithInspectedWebView:(WebView *)webView
262 {
263     if (!(self = [self init]))
264         return nil;
265
266     _inspectedWebView = webView;
267     return self;
268 }
269
270 - (void)dealloc
271 {
272     [_webView release];
273     [super dealloc];
274 }
275
276 // MARK: -
277
278 - (WebView *)webView
279 {
280     return _webView;
281 }
282
283 - (NSWindow *)window
284 {
285     NSWindow *window = [super window];
286     if (window)
287         return window;
288
289     NSUInteger styleMask = (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask);
290
291     styleMask |= NSTexturedBackgroundWindowMask;
292
293     window = [[NSWindow alloc] initWithContentRect:NSMakeRect(60.0, 200.0, 750.0, 650.0) styleMask:styleMask backing:NSBackingStoreBuffered defer:NO];
294     [window setDelegate:self];
295     [window setMinSize:NSMakeSize(400.0, 400.0)];
296
297     [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
298     [window setContentBorderThickness:55. forEdge:NSMaxYEdge];
299
300     WKNSWindowMakeBottomCornersSquare(window);
301
302     [self setWindow:window];
303     [window release];
304
305     return window;
306 }
307
308 // MARK: -
309
310 - (BOOL)windowShouldClose:(id)sender
311 {
312     [self destroyInspectorView:true];
313
314     return YES;
315 }
316
317 - (void)close
318 {
319     if (!_visible)
320         return;
321
322     _visible = NO;
323
324     if (_attachedToInspectedWebView) {
325         if ([_inspectedWebView.get() _isClosed])
326             return;
327
328         [_webView removeFromSuperview];
329
330         WebFrameView *frameView = [[_inspectedWebView.get() mainFrame] frameView];
331         NSRect frameViewRect = [frameView frame];
332
333         // Setting the height based on the previous height is done to work with
334         // Safari's find banner. This assumes the previous height is the Y origin.
335         frameViewRect.size.height += NSMinY(frameViewRect);
336         frameViewRect.origin.y = 0.0;
337
338         [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
339         [frameView setFrame:frameViewRect];
340
341         [_inspectedWebView.get() displayIfNeeded];
342     } else
343         [super close];
344 }
345
346 - (IBAction)showWindow:(id)sender
347 {
348     if (_visible) {
349         if (!_attachedToInspectedWebView)
350             [super showWindow:sender]; // call super so the window will be ordered front if needed
351         return;
352     }
353
354     _visible = YES;
355     
356     _shouldAttach = _inspectorClient->inspectorStartsAttached();
357     
358     if (_shouldAttach && !_frontendClient->canAttachWindow())
359         _shouldAttach = NO;
360
361     if (_shouldAttach) {
362         WebFrameView *frameView = [[_inspectedWebView.get() mainFrame] frameView];
363
364         [_webView removeFromSuperview];
365         [_inspectedWebView.get() addSubview:_webView positioned:NSWindowBelow relativeTo:(NSView *)frameView];
366
367         [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMaxYMargin)];
368         [frameView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable | NSViewMinYMargin)];
369
370         _attachedToInspectedWebView = YES;
371     } else {
372         _attachedToInspectedWebView = NO;
373
374         NSView *contentView = [[self window] contentView];
375         [_webView setFrame:[contentView frame]];
376         [_webView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
377         [_webView removeFromSuperview];
378         [contentView addSubview:_webView];
379
380         [super showWindow:nil];
381     }
382 }
383
384 // MARK: -
385
386 - (void)attach
387 {
388     if (_attachedToInspectedWebView)
389         return;
390
391     _inspectorClient->setInspectorStartsAttached(true);
392
393     [self close];
394     [self showWindow:nil];
395 }
396
397 - (void)detach
398 {
399     if (!_attachedToInspectedWebView)
400         return;
401
402     _inspectorClient->setInspectorStartsAttached(false);
403
404     [self close];
405     [self showWindow:nil];
406 }
407
408 - (BOOL)attached
409 {
410     return _attachedToInspectedWebView;
411 }
412
413 - (void)setFrontendClient:(WebInspectorFrontendClient*)frontendClient
414 {
415     _frontendClient = frontendClient;
416 }
417
418 - (void)setInspectorClient:(WebInspectorClient*)inspectorClient
419 {
420     _inspectorClient = inspectorClient;
421 }
422
423 - (WebInspectorClient*)inspectorClient
424 {
425     return _inspectorClient;
426 }
427
428 - (void)setAttachedWindowHeight:(unsigned)height
429 {
430     if (!_attachedToInspectedWebView)
431         return;
432
433     WebFrameView *frameView = [[_inspectedWebView.get() mainFrame] frameView];
434     NSRect frameViewRect = [frameView frame];
435
436     // Setting the height based on the difference is done to work with
437     // Safari's find banner. This assumes the previous height is the Y origin.
438     CGFloat heightDifference = (NSMinY(frameViewRect) - height);
439     frameViewRect.size.height += heightDifference;
440     frameViewRect.origin.y = height;
441
442     [_webView setFrame:NSMakeRect(0.0, 0.0, NSWidth(frameViewRect), height)];
443     [frameView setFrame:frameViewRect];
444 }
445
446 - (void)destroyInspectorView:(bool)notifyInspectorController
447 {
448     if (_destroyingInspectorView)
449         return;
450     _destroyingInspectorView = YES;
451
452     if (_attachedToInspectedWebView)
453         [self close];
454
455     _visible = NO;
456
457     if (notifyInspectorController) {
458         if (Page* inspectedPage = [_inspectedWebView.get() page])
459             inspectedPage->inspectorController()->disconnectFrontend();
460
461         _inspectorClient->releaseFrontendPage();
462     }
463
464     [_webView close];
465 }
466
467 // MARK: -
468 // MARK: UI delegate
469
470 - (NSUInteger)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
471 {
472     return WebDragDestinationActionNone;
473 }
474
475 // MARK: -
476 // These methods can be used by UI elements such as menu items and toolbar buttons when the inspector is the key window.
477
478 // This method is really only implemented to keep any UI elements enabled.
479 - (void)showWebInspector:(id)sender
480 {
481     [[_inspectedWebView.get() inspector] show:sender];
482 }
483
484 - (void)showErrorConsole:(id)sender
485 {
486     [[_inspectedWebView.get() inspector] showConsole:sender];
487 }
488
489 - (void)toggleDebuggingJavaScript:(id)sender
490 {
491     [[_inspectedWebView.get() inspector] toggleDebuggingJavaScript:sender];
492 }
493
494 - (void)toggleProfilingJavaScript:(id)sender
495 {
496     [[_inspectedWebView.get() inspector] toggleProfilingJavaScript:sender];
497 }
498
499 - (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
500 {
501     BOOL isMenuItem = [(id)item isKindOfClass:[NSMenuItem class]];
502     if ([item action] == @selector(toggleDebuggingJavaScript:) && isMenuItem) {
503         NSMenuItem *menuItem = (NSMenuItem *)item;
504         if ([[_inspectedWebView.get() inspector] isDebuggingJavaScript])
505             [menuItem setTitle:UI_STRING_INTERNAL("Stop Debugging JavaScript", "title for Stop Debugging JavaScript menu item")];
506         else
507             [menuItem setTitle:UI_STRING_INTERNAL("Start Debugging JavaScript", "title for Start Debugging JavaScript menu item")];
508     } else if ([item action] == @selector(toggleProfilingJavaScript:) && isMenuItem) {
509         NSMenuItem *menuItem = (NSMenuItem *)item;
510         if ([[_inspectedWebView.get() inspector] isProfilingJavaScript])
511             [menuItem setTitle:UI_STRING_INTERNAL("Stop Profiling JavaScript", "title for Stop Profiling JavaScript menu item")];
512         else
513             [menuItem setTitle:UI_STRING_INTERNAL("Start Profiling JavaScript", "title for Start Profiling JavaScript menu item")];
514     }
515
516     return YES;
517 }
518
519
520 @end