Title: [290295] trunk/Source
Revision
290295
Author
[email protected]
Date
2022-02-22 01:47:45 -0800 (Tue, 22 Feb 2022)

Log Message

Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events
https://bugs.webkit.org/show_bug.cgi?id=236985

Reviewed by Darin Adler.

Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events,
by keeping track of window objects interested in storage events. A Window object is interested
in storage events if both of the following conditions is true:
1. It has a Storage object (either localStorage or sessionStorage)
2. It has a storage event listener registered.

This patch also refactors the code so that we share more logic between WebKit1 and WebKit2.

Source/WebCore:

* inspector/agents/InspectorDOMStorageAgent.cpp:
(WebCore::InspectorDOMStorageAgent::setDOMStorageItem):
(WebCore::InspectorDOMStorageAgent::removeDOMStorageItem):
(WebCore::InspectorDOMStorageAgent::clearDOMStorageItems):
* loader/EmptyClients.cpp:
* page/DOMWindow.cpp:
(WebCore::windowsInterestedInStorageEvents):
(WebCore::DOMWindow::forEachWindowInterestedInStorageEvents):
(WebCore::DOMWindow::~DOMWindow):
(WebCore::DOMWindow::willDetachDocumentFromFrame):
(WebCore::DOMWindow::eventListenersDidChange):
* page/DOMWindow.h:
* page/DOMWindowProperty.h:
* storage/Storage.cpp:
(WebCore::Storage::setItem):
(WebCore::Storage::removeItem):
(WebCore::Storage::clear):
* storage/StorageArea.h:
* storage/StorageEventDispatcher.cpp:
(WebCore::dispatchSessionStorageEventsToWindows):
(WebCore::dispatchLocalStorageEventsToWindows):
(WebCore::StorageEventDispatcher::dispatchSessionStorageEvents):
(WebCore::StorageEventDispatcher::dispatchLocalStorageEvents):
(WebCore::StorageEventDispatcher::dispatchSessionStorageEventsToFrames): Deleted.
(WebCore::StorageEventDispatcher::dispatchLocalStorageEventsToFrames): Deleted.
* storage/StorageEventDispatcher.h:

Source/WebKit:

* WebProcess/WebStorage/StorageAreaImpl.cpp:
(WebKit::StorageAreaImpl::setItem):
(WebKit::StorageAreaImpl::removeItem):
(WebKit::StorageAreaImpl::clear):
* WebProcess/WebStorage/StorageAreaImpl.h:
* WebProcess/WebStorage/StorageAreaMap.cpp:
(WebKit::StorageAreaMap::setItem):
(WebKit::StorageAreaMap::removeItem):
(WebKit::StorageAreaMap::clear):
(WebKit::StorageAreaMap::dispatchSessionStorageEvent):
(WebKit::StorageAreaMap::dispatchLocalStorageEvent):
(WebKit::framesForEventDispatching): Deleted.
* WebProcess/WebStorage/StorageAreaMap.h:

Source/WebKitLegacy:

* Storage/StorageAreaImpl.cpp:
(WebKit::StorageAreaImpl::setItem):
(WebKit::StorageAreaImpl::removeItem):
(WebKit::StorageAreaImpl::clear):
(WebKit::StorageAreaImpl::dispatchStorageEvent):
* Storage/StorageAreaImpl.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (290294 => 290295)


--- trunk/Source/WebCore/ChangeLog	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/ChangeLog	2022-02-22 09:47:45 UTC (rev 290295)
@@ -1,3 +1,45 @@
+2022-02-22  Chris Dumez  <[email protected]>
+
+        Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events
+        https://bugs.webkit.org/show_bug.cgi?id=236985
+
+        Reviewed by Darin Adler.
+
+        Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events,
+        by keeping track of window objects interested in storage events. A Window object is interested
+        in storage events if both of the following conditions is true:
+        1. It has a Storage object (either localStorage or sessionStorage)
+        2. It has a storage event listener registered.
+
+        This patch also refactors the code so that we share more logic between WebKit1 and WebKit2.
+
+        * inspector/agents/InspectorDOMStorageAgent.cpp:
+        (WebCore::InspectorDOMStorageAgent::setDOMStorageItem):
+        (WebCore::InspectorDOMStorageAgent::removeDOMStorageItem):
+        (WebCore::InspectorDOMStorageAgent::clearDOMStorageItems):
+        * loader/EmptyClients.cpp:
+        * page/DOMWindow.cpp:
+        (WebCore::windowsInterestedInStorageEvents):
+        (WebCore::DOMWindow::forEachWindowInterestedInStorageEvents):
+        (WebCore::DOMWindow::~DOMWindow):
+        (WebCore::DOMWindow::willDetachDocumentFromFrame):
+        (WebCore::DOMWindow::eventListenersDidChange):
+        * page/DOMWindow.h:
+        * page/DOMWindowProperty.h:
+        * storage/Storage.cpp:
+        (WebCore::Storage::setItem):
+        (WebCore::Storage::removeItem):
+        (WebCore::Storage::clear):
+        * storage/StorageArea.h:
+        * storage/StorageEventDispatcher.cpp:
+        (WebCore::dispatchSessionStorageEventsToWindows):
+        (WebCore::dispatchLocalStorageEventsToWindows):
+        (WebCore::StorageEventDispatcher::dispatchSessionStorageEvents):
+        (WebCore::StorageEventDispatcher::dispatchLocalStorageEvents):
+        (WebCore::StorageEventDispatcher::dispatchSessionStorageEventsToFrames): Deleted.
+        (WebCore::StorageEventDispatcher::dispatchLocalStorageEventsToFrames): Deleted.
+        * storage/StorageEventDispatcher.h:
+
 2022-02-21  Alex Christensen  <[email protected]>
 
         Improve const correctness for SecurityOrigin accessors

Modified: trunk/Source/WebCore/inspector/agents/InspectorDOMStorageAgent.cpp (290294 => 290295)


--- trunk/Source/WebCore/inspector/agents/InspectorDOMStorageAgent.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/inspector/agents/InspectorDOMStorageAgent.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -125,7 +125,7 @@
         return makeUnexpected(errorString);
 
     bool quotaException = false;
-    storageArea->setItem(frame, key, value, quotaException);
+    storageArea->setItem(*frame, key, value, quotaException);
     if (quotaException)
         return makeUnexpected(DOMException::name(QuotaExceededError));
 
@@ -141,7 +141,7 @@
     if (!storageArea)
         return makeUnexpected(errorString);
 
-    storageArea->removeItem(frame, key);
+    storageArea->removeItem(*frame, key);
 
     return { };
 }
@@ -155,7 +155,7 @@
     if (!storageArea)
         return makeUnexpected(errorString);
 
-    storageArea->clear(frame);
+    storageArea->clear(*frame);
 
     return { };
 }

Modified: trunk/Source/WebCore/loader/EmptyClients.cpp (290294 => 290295)


--- trunk/Source/WebCore/loader/EmptyClients.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/loader/EmptyClients.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -484,9 +484,9 @@
         unsigned length() final { return 0; }
         String key(unsigned) final { return { }; }
         String item(const String&) final { return { }; }
-        void setItem(Frame*, const String&, const String&, bool&) final { }
-        void removeItem(Frame*, const String&) final { }
-        void clear(Frame*) final { }
+        void setItem(Frame&, const String&, const String&, bool&) final { }
+        void removeItem(Frame&, const String&) final { }
+        void clear(Frame&) final { }
         bool contains(const String&) final { return false; }
         StorageType storageType() const final { return StorageType::Local; }
         size_t memoryBytesUsedByCache() final { return 0; }

Modified: trunk/Source/WebCore/page/DOMWindow.cpp (290294 => 290295)


--- trunk/Source/WebCore/page/DOMWindow.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/page/DOMWindow.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -156,6 +156,18 @@
 
 static const Seconds defaultTransientActivationDuration { 2_s };
 
+static WeakHashSet<DOMWindow>& windowsInterestedInStorageEvents()
+{
+    static MainThreadNeverDestroyed<WeakHashSet<DOMWindow>> set;
+    return set;
+}
+
+void DOMWindow::forEachWindowInterestedInStorageEvents(const Function<void(DOMWindow&)>& apply)
+{
+    for (auto& window : copyToVectorOf<Ref<DOMWindow>>(windowsInterestedInStorageEvents()))
+        apply(window);
+}
+
 static std::optional<Seconds>& transientActivationDurationOverrideForTesting()
 {
     static NeverDestroyed<std::optional<Seconds>> overrideForTesting;
@@ -442,6 +454,8 @@
 #endif
 
     removeLanguageChangeObserver(this);
+
+    windowsInterestedInStorageEvents().remove(*this);
 }
 
 RefPtr<MediaQueryList> DOMWindow::matchMedia(const String& media)
@@ -499,6 +513,8 @@
     if (m_performance)
         m_performance->clearResourceTimings();
 
+    windowsInterestedInStorageEvents().remove(*this);
+
     JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(this);
     InspectorInstrumentation::frameWindowDiscarded(*frame(), this);
 }
@@ -832,6 +848,8 @@
 
     auto storageArea = page->sessionStorage()->storageArea(document->securityOrigin());
     m_sessionStorage = Storage::create(*this, WTFMove(storageArea));
+    if (hasEventListeners(eventNames().storageEvent))
+        windowsInterestedInStorageEvents().add(*this);
     return m_sessionStorage.get();
 }
 
@@ -866,6 +884,8 @@
 
     auto storageArea = page->storageNamespaceProvider().localStorageArea(*document);
     m_localStorage = Storage::create(*this, WTFMove(storageArea));
+    if (hasEventListeners(eventNames().storageEvent))
+        windowsInterestedInStorageEvents().add(*this);
     return m_localStorage.get();
 }
 
@@ -2697,4 +2717,14 @@
     return document ? document->frame() : nullptr;
 }
 
+void DOMWindow::eventListenersDidChange()
+{
+    if (m_localStorage || m_sessionStorage) {
+        if (hasEventListeners(eventNames().storageEvent))
+            windowsInterestedInStorageEvents().add(*this);
+        else
+            windowsInterestedInStorageEvents().remove(*this);
+    }
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/page/DOMWindow.h (290294 => 290295)


--- trunk/Source/WebCore/page/DOMWindow.h	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/page/DOMWindow.h	2022-02-22 09:47:45 UTC (rev 290295)
@@ -410,6 +410,9 @@
     void setMayReuseForNavigation(bool mayReuseForNavigation) { m_mayReuseForNavigation = mayReuseForNavigation; }
     bool mayReuseForNavigation() const { return m_mayReuseForNavigation; }
 
+    Page* page() const;
+    WEBCORE_EXPORT static void forEachWindowInterestedInStorageEvents(const Function<void(DOMWindow&)>&);
+
 private:
     explicit DOMWindow(Document&);
 
@@ -417,8 +420,8 @@
 
     bool isLocalDOMWindow() const final { return true; }
     bool isRemoteDOMWindow() const final { return false; }
+    void eventListenersDidChange() final;
 
-    Page* page() const;
     bool allowedToChangeWindowGeometry() const;
 
     static ExceptionOr<RefPtr<Frame>> createWindow(const String& urlString, const AtomString& frameName, const WindowFeatures&, DOMWindow& activeWindow, Frame& firstFrame, Frame& openerFrame, const Function<void(DOMWindow&)>& prepareDialogFunction = nullptr);

Modified: trunk/Source/WebCore/page/DOMWindowProperty.h (290294 => 290295)


--- trunk/Source/WebCore/page/DOMWindowProperty.h	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/page/DOMWindowProperty.h	2022-02-22 09:47:45 UTC (rev 290295)
@@ -34,7 +34,7 @@
 
 class DOMWindowProperty {
 public:
-    Frame* frame() const;
+    WEBCORE_EXPORT Frame* frame() const;
     DOMWindow* window() const;
 
 protected:

Modified: trunk/Source/WebCore/storage/Storage.cpp (290294 => 290295)


--- trunk/Source/WebCore/storage/Storage.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/storage/Storage.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -81,7 +81,7 @@
         return Exception { InvalidAccessError };
 
     bool quotaException = false;
-    m_storageArea->setItem(frame, key, value, quotaException);
+    m_storageArea->setItem(*frame, key, value, quotaException);
     if (quotaException)
         return Exception { QuotaExceededError };
     return { };
@@ -93,7 +93,7 @@
     if (!frame)
         return Exception { InvalidAccessError };
 
-    m_storageArea->removeItem(frame, key);
+    m_storageArea->removeItem(*frame, key);
     return { };
 }
 
@@ -103,7 +103,7 @@
     if (!frame)
         return Exception { InvalidAccessError };
 
-    m_storageArea->clear(frame);
+    m_storageArea->clear(*frame);
     return { };
 }
 

Modified: trunk/Source/WebCore/storage/StorageArea.h (290294 => 290295)


--- trunk/Source/WebCore/storage/StorageArea.h	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/storage/StorageArea.h	2022-02-22 09:47:45 UTC (rev 290295)
@@ -46,9 +46,9 @@
     virtual unsigned length() = 0;
     virtual String key(unsigned index) = 0;
     virtual String item(const String& key) = 0;
-    virtual void setItem(Frame* sourceFrame, const String& key, const String& value, bool& quotaException) = 0;
-    virtual void removeItem(Frame* sourceFrame, const String& key) = 0;
-    virtual void clear(Frame* sourceFrame) = 0;
+    virtual void setItem(Frame& sourceFrame, const String& key, const String& value, bool& quotaException) = 0;
+    virtual void removeItem(Frame& sourceFrame, const String& key) = 0;
+    virtual void clear(Frame& sourceFrame) = 0;
     virtual bool contains(const String& key) = 0;
 
     virtual StorageType storageType() const = 0;

Modified: trunk/Source/WebCore/storage/StorageEventDispatcher.cpp (290294 => 290295)


--- trunk/Source/WebCore/storage/StorageEventDispatcher.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/storage/StorageEventDispatcher.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -40,69 +40,68 @@
 
 namespace WebCore {
 
-void StorageEventDispatcher::dispatchSessionStorageEvents(const String& key, const String& oldValue, const String& newValue, const SecurityOrigin& securityOrigin, Frame* sourceFrame)
+static void dispatchSessionStorageEventsToWindows(Page& page, const Vector<Ref<DOMWindow>>& windows, const String& key, const String& oldValue, const String& newValue, const String& url, const SecurityOrigin& securityOrigin)
 {
-    Page* page = sourceFrame->page();
-    if (!page)
-        return;
+    InspectorInstrumentation::didDispatchDOMStorageEvent(page, key, oldValue, newValue, StorageType::Session, securityOrigin);
 
-    Vector<Ref<Frame>> frames;
-
-    // Send events only to our page.
-    for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
-        if (auto* window = frame->window(); !window || !window->hasEventListeners(eventNames().storageEvent))
-            continue;
-        if (sourceFrame != frame && frame->document()->securityOrigin().equal(&securityOrigin))
-            frames.append(*frame);
+    for (auto& window : windows) {
+        RefPtr document = window->document();
+        auto result = window->sessionStorage();
+        if (!result.hasException()) // https://html.spec.whatwg.org/multipage/webstorage.html#the-storage-event:event-storage
+            document->queueTaskToDispatchEventOnWindow(TaskSource::DOMManipulation, StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, url, result.releaseReturnValue()));
     }
-
-    dispatchSessionStorageEventsToFrames(*page, frames, key, oldValue, newValue, sourceFrame->document()->url().string(), securityOrigin);
 }
 
-void StorageEventDispatcher::dispatchLocalStorageEvents(const String& key, const String& oldValue, const String& newValue, const SecurityOrigin& securityOrigin, Frame* sourceFrame)
+static void dispatchLocalStorageEventsToWindows(PageGroup& pageGroup, const Vector<Ref<DOMWindow>>& windows, const String& key, const String& oldValue, const String& newValue, const String& url, const SecurityOrigin& securityOrigin)
 {
-    Page* page = sourceFrame->page();
-    if (!page)
-        return;
+    for (auto& page : pageGroup.pages())
+        InspectorInstrumentation::didDispatchDOMStorageEvent(page, key, oldValue, newValue, StorageType::Local, securityOrigin);
 
-    Vector<Ref<Frame>> frames;
-
-    // Send events to every page.
-    for (auto& pageInGroup : page->group().pages()) {
-        for (auto* frame = &pageInGroup.mainFrame(); frame; frame = frame->tree().traverseNext()) {
-            if (auto* window = frame->window(); !window || !window->hasEventListeners(eventNames().storageEvent))
-                continue;
-            if (sourceFrame != frame && frame->document()->securityOrigin().equal(&securityOrigin))
-                frames.append(*frame);
-        }
+    for (auto& window : windows) {
+        RefPtr document = window->document();
+        auto result = window->localStorage();
+        if (!result.hasException()) // https://html.spec.whatwg.org/multipage/webstorage.html#the-storage-event:event-storage
+            document->queueTaskToDispatchEventOnWindow(TaskSource::DOMManipulation, StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, url, result.releaseReturnValue()));
     }
-
-    dispatchLocalStorageEventsToFrames(page->group(), frames, key, oldValue, newValue, sourceFrame->document()->url().string(), securityOrigin);
 }
 
-void StorageEventDispatcher::dispatchSessionStorageEventsToFrames(Page& page, const Vector<Ref<Frame>>& frames, const String& key, const String& oldValue, const String& newValue, const String& url, const SecurityOrigin& securityOrigin)
+void StorageEventDispatcher::dispatchSessionStorageEvents(const String& key, const String& oldValue, const String& newValue, Page& page, const SecurityOrigin& securityOrigin, const String& url, const Function<bool(Storage&)>& isSourceStorage)
 {
-    InspectorInstrumentation::didDispatchDOMStorageEvent(page, key, oldValue, newValue, StorageType::Session, securityOrigin);
+    Vector<Ref<DOMWindow>> windows;
+    DOMWindow::forEachWindowInterestedInStorageEvents([&](auto& window) {
+        if (!window.optionalSessionStorage())
+            return;
+        // Send events only to our page.
+        if (window.page() != &page)
+            return;
+        if (isSourceStorage(*window.optionalSessionStorage()))
+            return;
+        if (!securityOrigin.equal(window.securityOrigin()))
+            return;
+        windows.append(window);
+    });
 
-    for (auto& frame : frames) {
-        RefPtr document = frame->document();
-        auto result = document->domWindow()->sessionStorage();
-        if (!result.hasException()) // https://html.spec.whatwg.org/multipage/webstorage.html#the-storage-event:event-storage
-            document->queueTaskToDispatchEventOnWindow(TaskSource::DOMManipulation, StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, url, result.releaseReturnValue()));
-    }
+    dispatchSessionStorageEventsToWindows(page, windows, key, oldValue, newValue, url, securityOrigin);
 }
 
-void StorageEventDispatcher::dispatchLocalStorageEventsToFrames(PageGroup& pageGroup, const Vector<Ref<Frame>>& frames, const String& key, const String& oldValue, const String& newValue, const String& url, const SecurityOrigin& securityOrigin)
+void StorageEventDispatcher::dispatchLocalStorageEvents(const String& key, const String& oldValue, const String& newValue, PageGroup& pageGroup, const SecurityOrigin& securityOrigin, const String& url, const Function<bool(Storage&)>& isSourceStorage)
 {
-    for (auto& page : pageGroup.pages())
-        InspectorInstrumentation::didDispatchDOMStorageEvent(page, key, oldValue, newValue, StorageType::Local, securityOrigin);
+    auto& pagesInGroup = pageGroup.pages();
+    Vector<Ref<DOMWindow>> windows;
+    DOMWindow::forEachWindowInterestedInStorageEvents([&](auto& window) {
+        if (!window.optionalLocalStorage())
+            return;
+        // Send events to every page in the group.
+        if (!window.page() || !pagesInGroup.contains(*window.page()))
+            return;
+        if (isSourceStorage(*window.optionalLocalStorage()))
+            return;
+        if (!securityOrigin.equal(window.securityOrigin()))
+            return;
+        windows.append(window);
+    });
 
-    for (auto& frame : frames) {
-        RefPtr document = frame->document();
-        auto result = document->domWindow()->localStorage();
-        if (!result.hasException()) // https://html.spec.whatwg.org/multipage/webstorage.html#the-storage-event:event-storage
-            document->queueTaskToDispatchEventOnWindow(TaskSource::DOMManipulation, StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, url, result.releaseReturnValue()));
-    }
+    dispatchLocalStorageEventsToWindows(pageGroup, windows, key, oldValue, newValue, url, securityOrigin);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/storage/StorageEventDispatcher.h (290294 => 290295)


--- trunk/Source/WebCore/storage/StorageEventDispatcher.h	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebCore/storage/StorageEventDispatcher.h	2022-02-22 09:47:45 UTC (rev 290295)
@@ -33,21 +33,14 @@
 
 namespace WebCore {
 
-class Frame;
 class Page;
 class PageGroup;
 class SecurityOrigin;
+class Storage;
 
-class StorageEventDispatcher {
-public:
-    WEBCORE_EXPORT static void dispatchSessionStorageEvents(const String& key, const String& oldValue, const String& newValue, const SecurityOrigin&, Frame* sourceFrame);
-    WEBCORE_EXPORT static void dispatchLocalStorageEvents(const String& key, const String& oldValue, const String& newValue, const SecurityOrigin&, Frame* sourceFrame);
+namespace StorageEventDispatcher {
+WEBCORE_EXPORT void dispatchSessionStorageEvents(const String& key, const String& oldValue, const String& newValue, Page&, const SecurityOrigin&, const String& url, const Function<bool(Storage&)>& isSourceStorage);
+WEBCORE_EXPORT void dispatchLocalStorageEvents(const String& key, const String& oldValue, const String& newValue, PageGroup&, const SecurityOrigin&, const String& url, const Function<bool(Storage&)>& isSourceStorage);
+}
 
-    WEBCORE_EXPORT static void dispatchSessionStorageEventsToFrames(Page&, const Vector<Ref<Frame>>& frames, const String& key, const String& oldValue, const String& newValue, const String& url, const SecurityOrigin&);
-    WEBCORE_EXPORT static void dispatchLocalStorageEventsToFrames(PageGroup&, const Vector<Ref<Frame>>& frames, const String& key, const String& oldValue, const String& newValue, const String& url, const SecurityOrigin&);
-private:
-    // Do not instantiate.
-    StorageEventDispatcher();
-};
-
 } // namespace WebCore

Modified: trunk/Source/WebKit/ChangeLog (290294 => 290295)


--- trunk/Source/WebKit/ChangeLog	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKit/ChangeLog	2022-02-22 09:47:45 UTC (rev 290295)
@@ -1,3 +1,32 @@
+2022-02-22  Chris Dumez  <[email protected]>
+
+        Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events
+        https://bugs.webkit.org/show_bug.cgi?id=236985
+
+        Reviewed by Darin Adler.
+
+        Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events,
+        by keeping track of window objects interested in storage events. A Window object is interested
+        in storage events if both of the following conditions is true:
+        1. It has a Storage object (either localStorage or sessionStorage)
+        2. It has a storage event listener registered.
+
+        This patch also refactors the code so that we share more logic between WebKit1 and WebKit2.
+
+        * WebProcess/WebStorage/StorageAreaImpl.cpp:
+        (WebKit::StorageAreaImpl::setItem):
+        (WebKit::StorageAreaImpl::removeItem):
+        (WebKit::StorageAreaImpl::clear):
+        * WebProcess/WebStorage/StorageAreaImpl.h:
+        * WebProcess/WebStorage/StorageAreaMap.cpp:
+        (WebKit::StorageAreaMap::setItem):
+        (WebKit::StorageAreaMap::removeItem):
+        (WebKit::StorageAreaMap::clear):
+        (WebKit::StorageAreaMap::dispatchSessionStorageEvent):
+        (WebKit::StorageAreaMap::dispatchLocalStorageEvent):
+        (WebKit::framesForEventDispatching): Deleted.
+        * WebProcess/WebStorage/StorageAreaMap.h:
+
 2022-02-22  Kate Cheney  <[email protected]>
 
         NSSharingServicePicker gets deallocated when using the standard share menu item

Modified: trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaImpl.cpp (290294 => 290295)


--- trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaImpl.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaImpl.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -70,7 +70,7 @@
     return m_storageAreaMap ? m_storageAreaMap->item(key) : nullString();
 }
 
-void StorageAreaImpl::setItem(Frame* sourceFrame, const String& key, const String& value, bool& quotaException)
+void StorageAreaImpl::setItem(Frame& sourceFrame, const String& key, const String& value, bool& quotaException)
 {
     ASSERT(!value.isNull());
 
@@ -78,13 +78,13 @@
         m_storageAreaMap->setItem(sourceFrame, this, key, value, quotaException);
 }
 
-void StorageAreaImpl::removeItem(Frame* sourceFrame, const String& key)
+void StorageAreaImpl::removeItem(Frame& sourceFrame, const String& key)
 {
     if (m_storageAreaMap)
         m_storageAreaMap->removeItem(sourceFrame, this, key);
 }
 
-void StorageAreaImpl::clear(Frame* sourceFrame)
+void StorageAreaImpl::clear(Frame& sourceFrame)
 {
     if (m_storageAreaMap)
         m_storageAreaMap->clear(sourceFrame, this);

Modified: trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaImpl.h (290294 => 290295)


--- trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaImpl.h	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaImpl.h	2022-02-22 09:47:45 UTC (rev 290295)
@@ -55,9 +55,9 @@
     unsigned length() override;
     String key(unsigned index) override;
     String item(const String& key) override;
-    void setItem(WebCore::Frame* sourceFrame, const String& key, const String& value, bool& quotaException) override;
-    void removeItem(WebCore::Frame* sourceFrame, const String& key) override;
-    void clear(WebCore::Frame* sourceFrame) override;
+    void setItem(WebCore::Frame& sourceFrame, const String& key, const String& value, bool& quotaException) override;
+    void removeItem(WebCore::Frame& sourceFrame, const String& key) override;
+    void clear(WebCore::Frame& sourceFrame) override;
     bool contains(const String& key) override;
     WebCore::StorageType storageType() const override;
     size_t memoryBytesUsedByCache() override;

Modified: trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaMap.cpp (290294 => 290295)


--- trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaMap.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaMap.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -82,7 +82,7 @@
     return ensureMap().getItem(key);
 }
 
-void StorageAreaMap::setItem(Frame* sourceFrame, StorageAreaImpl* sourceArea, const String& key, const String& value, bool& quotaException)
+void StorageAreaMap::setItem(Frame& sourceFrame, StorageAreaImpl* sourceArea, const String& key, const String& value, bool& quotaException)
 {
     auto& map = ensureMap();
     ASSERT(!map.isShared());
@@ -108,10 +108,10 @@
             weakThis->didSetItem(seed, key, hasQuotaException);
     };
     auto& connection = WebProcess::singleton().ensureNetworkProcessConnection().connection();
-    connection.sendWithAsyncReply(Messages::NetworkStorageManager::SetItem(*m_remoteAreaIdentifier, sourceArea->identifier(), key, value, sourceFrame->document()->url().string()), WTFMove(callback));
+    connection.sendWithAsyncReply(Messages::NetworkStorageManager::SetItem(*m_remoteAreaIdentifier, sourceArea->identifier(), key, value, sourceFrame.document()->url().string()), WTFMove(callback));
 }
 
-void StorageAreaMap::removeItem(WebCore::Frame* sourceFrame, StorageAreaImpl* sourceArea, const String& key)
+void StorageAreaMap::removeItem(WebCore::Frame& sourceFrame, StorageAreaImpl* sourceArea, const String& key)
 {
     auto& map = ensureMap();
     ASSERT(!map.isShared());
@@ -133,10 +133,10 @@
         if (weakThis)
             weakThis->didRemoveItem(seed, key);
     };
-    WebProcess::singleton().ensureNetworkProcessConnection().connection().sendWithAsyncReply(Messages::NetworkStorageManager::RemoveItem(*m_remoteAreaIdentifier, sourceArea->identifier(), key, sourceFrame->document()->url().string()), WTFMove(callback));
+    WebProcess::singleton().ensureNetworkProcessConnection().connection().sendWithAsyncReply(Messages::NetworkStorageManager::RemoveItem(*m_remoteAreaIdentifier, sourceArea->identifier(), key, sourceFrame.document()->url().string()), WTFMove(callback));
 }
 
-void StorageAreaMap::clear(WebCore::Frame* sourceFrame, StorageAreaImpl* sourceArea)
+void StorageAreaMap::clear(WebCore::Frame& sourceFrame, StorageAreaImpl* sourceArea)
 {
     connectSync();
     resetValues();
@@ -153,7 +153,7 @@
         if (weakThis)
             weakThis->didClear(seed);
     };
-    WebProcess::singleton().ensureNetworkProcessConnection().connection().sendWithAsyncReply(Messages::NetworkStorageManager::Clear(*m_remoteAreaIdentifier, sourceArea->identifier(), sourceFrame->document()->url().string()), WTFMove(callback));
+    WebProcess::singleton().ensureNetworkProcessConnection().connection().sendWithAsyncReply(Messages::NetworkStorageManager::Clear(*m_remoteAreaIdentifier, sourceArea->identifier(), sourceFrame.document()->url().string()), WTFMove(callback));
 }
 
 bool StorageAreaMap::contains(const String& key)
@@ -296,43 +296,6 @@
     resetValues();
 }
 
-static Vector<Ref<Frame>> framesForEventDispatching(Page& page, const SecurityOrigin& origin, StorageType storageType, const std::optional<StorageAreaImplIdentifier>& storageAreaImplID)
-{
-    Vector<Ref<Frame>> frames;
-    page.forEachDocument([&](auto& document) {
-        if (!document.securityOrigin().equal(&origin))
-            return;
-
-        auto* window = document.domWindow();
-        if (!window || !window->hasEventListeners(eventNames().storageEvent))
-            return;
-        
-        Storage* storage = nullptr;
-        switch (storageType) {
-        case StorageType::Session:
-            storage = window->optionalSessionStorage();
-            break;
-        case StorageType::Local:
-        case StorageType::TransientLocal:
-            storage = window->optionalLocalStorage();
-            break;
-        }
-
-        if (!storage)
-            return;
-        
-        auto& storageArea = static_cast<StorageAreaImpl&>(storage->area());
-        if (storageArea.identifier() == storageAreaImplID) {
-            // This is the storage area that caused the event to be dispatched.
-            return;
-        }
-       
-        if (auto* frame = document.frame()) 
-            frames.append(*frame);
-    });
-    return frames;
-}
-
 void StorageAreaMap::dispatchSessionStorageEvent(const std::optional<StorageAreaImplIdentifier>& storageAreaImplID, const String& key, const String& oldValue, const String& newValue, const String& urlString)
 {
     // Namespace IDs for session storage namespaces are equivalent to web page IDs
@@ -345,8 +308,9 @@
     if (!page)
         return;
 
-    auto frames = framesForEventDispatching(*page, m_securityOrigin, StorageType::Session, storageAreaImplID);
-    StorageEventDispatcher::dispatchSessionStorageEventsToFrames(*page, frames, key, oldValue, newValue, urlString, m_securityOrigin);
+    StorageEventDispatcher::dispatchSessionStorageEvents(key, oldValue, newValue, *page, m_securityOrigin, urlString, [storageAreaImplID](auto& storage) {
+        return static_cast<StorageAreaImpl&>(storage.area()).identifier() == storageAreaImplID;
+    });
 }
 
 void StorageAreaMap::dispatchLocalStorageEvent(const std::optional<StorageAreaImplIdentifier>& storageAreaImplID, const String& key, const String& oldValue, const String& newValue, const String& urlString)
@@ -353,14 +317,11 @@
 {
     ASSERT(isLocalStorage(type()));
 
-    Vector<Ref<Frame>> frames;
-
     // Namespace IDs for local storage namespaces are currently equivalent to web page group IDs.
     auto& pageGroup = *WebProcess::singleton().webPageGroup(m_namespace.pageGroupID())->corePageGroup();
-    for (auto& page : pageGroup.pages())
-        frames.appendVector(framesForEventDispatching(page, m_securityOrigin, StorageType::Local, storageAreaImplID));
-
-    StorageEventDispatcher::dispatchLocalStorageEventsToFrames(pageGroup, frames, key, oldValue, newValue, urlString, m_securityOrigin);
+    StorageEventDispatcher::dispatchLocalStorageEvents(key, oldValue, newValue, pageGroup, m_securityOrigin, urlString, [storageAreaImplID](auto& storage) {
+        return static_cast<StorageAreaImpl&>(storage.area()).identifier() == storageAreaImplID;
+    });
 }
 
 void StorageAreaMap::sendConnectMessage(SendMode mode)

Modified: trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaMap.h (290294 => 290295)


--- trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaMap.h	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKit/WebProcess/WebStorage/StorageAreaMap.h	2022-02-22 09:47:45 UTC (rev 290295)
@@ -58,9 +58,9 @@
     unsigned length();
     String key(unsigned index);
     String item(const String& key);
-    void setItem(WebCore::Frame* sourceFrame, StorageAreaImpl* sourceArea, const String& key, const String& value, bool& quotaException);
-    void removeItem(WebCore::Frame* sourceFrame, StorageAreaImpl* sourceArea, const String& key);
-    void clear(WebCore::Frame* sourceFrame, StorageAreaImpl* sourceArea);
+    void setItem(WebCore::Frame& sourceFrame, StorageAreaImpl* sourceArea, const String& key, const String& value, bool& quotaException);
+    void removeItem(WebCore::Frame& sourceFrame, StorageAreaImpl* sourceArea, const String& key);
+    void clear(WebCore::Frame& sourceFrame, StorageAreaImpl* sourceArea);
     bool contains(const String& key);
 
     // IPC::MessageReceiver

Modified: trunk/Source/WebKitLegacy/ChangeLog (290294 => 290295)


--- trunk/Source/WebKitLegacy/ChangeLog	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKitLegacy/ChangeLog	2022-02-22 09:47:45 UTC (rev 290295)
@@ -1,3 +1,25 @@
+2022-02-22  Chris Dumez  <[email protected]>
+
+        Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events
+        https://bugs.webkit.org/show_bug.cgi?id=236985
+
+        Reviewed by Darin Adler.
+
+        Avoid having to iterate the whole frame tree(s) every time we need to dispatch storage events,
+        by keeping track of window objects interested in storage events. A Window object is interested
+        in storage events if both of the following conditions is true:
+        1. It has a Storage object (either localStorage or sessionStorage)
+        2. It has a storage event listener registered.
+
+        This patch also refactors the code so that we share more logic between WebKit1 and WebKit2.
+
+        * Storage/StorageAreaImpl.cpp:
+        (WebKit::StorageAreaImpl::setItem):
+        (WebKit::StorageAreaImpl::removeItem):
+        (WebKit::StorageAreaImpl::clear):
+        (WebKit::StorageAreaImpl::dispatchStorageEvent):
+        * Storage/StorageAreaImpl.h:
+
 2022-02-19  Chris Dumez  <[email protected]>
 
         Optimize DOM storage event dispatch

Modified: trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.cpp (290294 => 290295)


--- trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.cpp	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.cpp	2022-02-22 09:47:45 UTC (rev 290295)
@@ -28,9 +28,12 @@
 #include "StorageAreaSync.h"
 #include "StorageSyncManager.h"
 #include "StorageTracker.h"
+#include <WebCore/DOMWindow.h>
 #include <WebCore/Frame.h>
+#include <WebCore/Page.h>
 #include <WebCore/SecurityOrigin.h>
 #include <WebCore/SecurityOriginData.h>
+#include <WebCore/Storage.h>
 #include <WebCore/StorageEventDispatcher.h>
 #include <WebCore/StorageType.h>
 #include <wtf/MainThread.h>
@@ -121,7 +124,7 @@
     return m_storageMap.getItem(key);
 }
 
-void StorageAreaImpl::setItem(Frame* sourceFrame, const String& key, const String& value, bool& quotaException)
+void StorageAreaImpl::setItem(Frame& sourceFrame, const String& key, const String& value, bool& quotaException)
 {
     ASSERT(!m_isShutdown);
     ASSERT(!value.isNull());
@@ -141,7 +144,7 @@
     dispatchStorageEvent(key, oldValue, value, sourceFrame);
 }
 
-void StorageAreaImpl::removeItem(Frame* sourceFrame, const String& key)
+void StorageAreaImpl::removeItem(Frame& sourceFrame, const String& key)
 {
     ASSERT(!m_isShutdown);
     blockUntilImportComplete();
@@ -157,7 +160,7 @@
     dispatchStorageEvent(key, oldValue, String(), sourceFrame);
 }
 
-void StorageAreaImpl::clear(Frame* sourceFrame)
+void StorageAreaImpl::clear(Frame& sourceFrame)
 {
     ASSERT(!m_isShutdown);
     blockUntilImportComplete();
@@ -269,12 +272,19 @@
     }
 }
 
-void StorageAreaImpl::dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame)
+void StorageAreaImpl::dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame& sourceFrame)
 {
+    auto* page = sourceFrame.page();
+    if (!page)
+        return;
+
+    auto isSourceStorage = [&sourceFrame](Storage& storage) {
+        return storage.frame() == &sourceFrame;
+    };
     if (isLocalStorage(m_storageType))
-        StorageEventDispatcher::dispatchLocalStorageEvents(key, oldValue, newValue, m_securityOrigin, sourceFrame);
+        StorageEventDispatcher::dispatchLocalStorageEvents(key, oldValue, newValue, page->group(), m_securityOrigin, sourceFrame.document()->url().string(), WTFMove(isSourceStorage));
     else
-        StorageEventDispatcher::dispatchSessionStorageEvents(key, oldValue, newValue, m_securityOrigin, sourceFrame);
+        StorageEventDispatcher::dispatchSessionStorageEvents(key, oldValue, newValue, *page, m_securityOrigin, sourceFrame.document()->url().string(), WTFMove(isSourceStorage));
 }
 
 void StorageAreaImpl::sessionChanged(bool isNewSessionPersistent)

Modified: trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.h (290294 => 290295)


--- trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.h	2022-02-22 09:42:11 UTC (rev 290294)
+++ trunk/Source/WebKitLegacy/Storage/StorageAreaImpl.h	2022-02-22 09:47:45 UTC (rev 290295)
@@ -49,9 +49,9 @@
     unsigned length() override;
     String key(unsigned index) override;
     String item(const String& key) override;
-    void setItem(WebCore::Frame* sourceFrame, const String& key, const String& value, bool& quotaException) override;
-    void removeItem(WebCore::Frame* sourceFrame, const String& key) override;
-    void clear(WebCore::Frame* sourceFrame) override;
+    void setItem(WebCore::Frame& sourceFrame, const String& key, const String& value, bool& quotaException) override;
+    void removeItem(WebCore::Frame& sourceFrame, const String& key) override;
+    void clear(WebCore::Frame& sourceFrame) override;
     bool contains(const String& key) override;
 
     WebCore::StorageType storageType() const override;
@@ -82,7 +82,7 @@
     void blockUntilImportComplete() const;
     void closeDatabaseTimerFired();
 
-    void dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, WebCore::Frame* sourceFrame);
+    void dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, WebCore::Frame& sourceFrame);
 
     WebCore::StorageType m_storageType;
     Ref<const WebCore::SecurityOrigin> m_securityOrigin;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to