WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-203090-20191023133906.patch (text/plain), 22.10 KB, created by
Chris Dumez
on 2019-10-23 13:39:07 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Chris Dumez
Created:
2019-10-23 13:39:07 PDT
Size:
22.10 KB
patch
obsolete
>Subversion Revision: 251494 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 4ba002e11d93df5caea89d85bec9914349b3e079..1bbb70bb5f855cf4f219be5f47dd90ff72b6ccca 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,31 @@ >+2019-10-23 Chris Dumez <cdumez@apple.com> >+ >+ FileSystemDirectoryReader / FileSystemEntry should not prevent entering the back/forward cache >+ https://bugs.webkit.org/show_bug.cgi?id=203090 >+ <rdar://problem/56550805> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ FileSystemDirectoryReader / FileSystemEntry no longer prevent entering the back/forward cache. >+ We now dispatch tasks to the window event loop whenever we need to call a JS callback since the >+ window event loop takes care of suspending tasks while in the back/forward cache. >+ >+ Tests: editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache.html >+ editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache.html >+ >+ * Modules/entriesapi/FileSystemDirectoryEntry.cpp: >+ (WebCore::FileSystemDirectoryEntry::getEntry): >+ * Modules/entriesapi/FileSystemDirectoryReader.cpp: >+ (WebCore::FileSystemDirectoryReader::document const): >+ (WebCore::FileSystemDirectoryReader::readEntries): >+ * Modules/entriesapi/FileSystemDirectoryReader.h: >+ * Modules/entriesapi/FileSystemEntry.cpp: >+ (WebCore::FileSystemEntry::document const): >+ (WebCore::FileSystemEntry::getParent): >+ * Modules/entriesapi/FileSystemEntry.h: >+ * Modules/entriesapi/FileSystemFileEntry.cpp: >+ (WebCore::FileSystemFileEntry::file): >+ > 2019-10-23 Yury Semikhatsky <yurys@chromium.org> > > Web Inspector: notify inspector when provisional page is created, committed and destroyed >diff --git a/Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.cpp b/Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.cpp >index 02130a4a901db88bee512fb4bfe39e0aea0106aa..0aa21da0a04a95f7a2b74086a87e58e25069ae43 100644 >--- a/Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.cpp >+++ b/Source/WebCore/Modules/entriesapi/FileSystemDirectoryEntry.cpp >@@ -33,6 +33,7 @@ > #include "FileSystemEntryCallback.h" > #include "FileSystemFileEntry.h" > #include "ScriptExecutionContext.h" >+#include "WindowEventLoop.h" > > namespace WebCore { > >@@ -51,20 +52,30 @@ void FileSystemDirectoryEntry::getEntry(ScriptExecutionContext& context, const S > if (!successCallback && !errorCallback) > return; > >- filesystem().getEntry(context, *this, path, flags, [pendingActivity = makePendingActivity(*this), matches = WTFMove(matches), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback)](auto&& result) { >+ filesystem().getEntry(context, *this, path, flags, [this, pendingActivity = makePendingActivity(*this), matches = WTFMove(matches), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback)](auto&& result) mutable { >+ auto* document = this->document(); > if (result.hasException()) { >- if (errorCallback) >- errorCallback->handleEvent(DOMException::create(result.releaseException())); >+ if (errorCallback && document) { >+ document->eventLoop().queueTask(TaskSource::Networking, *document, [errorCallback = WTFMove(errorCallback), exception = result.releaseException(), pendingActivity = WTFMove(pendingActivity)]() mutable { >+ errorCallback->handleEvent(DOMException::create(WTFMove(exception))); >+ }); >+ } > return; > } > auto entry = result.releaseReturnValue(); > if (!matches(entry)) { >- if (errorCallback) >- errorCallback->handleEvent(DOMException::create(Exception { TypeMismatchError, "Entry at given path does not match expected type"_s })); >+ if (errorCallback && document) { >+ document->eventLoop().queueTask(TaskSource::Networking, *document, [errorCallback = WTFMove(errorCallback), pendingActivity = WTFMove(pendingActivity)]() mutable { >+ errorCallback->handleEvent(DOMException::create(Exception { TypeMismatchError, "Entry at given path does not match expected type"_s })); >+ }); >+ } > return; > } >- if (successCallback) >- successCallback->handleEvent(WTFMove(entry)); >+ if (successCallback && document) { >+ document->eventLoop().queueTask(TaskSource::Networking, *document, [successCallback = WTFMove(successCallback), entry = WTFMove(entry), pendingActivity = WTFMove(pendingActivity)]() mutable { >+ successCallback->handleEvent(WTFMove(entry)); >+ }); >+ } > }); > } > >diff --git a/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.cpp b/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.cpp >index 63aa0fea90957d53ae64b8527056914e29d5af57..5dddcf5d546429a6b63ee48ffdc9bae573aa6638 100644 >--- a/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.cpp >+++ b/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.cpp >@@ -28,10 +28,12 @@ > > #include "DOMException.h" > #include "DOMFileSystem.h" >+#include "Document.h" > #include "ErrorCallback.h" > #include "FileSystemDirectoryEntry.h" > #include "FileSystemEntriesCallback.h" > #include "ScriptExecutionContext.h" >+#include "WindowEventLoop.h" > #include <wtf/IsoMallocInlines.h> > #include <wtf/MainThread.h> > >@@ -53,10 +55,9 @@ const char* FileSystemDirectoryReader::activeDOMObjectName() const > return "FileSystemDirectoryReader"; > } > >-// FIXME: This should never prevent entering the back/forward cache. >-bool FileSystemDirectoryReader::shouldPreventEnteringBackForwardCache_DEPRECATED() const >+Document* FileSystemDirectoryReader::document() const > { >- return hasPendingActivity(); >+ return downcast<Document>(scriptExecutionContext()); > } > > // https://wicg.github.io/entries-api/#dom-filesystemdirectoryentry-readentries >@@ -83,15 +84,23 @@ void FileSystemDirectoryReader::readEntries(ScriptExecutionContext& context, Ref > auto pendingActivity = makePendingActivity(*this); > callOnMainThread([this, context = makeRef(context), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), pendingActivity = WTFMove(pendingActivity)]() mutable { > m_isReading = false; >- m_directory->filesystem().listDirectory(context, m_directory, [this, successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), pendingActivity = WTFMove(pendingActivity)](ExceptionOr<Vector<Ref<FileSystemEntry>>>&& result) { >+ m_directory->filesystem().listDirectory(context, m_directory, [this, successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), pendingActivity = WTFMove(pendingActivity)](ExceptionOr<Vector<Ref<FileSystemEntry>>>&& result) mutable { >+ auto* document = this->document(); > if (result.hasException()) { > m_error = result.releaseException(); >- if (errorCallback) >- errorCallback->handleEvent(DOMException::create(*m_error)); >+ if (errorCallback && document) { >+ document->eventLoop().queueTask(TaskSource::Networking, *document, [this, errorCallback = WTFMove(errorCallback), pendingActivity = WTFMove(pendingActivity)]() mutable { >+ errorCallback->handleEvent(DOMException::create(*m_error)); >+ }); >+ } > return; > } > m_isDone = true; >- successCallback->handleEvent(result.releaseReturnValue()); >+ if (document) { >+ document->eventLoop().queueTask(TaskSource::Networking, *document, [successCallback = WTFMove(successCallback), pendingActivity = WTFMove(pendingActivity), result = result.releaseReturnValue()]() mutable { >+ successCallback->handleEvent(WTFMove(result)); >+ }); >+ } > }); > }); > } >diff --git a/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.h b/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.h >index b900de7371a41bf201edf9d856173f5d87d18463..1a0998acaeeaa260ea8314f6a03f36b68a984c5b 100644 >--- a/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.h >+++ b/Source/WebCore/Modules/entriesapi/FileSystemDirectoryReader.h >@@ -54,7 +54,7 @@ private: > FileSystemDirectoryReader(ScriptExecutionContext&, FileSystemDirectoryEntry&); > > const char* activeDOMObjectName() const final; >- bool shouldPreventEnteringBackForwardCache_DEPRECATED() const final; >+ Document* document() const; > > Ref<FileSystemDirectoryEntry> m_directory; > Optional<Exception> m_error; >diff --git a/Source/WebCore/Modules/entriesapi/FileSystemEntry.cpp b/Source/WebCore/Modules/entriesapi/FileSystemEntry.cpp >index d2516a37245cfc7d20d089a270ea43fd267c1f64..a86db76fba5137de1c4380b574a03b004218285d 100644 >--- a/Source/WebCore/Modules/entriesapi/FileSystemEntry.cpp >+++ b/Source/WebCore/Modules/entriesapi/FileSystemEntry.cpp >@@ -32,6 +32,7 @@ > #include "FileSystemDirectoryEntry.h" > #include "FileSystemEntryCallback.h" > #include "ScriptExecutionContext.h" >+#include "WindowEventLoop.h" > #include <wtf/FileSystem.h> > #include <wtf/IsoMallocInlines.h> > >@@ -60,10 +61,9 @@ const char* FileSystemEntry::activeDOMObjectName() const > return "FileSystemEntry"; > } > >-// FIXME: This should never prevent entering the back/forward cache. >-bool FileSystemEntry::shouldPreventEnteringBackForwardCache_DEPRECATED() const >+Document* FileSystemEntry::document() const > { >- return hasPendingActivity(); >+ return downcast<Document>(scriptExecutionContext()); > } > > void FileSystemEntry::getParent(ScriptExecutionContext& context, RefPtr<FileSystemEntryCallback>&& successCallback, RefPtr<ErrorCallback>&& errorCallback) >@@ -71,14 +71,20 @@ void FileSystemEntry::getParent(ScriptExecutionContext& context, RefPtr<FileSyst > if (!successCallback && !errorCallback) > return; > >- filesystem().getParent(context, *this, [pendingActivity = makePendingActivity(*this), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback)](auto&& result) { >- if (result.hasException()) { >- if (errorCallback) >- errorCallback->handleEvent(DOMException::create(result.releaseException())); >+ filesystem().getParent(context, *this, [this, pendingActivity = makePendingActivity(*this), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback)](auto&& result) mutable { >+ auto* document = this->document(); >+ if (!document) > return; >- } >- if (successCallback) >- successCallback->handleEvent(result.releaseReturnValue()); >+ >+ document->eventLoop().queueTask(TaskSource::Networking, *document, [successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), result = WTFMove(result), pendingActivity = WTFMove(pendingActivity)]() mutable { >+ if (result.hasException()) { >+ if (errorCallback) >+ errorCallback->handleEvent(DOMException::create(result.releaseException())); >+ return; >+ } >+ if (successCallback) >+ successCallback->handleEvent(result.releaseReturnValue()); >+ }); > }); > } > >diff --git a/Source/WebCore/Modules/entriesapi/FileSystemEntry.h b/Source/WebCore/Modules/entriesapi/FileSystemEntry.h >index d9763effeb0b6f1425406a030768b2a96a30080e..af28ce09de613c0aaa9844dbc15d9c33ee3214b0 100644 >--- a/Source/WebCore/Modules/entriesapi/FileSystemEntry.h >+++ b/Source/WebCore/Modules/entriesapi/FileSystemEntry.h >@@ -52,10 +52,10 @@ public: > > protected: > FileSystemEntry(ScriptExecutionContext&, DOMFileSystem&, const String& virtualPath); >+ Document* document() const; > > private: > const char* activeDOMObjectName() const final; >- bool shouldPreventEnteringBackForwardCache_DEPRECATED() const final; > > Ref<DOMFileSystem> m_filesystem; > String m_name; >diff --git a/Source/WebCore/Modules/entriesapi/FileSystemFileEntry.cpp b/Source/WebCore/Modules/entriesapi/FileSystemFileEntry.cpp >index e8c9ef38e932e4e7a56cd9df448a62c75c971bca..40f4724baaf9d18ae4450ecd558c82e397ed0ad8 100644 >--- a/Source/WebCore/Modules/entriesapi/FileSystemFileEntry.cpp >+++ b/Source/WebCore/Modules/entriesapi/FileSystemFileEntry.cpp >@@ -28,8 +28,10 @@ > > #include "DOMException.h" > #include "DOMFileSystem.h" >+#include "Document.h" > #include "ErrorCallback.h" > #include "FileCallback.h" >+#include "WindowEventLoop.h" > > namespace WebCore { > >@@ -40,13 +42,19 @@ FileSystemFileEntry::FileSystemFileEntry(ScriptExecutionContext& context, DOMFil > > void FileSystemFileEntry::file(Ref<FileCallback>&& successCallback, RefPtr<ErrorCallback>&& errorCallback) > { >- filesystem().getFile(*this, [successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback)](auto&& result) { >- if (result.hasException()) { >- if (errorCallback) >- errorCallback->handleEvent(DOMException::create(result.releaseException())); >+ filesystem().getFile(*this, [this, pendingActivity = makePendingActivity(*this), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback)](auto&& result) mutable { >+ auto* document = this->document(); >+ if (!document) > return; >- } >- successCallback->handleEvent(result.releaseReturnValue()); >+ >+ document->eventLoop().queueTask(TaskSource::Networking, *document, [successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), result = WTFMove(result), pendingActivity = WTFMove(pendingActivity)]() mutable { >+ if (result.hasException()) { >+ if (errorCallback) >+ errorCallback->handleEvent(DOMException::create(result.releaseException())); >+ return; >+ } >+ successCallback->handleEvent(result.releaseReturnValue()); >+ }); > }); > } > >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index de138110e861f76bf8a064bd503b56cda01879e3..064e8cd20e31f6aa9ead935a0f1b71ac90c4b8ee 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,18 @@ >+2019-10-23 Chris Dumez <cdumez@apple.com> >+ >+ FileSystemDirectoryReader / FileSystemEntry should not prevent entering the back/forward cache >+ https://bugs.webkit.org/show_bug.cgi?id=203090 >+ <rdar://problem/56550805> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add layout test coverage. >+ >+ * editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache-expected.txt: Added. >+ * editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache.html: Added. >+ * editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache-expected.txt: Added. >+ * editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache.html: Added. >+ > 2019-10-23 Yury Semikhatsky <yurys@chromium.org> > > Web Inspector: notify inspector when provisional page is created, committed and destroyed >diff --git a/LayoutTests/editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache-expected.txt b/LayoutTests/editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..97af1834219d69c381117eba71887a9da664b5c7 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache-expected.txt >@@ -0,0 +1,15 @@ >+Make sure that fileSystemDirectoryEntry.getFile() does not prevent a page from entering the back/forward cache. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+pageshow - not from cache >+pagehide - entering cache >+pageshow - from cache >+PASS Page did enter and was restored from the page cache >+PASS Success callback was called >+PASS restoredFromPageCache is true >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache.html b/LayoutTests/editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache.html >new file mode 100644 >index 0000000000000000000000000000000000000000..0d6aee82eddede4b8ca84ae7369b1946bf3fe6a4 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/entries-api/DirectoryEntry-getFile-back-forward-cache.html >@@ -0,0 +1,60 @@ >+<!-- webkit-test-runner [ enableBackForwardCache=true ] --> >+<!DOCTYPE html> >+<html> >+<head> >+<script src="../../../resources/js-test.js"></script> >+<script src="../../editing.js"></script> >+</head> >+<body> >+<div id="dropzone" style="width: 200px; height: 200px; background-color: grey;"></div> >+<script> >+description("Make sure that fileSystemDirectoryEntry.getFile() does not prevent a page from entering the back/forward cache."); >+jsTestIsAsync = true; >+restoredFromPageCache = false; >+ >+window.addEventListener("pageshow", function(event) { >+ debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache"); >+ if (event.persisted) { >+ testPassed("Page did enter and was restored from the page cache"); >+ restoredFromPageCache = true; >+ } >+}, false); >+ >+window.addEventListener("pagehide", function(event) { >+ debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache"); >+ if (!event.persisted) { >+ testFailed("Page did not enter the page cache."); >+ finishJSTest(); >+ } else { >+ entry.getFile('/testFiles/subfolder1/file3.txt', {}, function(file) { >+ testPassed("Success callback was called"); >+ shouldBeTrue("restoredFromPageCache"); >+ finishJSTest(); >+ }, function(e) { >+ testFailed("Error callback was called"); >+ finishJSTest(); >+ }); >+ } >+}, false); >+ >+var dropzone = document.getElementById('dropzone'); >+dropzone.ondrop = function(e) { >+ e.preventDefault(); >+ dataTransfer = e.dataTransfer; >+ >+ entry = dataTransfer.items[0].webkitGetAsEntry(); >+ window.location.href = "../../../fast/history/resources/page-cache-helper.html"; >+}; >+ >+dropzone.ondragover = function(ev) { >+ ev.preventDefault(); >+} >+ >+onload = function() { >+ setTimeout(() => { >+ dragFilesOntoElement(dropzone, ['../../../fast/forms/file/entries-api/resources/testFiles']); >+ }, 0); >+} >+</script> >+</body> >+</html> >diff --git a/LayoutTests/editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache-expected.txt b/LayoutTests/editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..49542888f76b607dd566bbfe6f904056ce99a2e6 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache-expected.txt >@@ -0,0 +1,15 @@ >+Basic test coverage for fileSystemDirectoryReader.readEntries() >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+pageshow - not from cache >+pagehide - entering cache >+pageshow - from cache >+PASS Page did enter and was restored from the page cache >+PASS Success callback was called >+PASS restoredFromPageCache is true >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache.html b/LayoutTests/editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache.html >new file mode 100644 >index 0000000000000000000000000000000000000000..1d602799a433d5755eb920fa937575051015d943 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/entries-api/DirectoryReader-readEntries-back-forward-cache.html >@@ -0,0 +1,84 @@ >+<!-- webkit-test-runner [ enableBackForwardCache=true ] --> >+<!DOCTYPE html> >+<html> >+<head> >+<script src="../../../resources/js-test.js"></script> >+<script src="../../editing.js"></script> >+</head> >+<body> >+<div id="dropzone" style="width: 200px; height: 200px; background-color: grey;"></div> >+<script> >+description("Basic test coverage for fileSystemDirectoryReader.readEntries()"); >+jsTestIsAsync = true; >+restoredFromPageCache = false; >+ >+function getEntriesAsPromise(dirEntry) { >+ return new Promise((resolve, reject) => { >+ let result = []; >+ reader = dirEntry.createReader(); >+ let doBatch = () => { >+ reader.readEntries(entries => { >+ if (entries.length > 0) { >+ entries.forEach(e => result.push(e)); >+ doBatch(); >+ } else { >+ resolve(result.sort(function (a, b) { >+ if (a.name > b.name) >+ return 1; >+ else if (a.name < b.name) >+ return -1; >+ return 0; >+ })); >+ } >+ }, reject); >+ }; >+ doBatch(); >+ }); >+} >+ >+window.addEventListener("pageshow", function(event) { >+ debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache"); >+ if (event.persisted) { >+ testPassed("Page did enter and was restored from the page cache"); >+ restoredFromPageCache = true; >+ } >+}, false); >+ >+window.addEventListener("pagehide", function(event) { >+ debug("pagehide - " + (event.persisted ? "" : "not ") + "entering cache"); >+ if (!event.persisted) { >+ testFailed("Page did not enter the page cache."); >+ finishJSTest(); >+ } else { >+ getEntriesAsPromise(entry).then(function(entries) { >+ testPassed("Success callback was called"); >+ shouldBeTrue("restoredFromPageCache"); >+ finishJSTest(); >+ }, function(e) { >+ testFailed("Error callback was called"); >+ finishJSTest(); >+ }); >+ } >+}, false); >+ >+var dropzone = document.getElementById('dropzone'); >+dropzone.ondrop = function(e) { >+ e.preventDefault(); >+ dataTransfer = e.dataTransfer; >+ >+ entry = dataTransfer.items[0].webkitGetAsEntry(); >+ window.location.href = "../../../fast/history/resources/page-cache-helper.html"; >+}; >+ >+dropzone.ondragover = function(ev) { >+ ev.preventDefault(); >+} >+ >+onload = function() { >+ setTimeout(() => { >+ dragFilesOntoElement(dropzone, ['../../../fast/forms/file/entries-api/resources/testFiles']); >+ }, 0); >+} >+</script> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 203090
:
381717
|
381720
| 381723