+2007-08-17 Timothy Hatcher <timothy@apple.com>
+
+ Reviewed by Darin.
+
+ <rdar://problem/5398301> Xcode threw mutation exception while enumerating subviews (GC only)
+
+ I was never able to reproduce this exception. But there can be cases where layout will
+ trigger JavaScript or plugin code that can modify the WebView view hierarchy during a
+ recursive enumeration of all the subviews.
+
+ This patch does two things:
+ 1) Adds a check in debug builds that will LOG when any view is added or removed during layout.
+ Noting that added views will not recieve layout this round and might paint without first recieving layout.
+
+ 2) Recursivly builds up an array of descendant WebHTMLViews before calling layout on them.
+ This matches the behavior of makeObjectsPerformSelector: in the non-GC case (making a copy
+ before enumerating.)
+
+ * WebView/WebHTMLView.mm:
+ (-[WebHTMLView _web_setPrintingModeRecursive]): Use _web_addDescendantWebHTMLViewsToArray to build up an array
+ of WebHTMLViews to enumerate.
+ (-[WebHTMLView _web_clearPrintingModeRecursive]): Ditto.
+ (-[WebHTMLView _web_setPrintingModeRecursiveAndAdjustViewSize]): Ditto.
+ (-[WebHTMLView _web_layoutIfNeededRecursive]): Ditto.
+ (-[WebHTMLView _layoutIfNeeded]): Moved to WebHTMLViewFileInternal category.
+ (-[WebHTMLView didAddSubview:]): LOG in debug builds.
+ (-[WebHTMLView willRemoveSubview:]): Ditto.
+ (-[NSView _web_addDescendantWebHTMLViewsToArray:]): Recursivly build an array of descendant WebHTMLViews.
+ * WebView/WebHTMLViewInternal.h: Added a BOOL in WebHTMLViewPrivate to track subview changes (debug only.)
+
2007-08-17 Anders Carlsson <andersca@apple.com>
Reviewed by Dave Hyatt.
- (void)_setMouseDownEvent:(NSEvent *)event;
- (WebHTMLView *)_topHTMLView;
- (BOOL)_isTopHTMLView;
+- (void)_web_setPrintingModeRecursive;
+- (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
+- (void)_web_clearPrintingModeRecursive;
+- (void)_web_layoutIfNeededRecursive;
@end
@interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick.
@end
@interface NSView (WebHTMLViewFileInternal)
-- (void)_web_setPrintingModeRecursive;
-- (void)_web_setPrintingModeRecursiveAndAdjustViewSize;
-- (void)_web_clearPrintingModeRecursive;
-- (void)_web_layoutIfNeededRecursive;
+- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array;
@end
@interface NSMutableDictionary (WebHTMLViewFileInternal)
return self == [self _topHTMLView];
}
+- (void)_web_setPrintingModeRecursive
+{
+ [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = YES;
+#endif
+
+ NSMutableArray *decendantWebHTMLViews = [[NSMutableArray alloc] init];
+
+ [self _web_addDescendantWebHTMLViewsToArray:decendantWebHTMLViews];
+
+ unsigned count = [decendantWebHTMLViews count];
+ for (unsigned i = 0; i < count; ++i)
+ [[decendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
+
+ [decendantWebHTMLViews release];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = NO;
+#endif
+}
+
+- (void)_web_clearPrintingModeRecursive
+{
+ [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = YES;
+#endif
+
+ NSMutableArray *decendantWebHTMLViews = [[NSMutableArray alloc] init];
+
+ [self _web_addDescendantWebHTMLViewsToArray:decendantWebHTMLViews];
+
+ unsigned count = [decendantWebHTMLViews count];
+ for (unsigned i = 0; i < count; ++i)
+ [[decendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
+
+ [decendantWebHTMLViews release];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = NO;
+#endif
+}
+
+- (void)_web_setPrintingModeRecursiveAndAdjustViewSize
+{
+ [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = YES;
+#endif
+
+ NSMutableArray *decendantWebHTMLViews = [[NSMutableArray alloc] init];
+
+ [self _web_addDescendantWebHTMLViewsToArray:decendantWebHTMLViews];
+
+ unsigned count = [decendantWebHTMLViews count];
+ for (unsigned i = 0; i < count; ++i)
+ [[decendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
+
+ [decendantWebHTMLViews release];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = NO;
+#endif
+}
+
+- (void)_layoutIfNeeded
+{
+ ASSERT(!_private->subviewsSetAside);
+
+ if ([[self _bridge] needsLayout])
+ _private->needsLayout = YES;
+ if (_private->needsToApplyStyles || _private->needsLayout)
+ [self layout];
+}
+
+- (void)_web_layoutIfNeededRecursive
+{
+ [self _layoutIfNeeded];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = YES;
+#endif
+
+ NSMutableArray *decendantWebHTMLViews = [[NSMutableArray alloc] init];
+
+ [self _web_addDescendantWebHTMLViewsToArray:decendantWebHTMLViews];
+
+ unsigned count = [decendantWebHTMLViews count];
+ for (unsigned i = 0; i < count; ++i)
+ [[decendantWebHTMLViews objectAtIndex:i] _layoutIfNeeded];
+
+ [decendantWebHTMLViews release];
+
+#ifndef NDEBUG
+ _private->enumeratingSubviews = NO;
+#endif
+}
+
@end
@implementation WebHTMLView (WebPrivate)
_private->subviewsSetAside = NO;
}
+#ifndef NDEBUG
+
+- (void)didAddSubview:(NSView *)subview
+{
+ if (_private->enumeratingSubviews)
+ LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class]));
+}
+
+- (void)willRemoveSubview:(NSView *)subview
+{
+ if (_private->enumeratingSubviews)
+ LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class]));
+}
+
+#endif
+
#ifdef BUILDING_ON_TIGER
// This is called when we are about to draw, but before our dirty rect is propagated to our ancestors.
return _private->pluginController;
}
-- (void)_web_setPrintingModeRecursive
-{
- [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
- [super _web_setPrintingModeRecursive];
-}
-
-- (void)_web_clearPrintingModeRecursive
-{
- [self _setPrinting:NO minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:NO];
- [super _web_clearPrintingModeRecursive];
-}
-
-- (void)_web_setPrintingModeRecursiveAndAdjustViewSize
-{
- [self _setPrinting:YES minimumPageWidth:0.0f maximumPageWidth:0.0f adjustViewSize:YES];
- [super _web_setPrintingModeRecursiveAndAdjustViewSize];
-}
-
- (void)_layoutForPrinting
{
// Set printing mode temporarily so we can adjust the size of the view. This will allow
[self _web_clearPrintingModeRecursive];
}
-- (void)_layoutIfNeeded
-{
- ASSERT(!_private->subviewsSetAside);
-
- if ([[self _bridge] needsLayout])
- _private->needsLayout = YES;
- if (_private->needsToApplyStyles || _private->needsLayout)
- [self layout];
-}
-
-- (void)_web_layoutIfNeededRecursive
-{
- [self _layoutIfNeeded];
- [super _web_layoutIfNeededRecursive];
-}
-
- (void)_startAutoscrollTimer: (NSEvent *)triggerEvent
{
if (_private->autoscrollTimer == nil) {
@implementation NSView (WebHTMLViewFileInternal)
-- (void)_web_setPrintingModeRecursive
-{
- [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursive)];
-}
-
-- (void)_web_setPrintingModeRecursiveAndAdjustViewSize
+- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array
{
- [_subviews makeObjectsPerformSelector:@selector(_web_setPrintingModeRecursiveAndAdjustViewSize)];
-}
-
-- (void)_web_clearPrintingModeRecursive
-{
- [_subviews makeObjectsPerformSelector:@selector(_web_clearPrintingModeRecursive)];
-}
-
-- (void)_web_layoutIfNeededRecursive
-{
- [_subviews makeObjectsPerformSelector:@selector(_web_layoutIfNeededRecursive)];
+ unsigned count = [_subviews count];
+ for (unsigned i = 0; i < count; ++i) {
+ NSView *child = [_subviews objectAtIndex:i];
+ if ([child isKindOfClass:[WebHTMLView class]])
+ [array addObject:child];
+ [child _web_addDescendantWebHTMLViewsToArray:array];
+ }
}
@end