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 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 "WebNodeHighlight.h" 30#import "WebNodeHighlightView.h" 31#import "WebNSViewExtras.h" 32 33#import <WebCore/InspectorController.h> 34#import <wtf/Assertions.h> 35 36using namespace WebCore; 37 38@interface WebNodeHighlight (FileInternal) 39- (NSRect)_computeHighlightWindowFrame; 40- (void)_repositionHighlightWindow; 41@end 42 43@implementation WebNodeHighlight 44 45- (id)initWithTargetView:(NSView *)targetView inspectorController:(InspectorController*)inspectorController 46{ 47 self = [super init]; 48 if (!self) 49 return nil; 50 51 _targetView = [targetView retain]; 52 _inspectorController = inspectorController; 53 54 int styleMask = NSBorderlessWindowMask; 55 NSRect contentRect = [NSWindow contentRectForFrameRect:[self _computeHighlightWindowFrame] styleMask:styleMask]; 56 _highlightWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; 57 [_highlightWindow setBackgroundColor:[NSColor clearColor]]; 58 [_highlightWindow setOpaque:NO]; 59 [_highlightWindow setIgnoresMouseEvents:YES]; 60 [_highlightWindow setReleasedWhenClosed:NO]; 61 62 _highlightView = [[WebNodeHighlightView alloc] initWithWebNodeHighlight:self]; 63 [_highlightWindow setContentView:_highlightView]; 64 [_highlightView release]; 65 66 return self; 67} 68 69- (void)dealloc 70{ 71 ASSERT(!_highlightWindow); 72 ASSERT(!_targetView); 73 ASSERT(!_highlightView); 74 75 [super dealloc]; 76} 77 78- (void)attach 79{ 80 ASSERT(_targetView); 81 ASSERT([_targetView window]); 82 ASSERT(_highlightWindow); 83 84 if (!_highlightWindow || !_targetView || ![_targetView window]) 85 return; 86 87 [[_targetView window] addChildWindow:_highlightWindow ordered:NSWindowAbove]; 88 89 // Observe both frame-changed and bounds-changed notifications because either one could leave 90 // the highlight incorrectly positioned with respect to the target view. We need to do this for 91 // the entire superview hierarchy to handle scrolling, bars coming and going, etc. 92 // (without making concrete assumptions about the view hierarchy). 93 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 94 for (NSView *v = _targetView; v; v = [v superview]) { 95 [notificationCenter addObserver:self selector:@selector(_repositionHighlightWindow) name:NSViewFrameDidChangeNotification object:v]; 96 [notificationCenter addObserver:self selector:@selector(_repositionHighlightWindow) name:NSViewBoundsDidChangeNotification object:v]; 97 } 98 99 if (_delegate && [_delegate respondsToSelector:@selector(didAttachWebNodeHighlight:)]) 100 [_delegate didAttachWebNodeHighlight:self]; 101} 102 103- (id)delegate 104{ 105 return _delegate; 106} 107 108- (void)detach 109{ 110 if (!_highlightWindow) { 111 ASSERT(!_targetView); 112 return; 113 } 114 115 if (_delegate && [_delegate respondsToSelector:@selector(willDetachWebNodeHighlight:)]) 116 [_delegate willDetachWebNodeHighlight:self]; 117 118 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 119 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:nil]; 120 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:nil]; 121 122 [[_highlightWindow parentWindow] removeChildWindow:_highlightWindow]; 123 124 [_highlightWindow release]; 125 _highlightWindow = nil; 126 127 [_targetView release]; 128 _targetView = nil; 129 130 // We didn't retain _highlightView, but we do need to tell it to forget about us, so it doesn't 131 // try to send our delegate messages after we've been dealloc'ed, e.g. 132 [_highlightView detachFromWebNodeHighlight]; 133 _highlightView = nil; 134} 135 136- (WebNodeHighlightView *)highlightView 137{ 138 return _highlightView; 139} 140 141- (void)setDelegate:(id)delegate 142{ 143 // The delegate is not retained, as usual in Cocoa. 144 _delegate = delegate; 145} 146 147- (void)setNeedsUpdateInTargetViewRect:(NSRect)rect 148{ 149 ASSERT(_targetView); 150 151 [[_targetView window] disableScreenUpdatesUntilFlush]; 152 153 // Mark the whole highlight view as needing display since we don't know what areas 154 // need updated, since the highlight can be larger than the element to show margins. 155 [_highlightView setNeedsDisplay:YES]; 156 157 // Redraw highlight view immediately so it updates in sync with the target view. 158 // This is especially visible when resizing the window, scrolling or with DHTML. 159 [_highlightView displayIfNeeded]; 160} 161 162- (NSView *)targetView 163{ 164 return _targetView; 165} 166 167- (InspectorController*)inspectorController 168{ 169 return _inspectorController; 170} 171 172@end 173 174@implementation WebNodeHighlight (FileInternal) 175 176- (NSRect)_computeHighlightWindowFrame 177{ 178 ASSERT(_targetView); 179 ASSERT([_targetView window]); 180 181 NSRect highlightWindowFrame = [_targetView convertRect:[_targetView visibleRect] toView:nil]; 182 highlightWindowFrame.origin = [[_targetView window] convertBaseToScreen:highlightWindowFrame.origin]; 183 184 return highlightWindowFrame; 185} 186 187- (void)_repositionHighlightWindow 188{ 189 // This assertion fires in cases where a new tab is created while the highlight 190 // is showing (<http://bugs.webkit.org/show_bug.cgi?id=14254>) 191 ASSERT([_targetView window]); 192 193 // Until that bug is fixed, bail out to avoid worse problems where the highlight 194 // moves to a nonsense location. 195 if (![_targetView window]) 196 return; 197 198 // Disable screen updates so the highlight moves in sync with the view. 199 [[_targetView window] disableScreenUpdatesUntilFlush]; 200 201 [_highlightWindow setFrame:[self _computeHighlightWindowFrame] display:YES]; 202} 203 204@end 205