1/* 2 * Copyright (C) 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 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 "WebNodeHighlight.h" 30#import "WebNodeHighlightView.h" 31#import "WebNSViewExtras.h" 32 33#import <WebCore/InspectorController.h> 34#import <wtf/Assertions.h> 35 36#if PLATFORM(IOS) 37#import "WebFramePrivate.h" 38#import "WebHTMLView.h" 39#import "WebView.h" 40#import <QuartzCore/CALayerPrivate.h> 41#import <WebCore/WAKWindow.h> 42#endif 43 44using namespace WebCore; 45 46#if !PLATFORM(IOS) 47@interface WebNodeHighlight (FileInternal) 48- (NSRect)_computeHighlightWindowFrame; 49- (void)_repositionHighlightWindow; 50@end 51#endif 52 53#if PLATFORM(IOS) 54@implementation WebHighlightLayer 55 56- (id)initWithHighlightView:(WebNodeHighlightView *)view webView:(WebView *)webView 57{ 58 self = [super init]; 59 if (!self) 60 return nil; 61 _view = view; 62 _webView = webView; 63 return self; 64} 65 66- (void)layoutSublayers 67{ 68 CGFloat documentScale = [[[_webView mainFrame] documentView] scale]; 69 [self setTransform:CATransform3DMakeScale(documentScale, documentScale, 1.0)]; 70 71 [_view layoutSublayers:self]; 72} 73 74- (id<CAAction>)actionForKey:(NSString *)key 75{ 76 return nil; // Disable all default actions. 77} 78 79@end 80#endif 81 82@implementation WebNodeHighlight 83 84- (id)initWithTargetView:(NSView *)targetView inspectorController:(InspectorController*)inspectorController 85{ 86 self = [super init]; 87 if (!self) 88 return nil; 89 90 _targetView = [targetView retain]; 91 _inspectorController = inspectorController; 92 93#if !PLATFORM(IOS) 94 int styleMask = NSBorderlessWindowMask; 95 NSRect contentRect = [NSWindow contentRectForFrameRect:[self _computeHighlightWindowFrame] styleMask:styleMask]; 96 _highlightWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; 97 [_highlightWindow setBackgroundColor:[NSColor clearColor]]; 98 [_highlightWindow setOpaque:NO]; 99 [_highlightWindow setIgnoresMouseEvents:YES]; 100 [_highlightWindow setReleasedWhenClosed:NO]; 101 102 _highlightView = [[WebNodeHighlightView alloc] initWithWebNodeHighlight:self]; 103 [_highlightWindow setContentView:_highlightView]; 104 [_highlightView release]; 105#else 106 ASSERT([_targetView isKindOfClass:[WebView class]]); 107 WebView *webView = (WebView *)targetView; 108 109 _highlightView = [[WebNodeHighlightView alloc] initWithWebNodeHighlight:self]; 110 _highlightLayer = [[WebHighlightLayer alloc] initWithHighlightView:_highlightView webView:webView]; 111 [_highlightLayer setContentsScale:[[_targetView window] screenScale]]; // HiDPI. 112 [_highlightLayer setCanDrawConcurrently:NO]; 113#endif 114 115 return self; 116} 117 118- (void)dealloc 119{ 120#if !PLATFORM(IOS) 121 ASSERT(!_highlightWindow); 122#else 123 ASSERT(!_highlightLayer); 124#endif 125 ASSERT(!_targetView); 126 ASSERT(!_highlightView); 127 128 [super dealloc]; 129} 130 131- (void)attach 132{ 133 ASSERT(_targetView); 134 ASSERT([_targetView window]); 135 136#if !PLATFORM(IOS) 137 ASSERT(_highlightWindow); 138 139 if (!_highlightWindow || !_targetView || ![_targetView window]) 140 return; 141 142 [[_targetView window] addChildWindow:_highlightWindow ordered:NSWindowAbove]; 143 144 // Observe both frame-changed and bounds-changed notifications because either one could leave 145 // the highlight incorrectly positioned with respect to the target view. We need to do this for 146 // the entire superview hierarchy to handle scrolling, bars coming and going, etc. 147 // (without making concrete assumptions about the view hierarchy). 148 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 149 for (NSView *v = _targetView; v; v = [v superview]) { 150 [notificationCenter addObserver:self selector:@selector(_repositionHighlightWindow) name:NSViewFrameDidChangeNotification object:v]; 151 [notificationCenter addObserver:self selector:@selector(_repositionHighlightWindow) name:NSViewBoundsDidChangeNotification object:v]; 152 } 153#else 154 ASSERT(_highlightLayer); 155 156 WAKWindow *window = [_targetView window]; 157 [[window hostLayer] addSublayer:_highlightLayer]; 158 [self setNeedsDisplay]; 159#endif 160 161 if (_delegate && [_delegate respondsToSelector:@selector(didAttachWebNodeHighlight:)]) 162 [_delegate didAttachWebNodeHighlight:self]; 163} 164 165- (id)delegate 166{ 167 return _delegate; 168} 169 170- (void)detach 171{ 172#if !PLATFORM(IOS) 173 if (!_highlightWindow) { 174#else 175 if (!_highlightLayer) { 176#endif 177 ASSERT(!_targetView); 178 return; 179 } 180 181 if (_delegate && [_delegate respondsToSelector:@selector(willDetachWebNodeHighlight:)]) 182 [_delegate willDetachWebNodeHighlight:self]; 183 184#if !PLATFORM(IOS) 185 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 186 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:nil]; 187 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:nil]; 188 189 [[_highlightWindow parentWindow] removeChildWindow:_highlightWindow]; 190 191 [_highlightWindow release]; 192 _highlightWindow = nil; 193#else 194 [_highlightLayer removeFromSuperlayer]; 195 [_highlightLayer release]; 196 _highlightLayer = nil; 197#endif 198 199 [_targetView release]; 200 _targetView = nil; 201 202 // We didn't retain _highlightView, but we do need to tell it to forget about us, so it doesn't 203 // try to send our delegate messages after we've been dealloc'ed, e.g. 204 [_highlightView detachFromWebNodeHighlight]; 205#if PLATFORM(IOS) 206 // iOS did retain the highlightView, and we should release it here. 207 [_highlightView release]; 208#endif 209 _highlightView = nil; 210} 211 212- (WebNodeHighlightView *)highlightView 213{ 214 return _highlightView; 215} 216 217- (void)setDelegate:(id)delegate 218{ 219 // The delegate is not retained, as usual in Cocoa. 220 _delegate = delegate; 221} 222 223#if !PLATFORM(IOS) 224- (void)setNeedsUpdateInTargetViewRect:(NSRect)rect 225{ 226 ASSERT(_targetView); 227 228 [[_targetView window] disableScreenUpdatesUntilFlush]; 229 230 // Mark the whole highlight view as needing display since we don't know what areas 231 // need updated, since the highlight can be larger than the element to show margins. 232 [_highlightView setNeedsDisplay:YES]; 233 234 // Redraw highlight view immediately so it updates in sync with the target view. 235 // This is especially visible when resizing the window, scrolling or with DHTML. 236 [_highlightView displayIfNeeded]; 237} 238#else 239- (void)setNeedsDisplay 240{ 241 [_highlightLayer setNeedsLayout]; 242 [_highlightLayer setNeedsDisplay]; 243 [_highlightLayer displayIfNeeded]; 244} 245#endif 246 247- (NSView *)targetView 248{ 249 return _targetView; 250} 251 252- (InspectorController*)inspectorController 253{ 254 return _inspectorController; 255} 256 257@end 258 259#if !PLATFORM(IOS) 260@implementation WebNodeHighlight (FileInternal) 261 262- (NSRect)_computeHighlightWindowFrame 263{ 264 ASSERT(_targetView); 265 ASSERT([_targetView window]); 266 267 NSRect highlightWindowFrame = [_targetView convertRect:[_targetView visibleRect] toView:nil]; 268#pragma clang diagnostic push 269#pragma clang diagnostic ignored "-Wdeprecated-declarations" 270 highlightWindowFrame.origin = [[_targetView window] convertBaseToScreen:highlightWindowFrame.origin]; 271#pragma clang diagnostic pop 272 273 return highlightWindowFrame; 274} 275 276- (void)_repositionHighlightWindow 277{ 278 // This assertion fires in cases where a new tab is created while the highlight 279 // is showing (<http://bugs.webkit.org/show_bug.cgi?id=14254>) 280 ASSERT([_targetView window]); 281 282 // Until that bug is fixed, bail out to avoid worse problems where the highlight 283 // moves to a nonsense location. 284 if (![_targetView window]) 285 return; 286 287 // Disable screen updates so the highlight moves in sync with the view. 288 [[_targetView window] disableScreenUpdatesUntilFlush]; 289 290 [_highlightWindow setFrame:[self _computeHighlightWindowFrame] display:YES]; 291} 292 293@end 294#endif 295