1/* 2 * Copyright (C) 2010 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 "WebInspectorProxy.h" 28 29#if ENABLE(INSPECTOR) 30 31#import "WKAPICast.h" 32#import "WebContext.h" 33#import "WKInspectorPrivateMac.h" 34#import "WKMutableArray.h" 35#import "WKOpenPanelParameters.h" 36#import "WKOpenPanelResultListener.h" 37#import "WKRetainPtr.h" 38#import "WKURLCF.h" 39#import "WKViewPrivate.h" 40#import "WebInspectorMessages.h" 41#import "WebPageGroup.h" 42#import "WebPageProxy.h" 43#import "WebPreferences.h" 44#import "WebProcessProxy.h" 45#import <algorithm> 46#import <mach-o/dyld.h> 47#import <WebKitSystemInterface.h> 48#import <WebCore/InspectorFrontendClientLocal.h> 49#import <WebCore/LocalizedStrings.h> 50#import <WebCore/SoftLinking.h> 51#import <wtf/text/WTFString.h> 52 53SOFT_LINK_STAGED_FRAMEWORK(WebInspectorUI, PrivateFrameworks, A) 54 55using namespace WebCore; 56using namespace WebKit; 57 58// The height needed to match a typical NSToolbar. 59static const CGFloat windowContentBorderThickness = 55; 60 61// The margin from the top and right of the dock button (same as the full screen button). 62static const CGFloat dockButtonMargin = 3; 63 64// The spacing between the dock buttons. 65static const CGFloat dockButtonSpacing = dockButtonMargin * 2; 66 67static const NSUInteger windowStyleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask | NSTexturedBackgroundWindowMask; 68 69// WKWebInspectorProxyObjCAdapter is a helper ObjC object used as a delegate or notification observer 70// for the sole purpose of getting back into the C++ code from an ObjC caller. 71 72@interface WKWebInspectorProxyObjCAdapter () 73 74- (id)initWithWebInspectorProxy:(WebInspectorProxy*)inspectorProxy; 75- (void)close; 76 77@end 78 79@implementation WKWebInspectorProxyObjCAdapter 80 81- (WKInspectorRef)inspectorRef 82{ 83 return toAPI(static_cast<WebInspectorProxy*>(_inspectorProxy)); 84} 85 86- (id)initWithWebInspectorProxy:(WebInspectorProxy*)inspectorProxy 87{ 88 ASSERT_ARG(inspectorProxy, inspectorProxy); 89 90 if (!(self = [super init])) 91 return nil; 92 93 _inspectorProxy = static_cast<void*>(inspectorProxy); // Not retained to prevent cycles 94 95 return self; 96} 97 98- (IBAction)attachRight:(id)sender 99{ 100 static_cast<WebInspectorProxy*>(_inspectorProxy)->attach(AttachmentSideRight); 101} 102 103- (IBAction)attachBottom:(id)sender 104{ 105 static_cast<WebInspectorProxy*>(_inspectorProxy)->attach(AttachmentSideBottom); 106} 107 108- (void)close 109{ 110 _inspectorProxy = 0; 111} 112 113- (void)windowDidMove:(NSNotification *)notification 114{ 115 static_cast<WebInspectorProxy*>(_inspectorProxy)->windowFrameDidChange(); 116} 117 118- (void)windowDidResize:(NSNotification *)notification 119{ 120 static_cast<WebInspectorProxy*>(_inspectorProxy)->windowFrameDidChange(); 121} 122 123- (void)windowWillClose:(NSNotification *)notification 124{ 125 static_cast<WebInspectorProxy*>(_inspectorProxy)->close(); 126} 127 128- (void)inspectedViewFrameDidChange:(NSNotification *)notification 129{ 130 // Resizing the views while inside this notification can lead to bad results when entering 131 // or exiting full screen. To avoid that we need to perform the work after a delay. We only 132 // depend on this for enforcing the height constraints, so a small delay isn't terrible. Most 133 // of the time the views will already have the correct frames because of autoresizing masks. 134 135 dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^{ 136 if (!_inspectorProxy) 137 return; 138 static_cast<WebInspectorProxy*>(_inspectorProxy)->inspectedViewFrameDidChange(); 139 }); 140} 141 142@end 143 144@interface WKWebInspectorWKView : WKView 145@end 146 147@implementation WKWebInspectorWKView 148 149- (NSInteger)tag 150{ 151 return WKInspectorViewTag; 152} 153 154@end 155 156@interface NSWindow (AppKitDetails) 157- (NSCursor *)_cursorForResizeDirection:(NSInteger)direction; 158- (NSRect)_customTitleFrame; 159@end 160 161@interface WKWebInspectorWindow : NSWindow { 162@public 163 RetainPtr<NSButton> _dockBottomButton; 164 RetainPtr<NSButton> _dockRightButton; 165} 166@end 167 168@implementation WKWebInspectorWindow 169 170- (NSCursor *)_cursorForResizeDirection:(NSInteger)direction 171{ 172 // Don't show a resize cursor for the northeast (top right) direction if the dock button is visible. 173 // This matches what happens when the full screen button is visible. 174 if (direction == 1 && ![_dockRightButton isHidden]) 175 return nil; 176 return [super _cursorForResizeDirection:direction]; 177} 178 179- (NSRect)_customTitleFrame 180{ 181 // Adjust the title frame if needed to prevent it from intersecting the dock button. 182 NSRect titleFrame = [super _customTitleFrame]; 183 NSRect dockButtonFrame = _dockBottomButton.get().frame; 184 if (NSMaxX(titleFrame) > NSMinX(dockButtonFrame) - dockButtonMargin) 185 titleFrame.size.width -= (NSMaxX(titleFrame) - NSMinX(dockButtonFrame)) + dockButtonMargin; 186 return titleFrame; 187} 188 189@end 190 191namespace WebKit { 192 193static bool inspectorReallyUsesWebKitUserInterface(WebPreferences* preferences) 194{ 195 // This matches a similar check in WebInspectorMac.mm. Keep them in sync. 196 197 // Call the soft link framework function to dlopen it, then [NSBundle bundleWithIdentifier:] will work. 198 WebInspectorUILibrary(); 199 200 if (![[NSBundle bundleWithIdentifier:@"com.apple.WebInspectorUI"] pathForResource:@"Main" ofType:@"html"]) 201 return true; 202 203 if (![[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"]) 204 return false; 205 206 return preferences->inspectorUsesWebKitUserInterface(); 207} 208 209static WKRect getWindowFrame(WKPageRef, const void* clientInfo) 210{ 211 WebInspectorProxy* webInspectorProxy = static_cast<WebInspectorProxy*>(const_cast<void*>(clientInfo)); 212 ASSERT(webInspectorProxy); 213 214 return webInspectorProxy->inspectorWindowFrame(); 215} 216 217static void setWindowFrame(WKPageRef, WKRect frame, const void* clientInfo) 218{ 219 WebInspectorProxy* webInspectorProxy = static_cast<WebInspectorProxy*>(const_cast<void*>(clientInfo)); 220 ASSERT(webInspectorProxy); 221 222 webInspectorProxy->setInspectorWindowFrame(frame); 223} 224 225static unsigned long long exceededDatabaseQuota(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKStringRef, WKStringRef, unsigned long long, unsigned long long, unsigned long long currentDatabaseUsage, unsigned long long expectedUsage, const void*) 226{ 227 return std::max<unsigned long long>(expectedUsage, currentDatabaseUsage * 1.25); 228} 229 230static void runOpenPanel(WKPageRef page, WKFrameRef frame, WKOpenPanelParametersRef parameters, WKOpenPanelResultListenerRef listener, const void* clientInfo) 231{ 232 WebInspectorProxy* webInspectorProxy = static_cast<WebInspectorProxy*>(const_cast<void*>(clientInfo)); 233 ASSERT(webInspectorProxy); 234 235 NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 236 [openPanel setAllowsMultipleSelection:WKOpenPanelParametersGetAllowsMultipleFiles(parameters)]; 237 238 WKRetain(listener); 239 240 // If the inspector is detached, then openPanel will be window-modal; otherwise, openPanel is opened in a new window. 241 [openPanel beginSheetModalForWindow:webInspectorProxy->inspectorWindow() completionHandler:^(NSInteger result) { 242 if (result == NSFileHandlingPanelOKButton) { 243 WKMutableArrayRef fileURLs = WKMutableArrayCreate(); 244 245 for (NSURL* nsURL in [openPanel URLs]) { 246 WKURLRef wkURL = WKURLCreateWithCFURL(reinterpret_cast<CFURLRef>(nsURL)); 247 WKArrayAppendItem(fileURLs, wkURL); 248 WKRelease(wkURL); 249 } 250 251 WKOpenPanelResultListenerChooseFiles(listener, fileURLs); 252 253 WKRelease(fileURLs); 254 } else 255 WKOpenPanelResultListenerCancel(listener); 256 257 WKRelease(listener); 258 }]; 259} 260 261void WebInspectorProxy::setInspectorWindowFrame(WKRect& frame) 262{ 263 if (m_isAttached) 264 return; 265 [m_inspectorWindow setFrame:NSMakeRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height) display:YES]; 266} 267 268WKRect WebInspectorProxy::inspectorWindowFrame() 269{ 270 if (m_isAttached) 271 return WKRectMake(0, 0, 0, 0); 272 273 NSRect frame = m_inspectorWindow.get().frame; 274 return WKRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); 275} 276 277static NSButton *createDockButton(NSString *imageName) 278{ 279 // Create a full screen button so we can turn it into a dock button. 280 NSButton *dockButton = [NSWindow standardWindowButton:NSWindowFullScreenButton forStyleMask:windowStyleMask]; 281 282 // Set the autoresizing mask to keep the dock button pinned to the top right corner. 283 dockButton.autoresizingMask = NSViewMinXMargin | NSViewMinYMargin; 284 285 // Get the dock image and make it a template so the button cell effects will apply. 286 NSImage *dockImage = [[NSBundle bundleForClass:[WKWebInspectorWKView class]] imageForResource:imageName]; 287 [dockImage setTemplate:YES]; 288 289 // Set the dock image on the button cell. 290 NSCell *dockButtonCell = dockButton.cell; 291 dockButtonCell.image = dockImage; 292 293 return [dockButton retain]; 294} 295 296void WebInspectorProxy::createInspectorWindow() 297{ 298 ASSERT(!m_inspectorWindow); 299 300 NSRect windowFrame = NSMakeRect(0, 0, initialWindowWidth, initialWindowHeight); 301 302 // Restore the saved window frame, if there was one. 303 NSString *savedWindowFrameString = page()->pageGroup()->preferences()->inspectorWindowFrame(); 304 NSRect savedWindowFrame = NSRectFromString(savedWindowFrameString); 305 if (!NSIsEmptyRect(savedWindowFrame)) 306 windowFrame = savedWindowFrame; 307 308 WKWebInspectorWindow *window = [[WKWebInspectorWindow alloc] initWithContentRect:windowFrame styleMask:windowStyleMask backing:NSBackingStoreBuffered defer:NO]; 309 [window setDelegate:m_inspectorProxyObjCAdapter.get()]; 310 [window setMinSize:NSMakeSize(minimumWindowWidth, minimumWindowHeight)]; 311 [window setReleasedWhenClosed:NO]; 312 [window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge]; 313 [window setContentBorderThickness:windowContentBorderThickness forEdge:NSMaxYEdge]; 314 WKNSWindowMakeBottomCornersSquare(window); 315 316 m_inspectorWindow = adoptNS(window); 317 318 NSView *contentView = [window contentView]; 319 320 static const int32_t firstVersionOfSafariWithDockToRightSupport = 0x02181d0d; // 536.29.13 321 static bool supportsDockToRight = NSVersionOfLinkTimeLibrary("Safari") >= firstVersionOfSafariWithDockToRightSupport; 322 323 m_dockBottomButton = adoptNS(createDockButton(@"DockBottom")); 324 m_dockRightButton = adoptNS(createDockButton(@"DockRight")); 325 326 m_dockBottomButton.get().target = m_inspectorProxyObjCAdapter.get(); 327 m_dockBottomButton.get().action = @selector(attachBottom:); 328 329 m_dockRightButton.get().target = m_inspectorProxyObjCAdapter.get(); 330 m_dockRightButton.get().action = @selector(attachRight:); 331 m_dockRightButton.get().enabled = supportsDockToRight; 332 m_dockRightButton.get().alphaValue = supportsDockToRight ? 1 : 0.5; 333 334 // Store the dock buttons on the window too so it can check its visibility. 335 window->_dockBottomButton = m_dockBottomButton; 336 window->_dockRightButton = m_dockRightButton; 337 338 // Get the frame view, the superview of the content view, and its frame. 339 // This will be the superview of the dock button too. 340 NSView *frameView = contentView.superview; 341 NSRect frameViewBounds = frameView.bounds; 342 NSSize dockButtonSize = m_dockBottomButton.get().frame.size; 343 344 ASSERT(!frameView.isFlipped); 345 346 // Position the dock button in the corner to match where the full screen button is normally. 347 NSPoint dockButtonOrigin; 348 dockButtonOrigin.x = NSMaxX(frameViewBounds) - dockButtonSize.width - dockButtonMargin; 349 dockButtonOrigin.y = NSMaxY(frameViewBounds) - dockButtonSize.height - dockButtonMargin; 350 m_dockRightButton.get().frameOrigin = dockButtonOrigin; 351 352 dockButtonOrigin.x -= dockButtonSize.width + dockButtonSpacing; 353 m_dockBottomButton.get().frameOrigin = dockButtonOrigin; 354 355 [frameView addSubview:m_dockBottomButton.get()]; 356 [frameView addSubview:m_dockRightButton.get()]; 357 358 // Hide the dock buttons if we can't attach. 359 m_dockBottomButton.get().hidden = !canAttach(); 360 m_dockRightButton.get().hidden = !canAttach(); 361 362 [m_inspectorView.get() setFrame:[contentView bounds]]; 363 [m_inspectorView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 364 [contentView addSubview:m_inspectorView.get()]; 365 366 // Center the window if the saved frame was empty. 367 if (NSIsEmptyRect(savedWindowFrame)) 368 [window center]; 369 370 updateInspectorWindowTitle(); 371} 372 373void WebInspectorProxy::updateInspectorWindowTitle() const 374{ 375 if (!m_inspectorWindow) 376 return; 377 378 NSString *title = [NSString stringWithFormat:WEB_UI_STRING("Web Inspector — %@", "Web Inspector window title"), (NSString *)m_urlString]; 379 [m_inspectorWindow.get() setTitle:title]; 380} 381 382WebPageProxy* WebInspectorProxy::platformCreateInspectorPage() 383{ 384 ASSERT(m_page); 385 ASSERT(!m_inspectorView); 386 387 NSRect initialRect; 388 if (m_isAttached) { 389 NSRect inspectedViewFrame = m_page->wkView().frame; 390 391 switch (m_attachmentSide) { 392 case AttachmentSideBottom: 393 initialRect = NSMakeRect(0, 0, NSWidth(inspectedViewFrame), inspectorPageGroup()->preferences()->inspectorAttachedHeight()); 394 break; 395 case AttachmentSideRight: 396 initialRect = NSMakeRect(0, 0, inspectorPageGroup()->preferences()->inspectorAttachedWidth(), NSHeight(inspectedViewFrame)); 397 break; 398 } 399 } else { 400 initialRect = NSMakeRect(0, 0, initialWindowWidth, initialWindowHeight); 401 402 NSString *windowFrameString = page()->pageGroup()->preferences()->inspectorWindowFrame(); 403 NSRect windowFrame = NSRectFromString(windowFrameString); 404 if (!NSIsEmptyRect(windowFrame)) 405 initialRect = [NSWindow contentRectForFrameRect:windowFrame styleMask:windowStyleMask]; 406 } 407 408 m_inspectorView = adoptNS([[WKWebInspectorWKView alloc] initWithFrame:initialRect contextRef:toAPI(page()->process()->context()) pageGroupRef:toAPI(inspectorPageGroup()) relatedToPage:toAPI(m_page)]); 409 ASSERT(m_inspectorView); 410 411 [m_inspectorView.get() setDrawsBackground:NO]; 412 413 m_inspectorProxyObjCAdapter = adoptNS([[WKWebInspectorProxyObjCAdapter alloc] initWithWebInspectorProxy:this]); 414 415 WebPageProxy* inspectorPage = toImpl(m_inspectorView.get().pageRef); 416 417 WKPageUIClient uiClient = { 418 kWKPageUIClientCurrentVersion, 419 this, /* clientInfo */ 420 0, // createNewPage_deprecatedForUseWithV0 421 0, // showPage 422 0, // closePage 423 0, // takeFocus 424 0, // focus 425 0, // unfocus 426 0, // runJavaScriptAlert 427 0, // runJavaScriptConfirm 428 0, // runJavaScriptPrompt 429 0, // setStatusText 430 0, // mouseDidMoveOverElement_deprecatedForUseWithV0 431 0, // missingPluginButtonClicked_deprecatedForUseWithV0 432 0, // didNotHandleKeyEvent 433 0, // didNotHandleWheelEvent 434 0, // areToolbarsVisible 435 0, // setToolbarsVisible 436 0, // isMenuBarVisible 437 0, // setMenuBarVisible 438 0, // isStatusBarVisible 439 0, // setStatusBarVisible 440 0, // isResizable 441 0, // setResizable 442 getWindowFrame, 443 setWindowFrame, 444 0, // runBeforeUnloadConfirmPanel 445 0, // didDraw 446 0, // pageDidScroll 447 exceededDatabaseQuota, 448 runOpenPanel, 449 0, // decidePolicyForGeolocationPermissionRequest 450 0, // headerHeight 451 0, // footerHeight 452 0, // drawHeader 453 0, // drawFooter 454 0, // printFrame 455 0, // runModal 456 0, // unused 457 0, // saveDataToFileInDownloadsFolder 458 0, // shouldInterruptJavaScript 459 0, // createPage 460 0, // mouseDidMoveOverElement 461 0, // decidePolicyForNotificationPermissionRequest 462 0, // unavailablePluginButtonClicked_deprecatedForUseWithV1 463 0, // showColorPicker 464 0, // hideColorPicker 465 0, // unavailablePluginButtonClicked 466 }; 467 468 inspectorPage->initializeUIClient(&uiClient); 469 470 return inspectorPage; 471} 472 473void WebInspectorProxy::platformOpen() 474{ 475 if (m_isAttached) 476 platformAttach(); 477 else 478 createInspectorWindow(); 479 480 platformBringToFront(); 481} 482 483void WebInspectorProxy::platformDidClose() 484{ 485 if (m_inspectorWindow) { 486 [m_inspectorWindow.get() setDelegate:nil]; 487 [m_inspectorWindow.get() orderOut:nil]; 488 m_inspectorWindow = 0; 489 } 490 491 m_inspectorView = 0; 492 493 [m_inspectorProxyObjCAdapter.get() close]; 494 m_inspectorProxyObjCAdapter = 0; 495} 496 497void WebInspectorProxy::platformHide() 498{ 499 if (m_isAttached) { 500 platformDetach(); 501 return; 502 } 503 504 if (m_inspectorWindow) { 505 [m_inspectorWindow.get() setDelegate:nil]; 506 [m_inspectorWindow.get() orderOut:nil]; 507 m_inspectorWindow = 0; 508 } 509} 510 511void WebInspectorProxy::platformBringToFront() 512{ 513 // If the Web Inspector is no longer in the same window as the inspected view, 514 // then we need to reopen the Inspector to get it attached to the right window. 515 // This can happen when dragging tabs to another window in Safari. 516 if (m_isAttached && m_inspectorView.get().window != m_page->wkView().window) { 517 platformOpen(); 518 return; 519 } 520 521 // FIXME <rdar://problem/10937688>: this will not bring a background tab in Safari to the front, only its window. 522 [m_inspectorView.get().window makeKeyAndOrderFront:nil]; 523 [m_inspectorView.get().window makeFirstResponder:m_inspectorView.get()]; 524} 525 526bool WebInspectorProxy::platformIsFront() 527{ 528 // FIXME <rdar://problem/10937688>: this will not return false for a background tab in Safari, only a background window. 529 return m_isVisible && [m_inspectorView.get().window isMainWindow]; 530} 531 532void WebInspectorProxy::platformAttachAvailabilityChanged(bool available) 533{ 534 m_dockBottomButton.get().hidden = !available; 535 m_dockRightButton.get().hidden = !available; 536} 537 538void WebInspectorProxy::platformInspectedURLChanged(const String& urlString) 539{ 540 m_urlString = urlString; 541 542 updateInspectorWindowTitle(); 543} 544 545void WebInspectorProxy::platformSave(const String& suggestedURL, const String& content, bool forceSaveDialog) 546{ 547 ASSERT(!suggestedURL.isEmpty()); 548 549 NSURL *platformURL = m_suggestedToActualURLMap.get(suggestedURL).get(); 550 if (!platformURL) { 551 platformURL = [NSURL URLWithString:suggestedURL]; 552 // The user must confirm new filenames before we can save to them. 553 forceSaveDialog = true; 554 } 555 556 ASSERT(platformURL); 557 if (!platformURL) 558 return; 559 560 // Necessary for the block below. 561 String suggestedURLCopy = suggestedURL; 562 String contentCopy = content; 563 564 auto saveToURL = ^(NSURL *actualURL) { 565 ASSERT(actualURL); 566 567 m_suggestedToActualURLMap.set(suggestedURLCopy, actualURL); 568 [contentCopy writeToURL:actualURL atomically:YES encoding:NSUTF8StringEncoding error:NULL]; 569 m_page->process()->send(Messages::WebInspector::DidSave([actualURL absoluteString]), m_page->pageID()); 570 }; 571 572 if (!forceSaveDialog) { 573 saveToURL(platformURL); 574 return; 575 } 576 577 NSSavePanel *panel = [NSSavePanel savePanel]; 578 panel.nameFieldStringValue = platformURL.lastPathComponent; 579 panel.directoryURL = [platformURL URLByDeletingLastPathComponent]; 580 581 [panel beginSheetModalForWindow:m_inspectorWindow.get() completionHandler:^(NSInteger result) { 582 if (result == NSFileHandlingPanelCancelButton) 583 return; 584 ASSERT(result == NSFileHandlingPanelOKButton); 585 saveToURL(panel.URL); 586 }]; 587} 588 589void WebInspectorProxy::platformAppend(const String& suggestedURL, const String& content) 590{ 591 ASSERT(!suggestedURL.isEmpty()); 592 593 RetainPtr<NSURL> actualURL = m_suggestedToActualURLMap.get(suggestedURL); 594 // Do not append unless the user has already confirmed this filename in save(). 595 if (!actualURL) 596 return; 597 598 NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:actualURL.get() error:NULL]; 599 [handle seekToEndOfFile]; 600 [handle writeData:[content dataUsingEncoding:NSUTF8StringEncoding]]; 601 [handle closeFile]; 602 603 m_page->process()->send(Messages::WebInspector::DidAppend([actualURL absoluteString]), m_page->pageID()); 604} 605 606void WebInspectorProxy::windowFrameDidChange() 607{ 608 ASSERT(!m_isAttached); 609 ASSERT(m_isVisible); 610 ASSERT(m_inspectorWindow); 611 612 if (m_isAttached || !m_isVisible || !m_inspectorWindow) 613 return; 614 615 NSString *frameString = NSStringFromRect([m_inspectorWindow frame]); 616 page()->pageGroup()->preferences()->setInspectorWindowFrame(frameString); 617} 618 619void WebInspectorProxy::inspectedViewFrameDidChange(CGFloat currentDimension) 620{ 621 if (!m_isAttached || !m_isVisible) 622 return; 623 624 WKView *inspectedView = m_page->wkView(); 625 NSRect inspectedViewFrame = [inspectedView frame]; 626 NSRect inspectorFrame = NSZeroRect; 627 NSRect parentBounds = [[inspectedView superview] bounds]; 628 CGFloat inspectedViewTop = NSMaxY(inspectedViewFrame); 629 630 switch (m_attachmentSide) { 631 case AttachmentSideBottom: { 632 if (!currentDimension) 633 currentDimension = NSHeight([m_inspectorView.get() frame]); 634 635 CGFloat parentHeight = NSHeight(parentBounds); 636 CGFloat inspectorHeight = InspectorFrontendClientLocal::constrainedAttachedWindowHeight(currentDimension, parentHeight); 637 638 // Preserve the top position of the inspected view so banners in Safari still work. 639 inspectedViewFrame = NSMakeRect(0, inspectorHeight, NSWidth(parentBounds), inspectedViewTop - inspectorHeight); 640 inspectorFrame = NSMakeRect(0, 0, NSWidth(inspectedViewFrame), inspectorHeight); 641 break; 642 } 643 644 case AttachmentSideRight: { 645 if (!currentDimension) 646 currentDimension = NSWidth([m_inspectorView.get() frame]); 647 648 CGFloat parentWidth = NSWidth(parentBounds); 649 CGFloat inspectorWidth = InspectorFrontendClientLocal::constrainedAttachedWindowWidth(currentDimension, parentWidth); 650 651 // Preserve the top position of the inspected view so banners in Safari still work. But don't use that 652 // top position for the inspector view since the banners only stretch as wide as the the inspected view. 653 inspectedViewFrame = NSMakeRect(0, 0, parentWidth - inspectorWidth, inspectedViewTop); 654 inspectorFrame = NSMakeRect(parentWidth - inspectorWidth, 0, inspectorWidth, NSHeight(parentBounds)); 655 break; 656 } 657 } 658 659 // Disable screen updates to make sure the layers for both views resize in sync. 660 [[m_inspectorView window] disableScreenUpdatesUntilFlush]; 661 662 [m_inspectorView setFrame:inspectorFrame]; 663 [inspectedView setFrame:inspectedViewFrame]; 664} 665 666unsigned WebInspectorProxy::platformInspectedWindowHeight() 667{ 668 WKView *inspectedView = m_page->wkView(); 669 NSRect inspectedViewRect = [inspectedView frame]; 670 return static_cast<unsigned>(inspectedViewRect.size.height); 671} 672 673unsigned WebInspectorProxy::platformInspectedWindowWidth() 674{ 675 WKView *inspectedView = m_page->wkView(); 676 NSRect inspectedViewRect = [inspectedView frame]; 677 return static_cast<unsigned>(inspectedViewRect.size.width); 678} 679 680void WebInspectorProxy::platformAttach() 681{ 682 WKView *inspectedView = m_page->wkView(); 683 [[NSNotificationCenter defaultCenter] addObserver:m_inspectorProxyObjCAdapter.get() selector:@selector(inspectedViewFrameDidChange:) name:NSViewFrameDidChangeNotification object:inspectedView]; 684 685 if (m_inspectorWindow) { 686 [m_inspectorWindow.get() setDelegate:nil]; 687 [m_inspectorWindow.get() orderOut:nil]; 688 m_inspectorWindow = 0; 689 } 690 691 [m_inspectorView.get() removeFromSuperview]; 692 693 [m_inspectorView.get() setAutoresizingMask:NSViewWidthSizable | NSViewMaxYMargin]; 694 695 CGFloat currentDimension; 696 697 switch (m_attachmentSide) { 698 case AttachmentSideBottom: 699 currentDimension = inspectorPageGroup()->preferences()->inspectorAttachedHeight(); 700 break; 701 case AttachmentSideRight: 702 currentDimension = inspectorPageGroup()->preferences()->inspectorAttachedWidth(); 703 break; 704 } 705 706 inspectedViewFrameDidChange(currentDimension); 707 708 [[inspectedView superview] addSubview:m_inspectorView.get() positioned:NSWindowBelow relativeTo:inspectedView]; 709 710 [[inspectedView window] makeFirstResponder:m_inspectorView.get()]; 711} 712 713void WebInspectorProxy::platformDetach() 714{ 715 WKView *inspectedView = m_page->wkView(); 716 [[NSNotificationCenter defaultCenter] removeObserver:m_inspectorProxyObjCAdapter.get() name:NSViewFrameDidChangeNotification object:inspectedView]; 717 718 [m_inspectorView.get() removeFromSuperview]; 719 720 // Make sure that we size the inspected view's frame after detaching so that it takes up the space that the 721 // attached inspector used to. Preserve the top position of the inspected view so banners in Safari still work. 722 723 inspectedView.frame = NSMakeRect(0, 0, NSWidth(inspectedView.superview.bounds), NSMaxY(inspectedView.frame)); 724 725 // Return early if we are not visible. This means the inspector was closed while attached 726 // and we should not create and show the inspector window. 727 if (!m_isVisible) 728 return; 729 730 createInspectorWindow(); 731 732 platformBringToFront(); 733} 734 735void WebInspectorProxy::platformSetAttachedWindowHeight(unsigned height) 736{ 737 if (!m_isAttached) 738 return; 739 740 inspectedViewFrameDidChange(height); 741} 742 743void WebInspectorProxy::platformSetAttachedWindowWidth(unsigned width) 744{ 745 if (!m_isAttached) 746 return; 747 748 inspectedViewFrameDidChange(width); 749} 750 751void WebInspectorProxy::platformSetToolbarHeight(unsigned height) 752{ 753 [m_inspectorWindow setContentBorderThickness:height forEdge:NSMaxYEdge]; 754} 755 756String WebInspectorProxy::inspectorPageURL() const 757{ 758 NSString *path; 759 if (inspectorReallyUsesWebKitUserInterface(page()->pageGroup()->preferences())) 760 path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] pathForResource:@"inspector" ofType:@"html" inDirectory:@"inspector"]; 761 else 762 path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspectorUI"] pathForResource:@"Main" ofType:@"html"]; 763 764 ASSERT([path length]); 765 766 return [[NSURL fileURLWithPath:path] absoluteString]; 767} 768 769String WebInspectorProxy::inspectorBaseURL() const 770{ 771 NSString *path; 772 if (inspectorReallyUsesWebKitUserInterface(page()->pageGroup()->preferences())) 773 path = [[NSBundle bundleWithIdentifier:@"com.apple.WebCore"] resourcePath]; 774 else 775 path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspectorUI"] resourcePath]; 776 777 ASSERT([path length]); 778 779 return [[NSURL fileURLWithPath:path] absoluteString]; 780} 781 782} // namespace WebKit 783 784#endif // ENABLE(INSPECTOR) 785