WebInspectorClient.mm 27 KB

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