2011-04-08 Maciej Stachowiak <mjs@apple.com>
authormjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Apr 2011 19:33:04 +0000 (19:33 +0000)
committermjs@apple.com <mjs@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 8 Apr 2011 19:33:04 +0000 (19:33 +0000)
        Reviewed by Darin Adler.

        -[WebView setPreferences:] can take a lot of time if loading lots of webviews at once
        https://bugs.webkit.org/show_bug.cgi?id=58128
        <rdar://problem/9244553>

        The root of the problem here was an O(N^2) issue - each WebView,
        upon having its preferences initialized, would broadcast a
        notification that was listened to by all other WebViews sharing
        its preferences.

        To maintain the API contract, I split the notification into two,
        one that is for public API consumption, and the other which is for
        internal use only. Changes that don't need to be picked up by
        other WebViews broadcast the public notification only. And we
        avoid WebView broadcasting a notification just to get itself to
        update.

        * Misc/WebIconDatabase.mm:
        (-[WebIconDatabase _startUpIconDatabase]):
        (-[WebIconDatabase _shutDownIconDatabase]):
        * Plugins/WebBaseNetscapePluginView.mm:
        (-[WebBaseNetscapePluginView viewWillMoveToWindow:]):
        (-[WebBaseNetscapePluginView viewWillMoveToSuperview:]):
        (-[WebBaseNetscapePluginView viewDidMoveToWindow]):
        (-[WebBaseNetscapePluginView viewWillMoveToHostWindow:]):
        * WebView/WebPreferences.mm:
        (-[WebPreferences initWithIdentifier:]):
        (-[WebPreferences _setStringValue:forKey:]):
        (-[WebPreferences _setIntegerValue:forKey:]):
        (-[WebPreferences _setFloatValue:forKey:]):
        (-[WebPreferences _setBoolValue:forKey:]):
        (-[WebPreferences _setLongLongValue:forKey:]):
        (-[WebPreferences _setUnsignedLongLongValue:forKey:]):
        (-[WebPreferences _postPreferencesChangedNotification]):
        (-[WebPreferences _postPreferencesChangedAPINotification]):
        * WebView/WebPreferencesPrivate.h:
        * WebView/WebView.mm:
        (-[WebView _commonInitializationWithFrameName:groupName:usesDocumentViews:]):
        (-[WebView _preferencesChangedNotification:]):
        (-[WebView _preferencesChanged:]):
        (-[WebView setUsesPageCache:]):
        (+[WebView initialize]):
        (-[WebView setPreferences:]):
        (-[WebView _keyboardUIMode]):
        * WebView/WebViewInternal.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@83324 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit/mac/ChangeLog
Source/WebKit/mac/Misc/WebIconDatabase.mm
Source/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm
Source/WebKit/mac/WebView/WebPreferences.mm
Source/WebKit/mac/WebView/WebPreferencesPrivate.h
Source/WebKit/mac/WebView/WebView.mm
Source/WebKit/mac/WebView/WebViewInternal.h

index 31d3471..f784c0a 100644 (file)
@@ -1,3 +1,52 @@
+2011-04-08  Maciej Stachowiak  <mjs@apple.com>
+
+        Reviewed by Darin Adler.
+
+        -[WebView setPreferences:] can take a lot of time if loading lots of webviews at once
+        https://bugs.webkit.org/show_bug.cgi?id=58128
+        <rdar://problem/9244553>
+        
+        The root of the problem here was an O(N^2) issue - each WebView,
+        upon having its preferences initialized, would broadcast a
+        notification that was listened to by all other WebViews sharing
+        its preferences.
+        
+        To maintain the API contract, I split the notification into two,
+        one that is for public API consumption, and the other which is for
+        internal use only. Changes that don't need to be picked up by
+        other WebViews broadcast the public notification only. And we
+        avoid WebView broadcasting a notification just to get itself to
+        update.
+
+        * Misc/WebIconDatabase.mm:
+        (-[WebIconDatabase _startUpIconDatabase]):
+        (-[WebIconDatabase _shutDownIconDatabase]):
+        * Plugins/WebBaseNetscapePluginView.mm:
+        (-[WebBaseNetscapePluginView viewWillMoveToWindow:]):
+        (-[WebBaseNetscapePluginView viewWillMoveToSuperview:]):
+        (-[WebBaseNetscapePluginView viewDidMoveToWindow]):
+        (-[WebBaseNetscapePluginView viewWillMoveToHostWindow:]):
+        * WebView/WebPreferences.mm:
+        (-[WebPreferences initWithIdentifier:]):
+        (-[WebPreferences _setStringValue:forKey:]):
+        (-[WebPreferences _setIntegerValue:forKey:]):
+        (-[WebPreferences _setFloatValue:forKey:]):
+        (-[WebPreferences _setBoolValue:forKey:]):
+        (-[WebPreferences _setLongLongValue:forKey:]):
+        (-[WebPreferences _setUnsignedLongLongValue:forKey:]):
+        (-[WebPreferences _postPreferencesChangedNotification]):
+        (-[WebPreferences _postPreferencesChangedAPINotification]):
+        * WebView/WebPreferencesPrivate.h:
+        * WebView/WebView.mm:
+        (-[WebView _commonInitializationWithFrameName:groupName:usesDocumentViews:]):
+        (-[WebView _preferencesChangedNotification:]):
+        (-[WebView _preferencesChanged:]):
+        (-[WebView setUsesPageCache:]):
+        (+[WebView initialize]):
+        (-[WebView setPreferences:]):
+        (-[WebView _keyboardUIMode]):
+        * WebView/WebViewInternal.h:
+
 2011-04-07  Andrew Scherkus  <scherkus@chromium.org>
 
         Revert ENABLE_TRACK patch due to compile failures.
index f223f09..e9aa8ec 100644 (file)
@@ -35,7 +35,7 @@
 #import "WebNSFileManagerExtras.h"
 #import "WebNSNotificationCenterExtras.h"
 #import "WebNSURLExtras.h"
-#import "WebPreferences.h"
+#import "WebPreferencesPrivate.h"
 #import "WebTypesInternal.h"
 #import <WebCore/IconDatabase.h>
 #import <WebCore/Image.h>
@@ -313,7 +313,7 @@ static WebIconDatabaseClient* defaultClient()
                                                object:NSApp];
     [[NSNotificationCenter defaultCenter] addObserver:self
                                              selector:@selector(_resetCachedWebPreferences:)
-                                                 name:WebPreferencesChangedNotification
+                                                 name:WebPreferencesChangedInternalNotification
                                                object:nil];
 }
 
@@ -324,7 +324,7 @@ static WebIconDatabaseClient* defaultClient()
                                                     name:NSApplicationWillTerminateNotification
                                                   object:NSApp];
     [[NSNotificationCenter defaultCenter] removeObserver:self
-                                                    name:WebPreferencesChangedNotification
+                                                    name:WebPreferencesChangedInternalNotification
                                                   object:nil];
 }
 
index 3ccbb9f..f1eecde 100644 (file)
@@ -619,9 +619,9 @@ String WebHaltablePlugin::pluginName() const
             // View will have no associated windows.
             [self stop];
             
-            // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
+            // Stop observing WebPreferencesChangedInternalNotification -- we only need to observe this when installed in the view hierarchy.
             // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
-            [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
+            [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedInternalNotification object:nil];
         }
     }
 }
@@ -634,9 +634,9 @@ String WebHaltablePlugin::pluginName() const
         // There is no need to start the plug-in when moving into a superview.  -viewDidMoveToWindow takes care of that.
         [self stop];
         
-        // Stop observing WebPreferencesChangedNotification -- we only need to observe this when installed in the view hierarchy.
+        // Stop observing WebPreferencesChangedInternalNotification -- we only need to observe this when installed in the view hierarchy.
         // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed.
-        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
+        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedInternalNotification object:nil];
     }
 }
 
@@ -645,11 +645,11 @@ String WebHaltablePlugin::pluginName() const
     [self resetTrackingRect];
     
     if ([self window]) {
-        // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending
+        // While in the view hierarchy, observe WebPreferencesChangedInternalNotification so that we can start/stop depending
         // on whether plugins are enabled.
         [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(preferencesHaveChanged:)
-                                                     name:WebPreferencesChangedNotification
+                                                     name:WebPreferencesChangedInternalNotification
                                                    object:nil];
 
         _isPrivateBrowsingEnabled = [[[self webView] preferences] privateBrowsingEnabled];
@@ -677,8 +677,8 @@ String WebHaltablePlugin::pluginName() const
         // View will have no associated windows.
         [self stop];
         
-        // Remove WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window
-        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil];
+        // Remove WebPreferencesChangedInternalNotification observer -- we will observe once again when we move back into the window
+        [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedInternalNotification object:nil];
     }
 }
 
index a5f24e0..0160aaf 100644 (file)
@@ -42,6 +42,7 @@
 
 NSString *WebPreferencesChangedNotification = @"WebPreferencesChangedNotification";
 NSString *WebPreferencesRemovedNotification = @"WebPreferencesRemovedNotification";
+NSString *WebPreferencesChangedInternalNotification = @"WebPreferencesChangedInternalNotification";
 
 #define KEY(x) (_private->identifier ? [_private->identifier stringByAppendingString:(x)] : (x))
 
@@ -232,7 +233,7 @@ static bool useQuickLookQuirks(void)
 
     [[self class] _setInstance:self forIdentifier:_private->identifier];
 
-    [self _postPreferencesChangesNotification];
+    [self _postPreferencesChangedNotification];
 
     return self;
 }
@@ -436,7 +437,7 @@ static bool useQuickLookQuirks(void)
     [_private->values setObject:value forKey:_key];
     if (_private->autosaves)
         [[NSUserDefaults standardUserDefaults] setObject:value forKey:_key];
-    [self _postPreferencesChangesNotification];
+    [self _postPreferencesChangedNotification];
 }
 
 - (int)_integerValueForKey:(NSString *)key
@@ -453,7 +454,7 @@ static bool useQuickLookQuirks(void)
     [_private->values _webkit_setInt:value forKey:_key];
     if (_private->autosaves)
         [[NSUserDefaults standardUserDefaults] setInteger:value forKey:_key];
-    [self _postPreferencesChangesNotification];
+    [self _postPreferencesChangedNotification];
 }
 
 - (float)_floatValueForKey:(NSString *)key
@@ -470,7 +471,7 @@ static bool useQuickLookQuirks(void)
     [_private->values _webkit_setFloat:value forKey:_key];
     if (_private->autosaves)
         [[NSUserDefaults standardUserDefaults] setFloat:value forKey:_key];
-    [self _postPreferencesChangesNotification];
+    [self _postPreferencesChangedNotification];
 }
 
 - (BOOL)_boolValueForKey:(NSString *)key
@@ -486,7 +487,7 @@ static bool useQuickLookQuirks(void)
     [_private->values _webkit_setBool:value forKey:_key];
     if (_private->autosaves)
         [[NSUserDefaults standardUserDefaults] setBool:value forKey:_key];
-    [self _postPreferencesChangesNotification];
+    [self _postPreferencesChangedNotification];
 }
 
 - (long long)_longLongValueForKey:(NSString *)key
@@ -503,7 +504,7 @@ static bool useQuickLookQuirks(void)
     [_private->values _webkit_setLongLong:value forKey:_key];
     if (_private->autosaves)
         [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithLongLong:value] forKey:_key];
-    [self _postPreferencesChangesNotification];
+    [self _postPreferencesChangedNotification];
 }
 
 - (unsigned long long)_unsignedLongLongValueForKey:(NSString *)key
@@ -520,7 +521,7 @@ static bool useQuickLookQuirks(void)
     [_private->values _webkit_setUnsignedLongLong:value forKey:_key];
     if (_private->autosaves)
         [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithUnsignedLongLong:value] forKey:_key];
-    [self _postPreferencesChangesNotification];
+    [self _postPreferencesChangedNotification];
 }
 
 - (NSString *)standardFontFamily
@@ -1173,16 +1174,25 @@ static bool useQuickLookQuirks(void)
         [self performSelector:@selector(_checkLastReferenceForIdentifier:) withObject:[self _concatenateKeyWithIBCreatorID:ident] afterDelay:0.1];
 }
 
-- (void)_postPreferencesChangesNotification
+- (void)_postPreferencesChangedNotification
 {
     if (!pthread_main_np()) {
         [self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
         return;
     }
 
-    [[NSNotificationCenter defaultCenter]
-        postNotificationName:WebPreferencesChangedNotification object:self
-                    userInfo:nil];
+    [[NSNotificationCenter defaultCenter] postNotificationName:WebPreferencesChangedInternalNotification object:self userInfo:nil];
+    [[NSNotificationCenter defaultCenter] postNotificationName:WebPreferencesChangedNotification object:self userInfo:nil];
+}
+
+- (void)_postPreferencesChangedAPINotification
+{
+    if (!pthread_main_np()) {
+        [self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
+        return;
+    }
+
+    [[NSNotificationCenter defaultCenter] postNotificationName:WebPreferencesChangedNotification object:self userInfo:nil];
 }
 
 + (CFStringEncoding)_systemCFStringEncoding
index 7e1f6a2..df2b52a 100644 (file)
@@ -51,6 +51,7 @@ typedef enum {
 
 extern NSString *WebPreferencesChangedNotification;
 extern NSString *WebPreferencesRemovedNotification;
+extern NSString *WebPreferencesChangedInternalNotification;
 
 @interface WebPreferences (WebPrivate)
 
@@ -210,7 +211,8 @@ extern NSString *WebPreferencesRemovedNotification;
 - (void)setHyperlinkAuditingEnabled:(BOOL)enabled;
 
 // Other private methods
-- (void)_postPreferencesChangesNotification;
+- (void)_postPreferencesChangedNotification;
+- (void)_postPreferencesChangedAPINotification;
 + (WebPreferences *)_getInstanceForIdentifier:(NSString *)identifier;
 + (void)_setInstance:(WebPreferences *)instance forIdentifier:(NSString *)identifier;
 + (void)_removeReferenceForIdentifier:(NSString *)identifier;
index 94688ba..35da9d6 100644 (file)
@@ -777,10 +777,10 @@ static NSString *leakOutlookQuirksUserScriptContents()
 
     WebPreferences *prefs = [self preferences];
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
-                                                 name:WebPreferencesChangedNotification object:prefs];
+                                                 name:WebPreferencesChangedInternalNotification object:prefs];
 
-    // Post a notification so the WebCore settings update.
-    [[self preferences] _postPreferencesChangesNotification];
+    [self _preferencesChanged:[self preferences]];
+    [[self preferences] _postPreferencesChangedAPINotification];
 
     if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_LOCAL_RESOURCE_SECURITY_RESTRICTION)) {
         // Originally, we allowed all local loads.
@@ -1441,8 +1441,12 @@ static bool fastDocumentTeardownEnabled()
 - (void)_preferencesChangedNotification:(NSNotification *)notification
 {
     WebPreferences *preferences = (WebPreferences *)[notification object];
+    [self _preferencesChanged:preferences];
+}
+
+- (void)_preferencesChanged:(WebPreferences *)preferences
+{    
     ASSERT(preferences == [self preferences]);
-    
     if (!_private->userAgentOverridden)
         _private->userAgent = String();
 
@@ -2287,8 +2291,9 @@ static inline IMP getMethod(id o, SEL s)
 {
     _private->usesPageCache = usesPageCache;
 
-    // Post a notification so the WebCore settings update.
-    [[self preferences] _postPreferencesChangesNotification];
+    // Update our own settings and post the public notification only
+    [self _preferencesChanged:[self preferences]];
+    [[self preferences] _postPreferencesChangedAPINotification];
 }
 
 - (WebHistoryItem *)_globalHistoryItem
@@ -2873,7 +2878,7 @@ static PassOwnPtr<Vector<String> > toStringVector(NSArray* patterns)
     WTF::initializeMainThreadToProcessMainThread();
 
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillTerminate) name:NSApplicationWillTerminateNotification object:NSApp];
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:) name:WebPreferencesChangedNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:) name:WebPreferencesChangedInternalNotification object:nil];
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesRemovedNotification:) name:WebPreferencesRemovedNotification object:nil];    
 
     continuousSpellCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebContinuousSpellCheckingEnabled];
@@ -3378,15 +3383,16 @@ static bool needsWebViewInitThreadWorkaround()
 
     WebPreferences *oldPrefs = _private->preferences;
 
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:[self preferences]];
+    [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedInternalNotification object:[self preferences]];
     [WebPreferences _removeReferenceForIdentifier:[oldPrefs identifier]];
 
     _private->preferences = [prefs retain];
 
     // After registering for the notification, post it so the WebCore settings update.
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
-        name:WebPreferencesChangedNotification object:[self preferences]];
-    [[self preferences] _postPreferencesChangesNotification];
+        name:WebPreferencesChangedInternalNotification object:[self preferences]];
+    [self _preferencesChanged:[self preferences]];
+    [[self preferences] _postPreferencesChangedAPINotification];
 
     [oldPrefs didRemoveFromWebView];
     [oldPrefs release];
@@ -5989,7 +5995,7 @@ static inline uint64_t roundUpToPowerOf2(uint64_t num)
 
         [[NSNotificationCenter defaultCenter] 
             addObserver:self selector:@selector(_retrieveKeyboardUIModeFromPreferences:) 
-            name:WebPreferencesChangedNotification object:nil];
+            name:WebPreferencesChangedInternalNotification object:nil];
     }
     return _private->_keyboardUIMode;
 }
index 65cb4d8..aeef7eb 100644 (file)
@@ -183,6 +183,8 @@ namespace WebCore {
 
 - (void)_setInsertionPasteboard:(NSPasteboard *)pasteboard;
 
+- (void)_preferencesChanged:(WebPreferences *)preferences;
+
 #if ENABLE(VIDEO) && defined(__cplusplus)
 - (void)_enterFullscreenForNode:(WebCore::Node*)node;
 - (void)_exitFullscreen;