Title: [276724] branches/safari-607-branch/Source/WebCore

Diff

Modified: branches/safari-607-branch/Source/WebCore/PlatformWin.cmake (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/PlatformWin.cmake	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/PlatformWin.cmake	2021-04-28 18:12:18 UTC (rev 276724)
@@ -34,6 +34,7 @@
     platform/Cursor.cpp
     platform/LocalizedStrings.cpp
     platform/StaticPasteboard.cpp
+    platform/SuspendableTaskQueue.cpp
 
     platform/audio/PlatformMediaSessionManager.cpp
 

Modified: branches/safari-607-branch/Source/WebCore/Sources.txt (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/Sources.txt	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/Sources.txt	2021-04-28 18:12:18 UTC (rev 276724)
@@ -1592,6 +1592,7 @@
 platform/SharedBufferChunkReader.cpp
 platform/SharedStringHash.cpp
 platform/StaticPasteboard.cpp
+platform/SuspendableTaskQueue.cpp
 platform/Theme.cpp
 platform/ThreadGlobalData.cpp
 platform/ThreadTimers.cpp

Modified: branches/safari-607-branch/Source/WebCore/WebCore.xcodeproj/project.pbxproj (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-04-28 18:12:18 UTC (rev 276724)
@@ -1262,6 +1262,7 @@
 		46C696CC1E7205FC00597937 /* CPUMonitor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 46C696CA1E7205E400597937 /* CPUMonitor.cpp */; };
 		46C83EFE1A9BBE2900A79A41 /* GeoNotifier.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C83EFC1A9BBE2900A79A41 /* GeoNotifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		46CA9C441F97BBE9004CFC3A /* VisibilityState.h in Headers */ = {isa = PBXBuildFile; fileRef = 46CA9C411F97BBE7004CFC3A /* VisibilityState.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		46CDC1F023466B2300D2E70E /* SuspendableTaskQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 466907F82346686200EFEFF0 /* SuspendableTaskQueue.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		46DBB6501AB8C96F00D9A813 /* PowerObserverMac.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DBB64E1AB8C96F00D9A813 /* PowerObserverMac.h */; };
 		46DFF4981DC2603100B80B48 /* ShadowRootMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DFF4961DC2601300B80B48 /* ShadowRootMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		46DFF49C1DC2620B00B80B48 /* JSShadowRootMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 46DFF49A1DC261F900B80B48 /* JSShadowRootMode.h */; };
@@ -7636,6 +7637,8 @@
 		458FE4071589DF0B005609E6 /* RenderSearchField.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderSearchField.cpp; sourceTree = "<group>"; };
 		458FE4081589DF0B005609E6 /* RenderSearchField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderSearchField.h; sourceTree = "<group>"; };
 		45FEA5CD156DDE8C00654101 /* Decimal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Decimal.cpp; sourceTree = "<group>"; };
+		466907F62346686200EFEFF0 /* SuspendableTaskQueue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SuspendableTaskQueue.cpp; sourceTree = "<group>"; };
+		466907F82346686200EFEFF0 /* SuspendableTaskQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SuspendableTaskQueue.h; sourceTree = "<group>"; };
 		45FEA5CE156DDE8C00654101 /* Decimal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Decimal.h; sourceTree = "<group>"; };
 		460BB6131D0A1BEC00221812 /* Base64Utilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Base64Utilities.cpp; sourceTree = "<group>"; };
 		460BB6141D0A1BEC00221812 /* Base64Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base64Utilities.h; sourceTree = "<group>"; };
@@ -25239,6 +25242,8 @@
 				F433E9021DBBDBA200EF0D14 /* StaticPasteboard.h */,
 				93B2D8150F9920D2006AE6B2 /* SuddenTermination.h */,
 				97627B9714FB5424002CDCA1 /* Supplementable.h */,
+				466907F62346686200EFEFF0 /* SuspendableTaskQueue.cpp */,
+				466907F82346686200EFEFF0 /* SuspendableTaskQueue.h */,
 				7CC564B618BABEA6001B9652 /* TelephoneNumberDetector.h */,
 				BCE65D310EAD1211007E4533 /* Theme.cpp */,
 				BCE658FE0EA9248A007E4533 /* Theme.h */,
@@ -31280,6 +31285,7 @@
 				97C078501165D5BE003A32EF /* SuffixTree.h in Headers */,
 				97627B9814FB5424002CDCA1 /* Supplementable.h in Headers */,
 				087558C613B4A57D00F49307 /* SurrogatePairAwareTextIterator.h in Headers */,
+				46CDC1F023466B2300D2E70E /* SuspendableTaskQueue.h in Headers */,
 				62C1217D11AB9E77003C462C /* SuspendableTimer.h in Headers */,
 				B22279740D00BF220071B782 /* SVGAElement.h in Headers */,
 				24D912B113CA9A1F00D21915 /* SVGAltGlyphDefElement.h in Headers */,

Modified: branches/safari-607-branch/Source/WebCore/dom/ActiveDOMObject.cpp (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/dom/ActiveDOMObject.cpp	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/dom/ActiveDOMObject.cpp	2021-04-28 18:12:18 UTC (rev 276724)
@@ -113,4 +113,9 @@
     return !scriptExecutionContext() || scriptExecutionContext()->activeDOMObjectsAreStopped();
 }
 
+bool ActiveDOMObject::isAllowedToRunScript() const
+{
+    return scriptExecutionContext() && !scriptExecutionContext()->activeDOMObjectsAreStopped() && !scriptExecutionContext()->activeDOMObjectsAreSuspended();
+}
+
 } // namespace WebCore

Modified: branches/safari-607-branch/Source/WebCore/dom/ActiveDOMObject.h (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/dom/ActiveDOMObject.h	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/dom/ActiveDOMObject.h	2021-04-28 18:12:18 UTC (rev 276724)
@@ -113,6 +113,7 @@
     }
 
     bool isContextStopped() const;
+    bool isAllowedToRunScript() const;
 
 protected:
     explicit ActiveDOMObject(ScriptExecutionContext*);

Modified: branches/safari-607-branch/Source/WebCore/dom/ScriptExecutionContext.cpp (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/dom/ScriptExecutionContext.cpp	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/dom/ScriptExecutionContext.cpp	2021-04-28 18:12:18 UTC (rev 276724)
@@ -300,12 +300,13 @@
 
     if (m_reasonForSuspendingActiveDOMObjects != why)
         return;
-    m_activeDOMObjectsAreSuspended = false;
 
     forEachActiveDOMObject([](auto& activeDOMObject) {
         activeDOMObject.resume();
         return ShouldContinue::Yes;
     });
+
+    m_activeDOMObjectsAreSuspended = false;
 }
 
 void ScriptExecutionContext::stopActiveDOMObjects()

Modified: branches/safari-607-branch/Source/WebCore/fileapi/FileReader.cpp (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/fileapi/FileReader.cpp	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/fileapi/FileReader.cpp	2021-04-28 18:12:18 UTC (rev 276724)
@@ -36,6 +36,7 @@
 #include "Logging.h"
 #include "ProgressEvent.h"
 #include "ScriptExecutionContext.h"
+#include "SuspendableTaskQueue.h"
 #include <_javascript_Core/ArrayBuffer.h>
 #include <wtf/text/CString.h>
 
@@ -53,6 +54,7 @@
 
 FileReader::FileReader(ScriptExecutionContext& context)
     : ActiveDOMObject(&context)
+    , m_taskQueue(SuspendableTaskQueue::create(&context))
 {
 }
 
@@ -62,12 +64,6 @@
         m_loader->cancel();
 }
 
-bool FileReader::canSuspendForDocumentSuspension() const
-{
-    // FIXME: It is not currently possible to suspend a FileReader, so pages with FileReader can not go into page cache.
-    return false;
-}
-
 const char* FileReader::activeDOMObjectName() const
 {
     return "FileReader";
@@ -82,6 +78,16 @@
     m_state = DONE;
 }
 
+bool FileReader::canSuspendForDocumentSuspension() const
+{
+    return true;
+}
+
+bool FileReader::hasPendingActivity() const
+{
+    return m_taskQueue->hasPendingTasks() || m_state == LOADING || ActiveDOMObject::hasPendingActivity();
+}
+
 ExceptionOr<void> FileReader::readAsArrayBuffer(Blob* blob)
 {
     if (!blob)
@@ -129,8 +135,6 @@
     if (m_state == LOADING)
         return Exception { InvalidStateError };
 
-    setPendingActivity(this);
-
     m_blob = &blob;
     m_readType = type;
     m_state = LOADING;
@@ -153,7 +157,8 @@
     m_aborting = true;
 
     // Schedule to have the abort done later since abort() might be called from the event handler and we do not want the resource loading code to be in the stack.
-    scriptExecutionContext()->postTask([this] (ScriptExecutionContext&) {
+    m_taskQueue->cancelAllTasks();
+    m_taskQueue->enqueueTask([this] {
         ASSERT(m_state != DONE);
 
         stop();
@@ -164,28 +169,29 @@
         fireEvent(eventNames().errorEvent);
         fireEvent(eventNames().abortEvent);
         fireEvent(eventNames().loadendEvent);
-
-        // All possible events have fired and we're done, no more pending activity.
-        unsetPendingActivity(this);
     });
 }
 
 void FileReader::didStartLoading()
 {
-    fireEvent(eventNames().loadstartEvent);
+    m_taskQueue->enqueueTask([this] {
+        fireEvent(eventNames().loadstartEvent);
+    });
 }
 
 void FileReader::didReceiveData()
 {
-    auto now = MonotonicTime::now();
-    if (std::isnan(m_lastProgressNotificationTime)) {
-        m_lastProgressNotificationTime = now;
-        return;
-    }
-    if (now - m_lastProgressNotificationTime > progressNotificationInterval) {
-        fireEvent(eventNames().progressEvent);
-        m_lastProgressNotificationTime = now;
-    }
+    m_taskQueue->enqueueTask([this] {
+        auto now = MonotonicTime::now();
+        if (std::isnan(m_lastProgressNotificationTime)) {
+            m_lastProgressNotificationTime = now;
+            return;
+        }
+        if (now - m_lastProgressNotificationTime > progressNotificationInterval) {
+            fireEvent(eventNames().progressEvent);
+            m_lastProgressNotificationTime = now;
+        }
+    });
 }
 
 void FileReader::didFinishLoading()
@@ -193,15 +199,14 @@
     if (m_aborting)
         return;
 
-    ASSERT(m_state != DONE);
-    m_state = DONE;
+    m_taskQueue->enqueueTask([this] {
+        ASSERT(m_state != DONE);
+        m_state = DONE;
 
-    fireEvent(eventNames().progressEvent);
-    fireEvent(eventNames().loadEvent);
-    fireEvent(eventNames().loadendEvent);
-    
-    // All possible events have fired and we're done, no more pending activity.
-    unsetPendingActivity(this);
+        fireEvent(eventNames().progressEvent);
+        fireEvent(eventNames().loadEvent);
+        fireEvent(eventNames().loadendEvent);
+    });
 }
 
 void FileReader::didFail(int errorCode)
@@ -210,19 +215,19 @@
     if (m_aborting)
         return;
 
-    ASSERT(m_state != DONE);
-    m_state = DONE;
+    m_taskQueue->enqueueTask([this, errorCode] {
+        ASSERT(m_state != DONE);
+        m_state = DONE;
 
-    m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
-    fireEvent(eventNames().errorEvent);
-    fireEvent(eventNames().loadendEvent);
-    
-    // All possible events have fired and we're done, no more pending activity.
-    unsetPendingActivity(this);
+        m_error = FileError::create(static_cast<FileError::ErrorCode>(errorCode));
+        fireEvent(eventNames().errorEvent);
+        fireEvent(eventNames().loadendEvent);
+    });
 }
 
 void FileReader::fireEvent(const AtomicString& type)
 {
+    RELEASE_ASSERT(isAllowedToRunScript());
     dispatchEvent(ProgressEvent::create(type, true, m_loader ? m_loader->bytesLoaded() : 0, m_loader ? m_loader->totalBytes() : 0));
 }
 

Modified: branches/safari-607-branch/Source/WebCore/fileapi/FileReader.h (276723 => 276724)


--- branches/safari-607-branch/Source/WebCore/fileapi/FileReader.h	2021-04-28 18:07:17 UTC (rev 276723)
+++ branches/safari-607-branch/Source/WebCore/fileapi/FileReader.h	2021-04-28 18:12:18 UTC (rev 276724)
@@ -36,6 +36,7 @@
 #include "FileError.h"
 #include "FileReaderLoader.h"
 #include "FileReaderLoaderClient.h"
+#include <wtf/UniqueRef.h>
 
 namespace JSC {
 class ArrayBuffer;
@@ -44,6 +45,7 @@
 namespace WebCore {
 
 class Blob;
+class SuspendableTaskQueue;
 
 class FileReader final : public RefCounted<FileReader>, public ActiveDOMObject, public EventTargetWithInlineData, private FileReaderLoaderClient {
 public:
@@ -73,6 +75,9 @@
     using RefCounted::ref;
     using RefCounted::deref;
 
+    // ActiveDOMObject
+    bool hasPendingActivity() const final;
+
 private:
     explicit FileReader(ScriptExecutionContext&);
 
@@ -102,6 +107,7 @@
     std::unique_ptr<FileReaderLoader> m_loader;
     RefPtr<FileError> m_error;
     MonotonicTime m_lastProgressNotificationTime { MonotonicTime::nan() };
+    UniqueRef<SuspendableTaskQueue> m_taskQueue;
 };
 
 } // namespace WebCore

Added: branches/safari-607-branch/Source/WebCore/platform/SuspendableTaskQueue.cpp (0 => 276724)


--- branches/safari-607-branch/Source/WebCore/platform/SuspendableTaskQueue.cpp	                        (rev 0)
+++ branches/safari-607-branch/Source/WebCore/platform/SuspendableTaskQueue.cpp	2021-04-28 18:12:18 UTC (rev 276724)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SuspendableTaskQueue.h"
+
+#include "ScriptExecutionContext.h"
+
+namespace WebCore {
+
+SuspendableTaskQueue::SuspendableTaskQueue(ScriptExecutionContext* context)
+    : ActiveDOMObject(context)
+    , m_taskQueue(context)
+    , m_isClosed(!context)
+{
+}
+
+SuspendableTaskQueue::~SuspendableTaskQueue() = default;
+
+void SuspendableTaskQueue::close()
+{
+    m_isClosed = true;
+    cancelAllTasks();
+}
+
+void SuspendableTaskQueue::cancelAllTasks()
+{
+    m_taskQueue.cancelAllTasks();
+    m_pendingTasks.clear();
+}
+
+void SuspendableTaskQueue::enqueueTask(TaskFunction&& task)
+{
+    if (m_isClosed)
+        return;
+
+    m_pendingTasks.append(WTFMove(task));
+
+    if (m_isSuspended)
+        return;
+
+    m_taskQueue.enqueueTask(std::bind(&SuspendableTaskQueue::runOneTask, this));
+}
+
+void SuspendableTaskQueue::runOneTask()
+{
+    ASSERT(!m_pendingTasks.isEmpty());
+    auto task = m_pendingTasks.takeFirst();
+    task();
+}
+
+const char* SuspendableTaskQueue::activeDOMObjectName() const
+{
+    return "SuspendableTaskQueue";
+}
+
+bool SuspendableTaskQueue::canSuspendForDocumentSuspension() const
+{
+    return true;
+}
+
+void SuspendableTaskQueue::stop()
+{
+    close();
+}
+
+void SuspendableTaskQueue::suspend(ReasonForSuspension)
+{
+    m_isSuspended = true;
+    m_taskQueue.cancelAllTasks();
+}
+
+void SuspendableTaskQueue::resume()
+{
+    m_isSuspended = false;
+    for (size_t i = 0; i < m_pendingTasks.size(); ++i)
+        m_taskQueue.enqueueTask(std::bind(&SuspendableTaskQueue::runOneTask, this));
+}
+
+} // namespace WebCore

Added: branches/safari-607-branch/Source/WebCore/platform/SuspendableTaskQueue.h (0 => 276724)


--- branches/safari-607-branch/Source/WebCore/platform/SuspendableTaskQueue.h	                        (rev 0)
+++ branches/safari-607-branch/Source/WebCore/platform/SuspendableTaskQueue.h	2021-04-28 18:12:18 UTC (rev 276724)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ActiveDOMObject.h"
+#include "GenericTaskQueue.h"
+#include <wtf/UniqueRef.h>
+
+namespace WebCore {
+
+class SuspendableTaskQueue : public ActiveDOMObject {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    static UniqueRef<SuspendableTaskQueue> create(ScriptExecutionContext* context)
+    {
+        auto taskQueue = makeUniqueRef<SuspendableTaskQueue>(context);
+        taskQueue->suspendIfNeeded();
+        return taskQueue;
+    }
+
+    ~SuspendableTaskQueue();
+
+    using TaskFunction = WTF::Function<void()>;
+    void enqueueTask(TaskFunction&&);
+    void cancelAllTasks();
+
+    void close();
+    bool isClosed() const { return m_isClosed; }
+
+    bool hasPendingTasks() const { return m_pendingTasks.isEmpty(); }
+
+private:
+    friend UniqueRef<SuspendableTaskQueue> WTF::makeUniqueRef<SuspendableTaskQueue, WebCore::ScriptExecutionContext*&>(WebCore::ScriptExecutionContext*&);
+
+    explicit SuspendableTaskQueue(ScriptExecutionContext*);
+
+    void runOneTask();
+
+    // ActiveDOMObject.
+    const char* activeDOMObjectName() const final;
+    bool canSuspendForDocumentSuspension() const final;
+    void stop() final;
+    void suspend(ReasonForSuspension) final;
+    void resume() final;
+
+    GenericTaskQueue<ScriptExecutionContext> m_taskQueue;
+    Deque<TaskFunction> m_pendingTasks;
+    bool m_isClosed { false };
+    bool m_isSuspended { false };
+};
+
+} // namespace WebCore
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to