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 36 using 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