Diff
Modified: trunk/Source/WebCore/ChangeLog (224754 => 224755)
--- trunk/Source/WebCore/ChangeLog 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/ChangeLog 2017-11-13 15:30:39 UTC (rev 224755)
@@ -1,3 +1,94 @@
+2017-11-13 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [Attachment Support] Implement SPI for clients to request data for a given attachment
+ https://bugs.webkit.org/show_bug.cgi?id=179586
+ <rdar://problem/35355720>
+
+ Reviewed by Darin Adler.
+
+ Adds support in WebCore for fetching data for a given attachment element. See per-method comments below for
+ more detail.
+
+ Test coverage by augmenting existing API tests in WKAttachmentTests, and adding 3 new tests.
+
+ * dom/Document.cpp:
+ (WebCore::Document::didInsertAttachmentElement):
+ (WebCore::Document::didRemoveAttachmentElement):
+ (WebCore::Document::attachmentForIdentifier const):
+
+ Fetches an attachment element matching the given identifier. Using the identifier => attachment element map here
+ allows us to avoid the cost of walking the DOM in search for HTMLAttachmentElements every time data is
+ requested.
+
+ * dom/Document.h:
+
+ Add a map of attachment identifier => HTMLAttachmentElement in Document. This map is updated when attachment
+ elements are connected to or disconnected from the document. Additionally, delegate attachment insertion and
+ removal out to the Editor if possible.
+
+ * editing/Editor.cpp:
+ (WebCore::Editor::insertAttachmentFromFile):
+ * editing/mac/WebContentReaderMac.mm:
+ (WebCore::WebContentReader::readFilenames):
+ * editing/markup.cpp:
+ (WebCore::createFragmentFromMarkup):
+
+ Tweak existing logic that transfers file-backed File objects when deserializing HTMLAttachmentElements from
+ markup to handle the case where the attachment element is not file-backed. In this case, we construct a new
+ File object using the deserializing constructor. To do this, we use the attachment element's blob URL to make
+ sure that the new File references an existing blob matching that URL.
+
+ * html/HTMLAttachmentElement.cpp:
+ (WebCore::AttachmentDataReader::create):
+ (WebCore::AttachmentDataReader::AttachmentDataReader):
+
+ Introduce AttachmentDataReader, a helper class local to HTMLAttachmentElement that is responsible for loading
+ an attachment element's file and invoking a given callback when loading has succeeded or failed.
+ Each AttachmentDataReader is retained exclusively by its HTMLAttachmentElement, through the
+ HTMLAttachmentElement's vector of unique_ptrs to AttachmentDataReaders.
+
+ (WebCore::HTMLAttachmentElement::~HTMLAttachmentElement):
+ (WebCore::HTMLAttachmentElement::blobURL const):
+
+ Add a convenience getter for the blob URL of the attachment's backing File object.
+
+ (WebCore::HTMLAttachmentElement::setFile):
+
+ When setting an attachment element's file, set the blob URL attribute as well to the blob URL. Also, tweak this
+ to take a RefPtr<File>&& instead of a raw File*.
+
+ (WebCore::HTMLAttachmentElement::insertedIntoAncestor):
+ (WebCore::HTMLAttachmentElement::removedFromAncestor):
+
+ Instead of delegating attachment insertion and removal to the Editor, just call out to the Document, which will
+ now call out to the Editor.
+
+ (WebCore::HTMLAttachmentElement::attachmentPath const):
+
+ Add a convenience getter for the attachment path attribute.
+
+ (WebCore::HTMLAttachmentElement::requestData):
+ (WebCore::HTMLAttachmentElement::destroyReader):
+
+ Called when a AttachmentDataReader has completed (either with success or failure), and is ready to be removed
+ from the attachment element's list of active data readers.
+
+ (WebCore::AttachmentDataReader::~AttachmentDataReader):
+ (WebCore::AttachmentDataReader::didFinishLoading):
+ (WebCore::AttachmentDataReader::didFail):
+ (WebCore::AttachmentDataReader::invokeCallbackAndFinishReading):
+
+ When the reader is done loading, or has failed, or is about to be destroyed, fire the callback with loaded data
+ (if any) and cancel the FileReaderLoader.
+
+ * html/HTMLAttachmentElement.h:
+ * html/HTMLAttributeNames.in:
+
+ Adds a new "webkitattachmentbloburl" attribute that keeps track of an attachment element's file's blob URL. This
+ is used to ensure that information about an attachment element's file is not lost between certain editing
+ operations (for instance, deleting a line break) that involve serializing and then deserializing markup into
+ DocumentFragments to then insert.
+
2017-11-13 Zan Dobersek <zdober...@igalia.com>
[Cairo] Remove GraphicsContext::mustUseShadowBlur()
Modified: trunk/Source/WebCore/dom/Document.cpp (224754 => 224755)
--- trunk/Source/WebCore/dom/Document.cpp 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/dom/Document.cpp 2017-11-13 15:30:39 UTC (rev 224755)
@@ -74,6 +74,7 @@
#include "GenericCachedHTMLCollection.h"
#include "HTMLAllCollection.h"
#include "HTMLAnchorElement.h"
+#include "HTMLAttachmentElement.h"
#include "HTMLBaseElement.h"
#include "HTMLBodyElement.h"
#include "HTMLCanvasElement.h"
@@ -7462,6 +7463,39 @@
return *m_timeline;
}
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+void Document::didInsertAttachmentElement(HTMLAttachmentElement& attachment)
+{
+ auto identifier = attachment.uniqueIdentifier();
+ if (!identifier)
+ return;
+
+ m_attachmentIdentifierToElementMap.set(identifier, attachment);
+
+ if (frame())
+ frame()->editor().didInsertAttachmentElement(attachment);
+}
+
+void Document::didRemoveAttachmentElement(HTMLAttachmentElement& attachment)
+{
+ auto identifier = attachment.uniqueIdentifier();
+ if (!identifier)
+ return;
+
+ m_attachmentIdentifierToElementMap.remove(identifier);
+
+ if (frame())
+ frame()->editor().didRemoveAttachmentElement(attachment);
+}
+
+RefPtr<HTMLAttachmentElement> Document::attachmentForIdentifier(const String& identifier) const
+{
+ return m_attachmentIdentifierToElementMap.get(identifier);
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
static MessageSource messageSourceForWTFLogChannel(const WTFLogChannel& channel)
{
static const NeverDestroyed<String> mediaChannel = MAKE_STATIC_STRING_IMPL("media");
Modified: trunk/Source/WebCore/dom/Document.h (224754 => 224755)
--- trunk/Source/WebCore/dom/Document.h 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/dom/Document.h 2017-11-13 15:30:39 UTC (rev 224755)
@@ -216,6 +216,10 @@
class MediaSession;
#endif
+#if ENABLE(ATTACHMENT_ELEMENT)
+class HTMLAttachmentElement;
+#endif
+
namespace Style {
class Scope;
};
@@ -1373,6 +1377,12 @@
DocumentTimeline& timeline();
+#if ENABLE(ATTACHMENT_ELEMENT)
+ void didInsertAttachmentElement(HTMLAttachmentElement&);
+ void didRemoveAttachmentElement(HTMLAttachmentElement&);
+ WEBCORE_EXPORT RefPtr<HTMLAttachmentElement> attachmentForIdentifier(const String& identifier) const;
+#endif
+
protected:
enum ConstructionFlags { Synthesized = 1, NonRenderedPlaceholder = 1 << 1 };
Document(Frame*, const URL&, unsigned = DefaultDocumentClass, unsigned constructionFlags = 0);
@@ -1721,6 +1731,10 @@
RefPtr<IDBClient::IDBConnectionProxy> m_idbConnectionProxy;
#endif
+#if ENABLE(ATTACHMENT_ELEMENT)
+ HashMap<String, Ref<HTMLAttachmentElement>> m_attachmentIdentifierToElementMap;
+#endif
+
Timer m_didAssociateFormControlsTimer;
Timer m_cookieCacheExpiryTimer;
Modified: trunk/Source/WebCore/editing/Editor.cpp (224754 => 224755)
--- trunk/Source/WebCore/editing/Editor.cpp 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/editing/Editor.cpp 2017-11-13 15:30:39 UTC (rev 224755)
@@ -3802,7 +3802,7 @@
attachment->setAttribute(HTMLNames::subtitleAttr, fileSizeDescription(file->size()));
attachment->setAttribute(HTMLNames::typeAttr, contentType);
attachment->setUniqueIdentifier(identifier);
- attachment->setFile(file.ptr());
+ attachment->setFile(WTFMove(file));
auto fragmentToInsert = document().createDocumentFragment();
fragmentToInsert->appendChild(attachment.get());
Modified: trunk/Source/WebCore/editing/mac/WebContentReaderMac.mm (224754 => 224755)
--- trunk/Source/WebCore/editing/mac/WebContentReaderMac.mm 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/editing/mac/WebContentReaderMac.mm 2017-11-13 15:30:39 UTC (rev 224755)
@@ -65,7 +65,7 @@
#if ENABLE(ATTACHMENT_ELEMENT)
if (RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled()) {
auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
- attachment->setFile(File::create([[NSURL fileURLWithPath:text] path]).ptr());
+ attachment->setFile(File::create([[NSURL fileURLWithPath:text] path]));
fragment->appendChild(attachment);
continue;
}
Modified: trunk/Source/WebCore/editing/markup.cpp (224754 => 224755)
--- trunk/Source/WebCore/editing/markup.cpp 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/editing/markup.cpp 2017-11-13 15:30:39 UTC (rev 224755)
@@ -763,7 +763,11 @@
attachments.append(attachment);
for (auto& attachment : attachments) {
- attachment->setFile(File::create(attachment->attributeWithoutSynchronization(webkitattachmentpathAttr)).ptr());
+ auto attachmentPath = attachment->attachmentPath();
+ if (attachmentPath.isEmpty())
+ attachment->setFile(File::deserialize({ }, attachment->blobURL(), attachment->attachmentType(), attachment->attachmentTitle()));
+ else
+ attachment->setFile(File::create(attachmentPath));
attachment->removeAttribute(webkitattachmentpathAttr);
}
#endif
Modified: trunk/Source/WebCore/html/HTMLAttachmentElement.cpp (224754 => 224755)
--- trunk/Source/WebCore/html/HTMLAttachmentElement.cpp 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/html/HTMLAttachmentElement.cpp 2017-11-13 15:30:39 UTC (rev 224755)
@@ -30,14 +30,47 @@
#include "Editor.h"
#include "File.h"
+#include "FileReaderLoader.h"
+#include "FileReaderLoaderClient.h"
#include "Frame.h"
#include "HTMLNames.h"
#include "RenderAttachment.h"
+#include "SharedBuffer.h"
namespace WebCore {
using namespace HTMLNames;
+class AttachmentDataReader : public FileReaderLoaderClient {
+public:
+ static std::unique_ptr<AttachmentDataReader> create(HTMLAttachmentElement& attachment, Function<void(RefPtr<SharedBuffer>&&)>&& callback)
+ {
+ return std::make_unique<AttachmentDataReader>(attachment, WTFMove(callback));
+ }
+
+ AttachmentDataReader(HTMLAttachmentElement& attachment, Function<void(RefPtr<SharedBuffer>&&)>&& callback)
+ : m_attachment(attachment)
+ , m_callback(std::make_unique<Function<void(RefPtr<SharedBuffer>&&)>>(WTFMove(callback)))
+ , m_loader(std::make_unique<FileReaderLoader>(FileReaderLoader::ReadType::ReadAsArrayBuffer, this))
+ {
+ m_loader->start(&attachment.document(), *attachment.file());
+ }
+
+ ~AttachmentDataReader();
+
+private:
+ void didStartLoading() final { }
+ void didReceiveData() final { }
+ void didFinishLoading() final;
+ void didFail(int error) final;
+
+ void invokeCallbackAndFinishReading(RefPtr<SharedBuffer>&&);
+
+ HTMLAttachmentElement& m_attachment;
+ std::unique_ptr<Function<void(RefPtr<SharedBuffer>&&)>> m_callback;
+ std::unique_ptr<FileReaderLoader> m_loader;
+};
+
HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document)
: HTMLElement(tagName, document)
{
@@ -61,10 +94,17 @@
return m_file.get();
}
-void HTMLAttachmentElement::setFile(File* file)
+URL HTMLAttachmentElement::blobURL() const
{
- m_file = file;
+ return { { }, attributeWithoutSynchronization(HTMLNames::webkitattachmentbloburlAttr).string() };
+}
+void HTMLAttachmentElement::setFile(RefPtr<File>&& file)
+{
+ m_file = WTFMove(file);
+
+ setAttributeWithoutSynchronization(HTMLNames::webkitattachmentbloburlAttr, m_file ? m_file->url() : emptyString());
+
if (auto* renderer = this->renderer())
renderer->invalidate();
}
@@ -72,10 +112,8 @@
Node::InsertedIntoAncestorResult HTMLAttachmentElement::insertedIntoAncestor(InsertionType type, ContainerNode& ancestor)
{
auto result = HTMLElement::insertedIntoAncestor(type, ancestor);
- if (auto* frame = document().frame()) {
- if (type.connectedToDocument)
- frame->editor().didInsertAttachmentElement(*this);
- }
+ if (type.connectedToDocument)
+ document().didInsertAttachmentElement(*this);
return result;
}
@@ -82,10 +120,8 @@
void HTMLAttachmentElement::removedFromAncestor(RemovalType type, ContainerNode& ancestor)
{
HTMLElement::removedFromAncestor(type, ancestor);
- if (auto* frame = document().frame()) {
- if (type.disconnectedFromDocument)
- frame->editor().didRemoveAttachmentElement(*this);
- }
+ if (type.disconnectedFromDocument)
+ document().didRemoveAttachmentElement(*this);
}
void HTMLAttachmentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
@@ -121,6 +157,57 @@
return attributeWithoutSynchronization(typeAttr);
}
+String HTMLAttachmentElement::attachmentPath() const
+{
+ return attributeWithoutSynchronization(webkitattachmentpathAttr);
+}
+
+void HTMLAttachmentElement::requestData(Function<void(RefPtr<SharedBuffer>&&)>&& callback)
+{
+ if (m_file)
+ m_attachmentReaders.append(AttachmentDataReader::create(*this, WTFMove(callback)));
+ else
+ callback(nullptr);
+}
+
+void HTMLAttachmentElement::destroyReader(AttachmentDataReader& finishedReader)
+{
+ m_attachmentReaders.removeFirstMatching([&] (const std::unique_ptr<AttachmentDataReader>& reader) -> bool {
+ return reader.get() == &finishedReader;
+ });
+}
+
+AttachmentDataReader::~AttachmentDataReader()
+{
+ invokeCallbackAndFinishReading(nullptr);
+}
+
+void AttachmentDataReader::didFinishLoading()
+{
+ if (auto arrayBuffer = m_loader->arrayBufferResult())
+ invokeCallbackAndFinishReading(SharedBuffer::create(reinterpret_cast<uint8_t*>(arrayBuffer->data()), arrayBuffer->byteLength()));
+ else
+ invokeCallbackAndFinishReading(nullptr);
+ m_attachment.destroyReader(*this);
+}
+
+void AttachmentDataReader::didFail(int)
+{
+ invokeCallbackAndFinishReading(nullptr);
+ m_attachment.destroyReader(*this);
+}
+
+void AttachmentDataReader::invokeCallbackAndFinishReading(RefPtr<SharedBuffer>&& data)
+{
+ auto callback = WTFMove(m_callback);
+ if (!callback)
+ return;
+
+ m_loader->cancel();
+ m_loader = nullptr;
+ (*callback)(WTFMove(data));
+}
+
} // namespace WebCore
#endif // ENABLE(ATTACHMENT_ELEMENT)
Modified: trunk/Source/WebCore/html/HTMLAttachmentElement.h (224754 => 224755)
--- trunk/Source/WebCore/html/HTMLAttachmentElement.h 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/html/HTMLAttachmentElement.h 2017-11-13 15:30:39 UTC (rev 224755)
@@ -31,15 +31,18 @@
namespace WebCore {
+class AttachmentDataReader;
class File;
class RenderAttachment;
+class SharedBuffer;
class HTMLAttachmentElement final : public HTMLElement {
public:
static Ref<HTMLAttachmentElement> create(const QualifiedName&, Document&);
+ WEBCORE_EXPORT URL blobURL() const;
WEBCORE_EXPORT File* file() const;
- void setFile(File*);
+ void setFile(RefPtr<File>&&);
WEBCORE_EXPORT String uniqueIdentifier() const;
void setUniqueIdentifier(const String&);
@@ -49,9 +52,13 @@
WEBCORE_EXPORT String attachmentTitle() const;
String attachmentType() const;
+ String attachmentPath() const;
RenderAttachment* renderer() const;
+ WEBCORE_EXPORT void requestData(Function<void(RefPtr<SharedBuffer>&&)>&& callback);
+ void destroyReader(AttachmentDataReader&);
+
private:
HTMLAttachmentElement(const QualifiedName&, Document&);
virtual ~HTMLAttachmentElement();
@@ -69,6 +76,7 @@
void parseAttribute(const QualifiedName&, const AtomicString&) final;
RefPtr<File> m_file;
+ Vector<std::unique_ptr<AttachmentDataReader>> m_attachmentReaders;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/html/HTMLAttributeNames.in (224754 => 224755)
--- trunk/Source/WebCore/html/HTMLAttributeNames.in 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebCore/html/HTMLAttributeNames.in 2017-11-13 15:30:39 UTC (rev 224755)
@@ -383,6 +383,7 @@
webkitallowfullscreen
webkitattachmentid
webkitattachmentpath
+webkitattachmentbloburl
webkitdirectory
width
wrap
Modified: trunk/Source/WebKit/ChangeLog (224754 => 224755)
--- trunk/Source/WebKit/ChangeLog 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/ChangeLog 2017-11-13 15:30:39 UTC (rev 224755)
@@ -1,3 +1,42 @@
+2017-11-13 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [Attachment Support] Implement SPI for clients to request data for a given attachment
+ https://bugs.webkit.org/show_bug.cgi?id=179586
+ <rdar://problem/35355720>
+
+ Reviewed by Darin Adler.
+
+ Adds support in WebKit for fetching data for a given attachment element. See WebCore/ChangeLog for more details.
+ Most of the changes here are boilerplate plumbing of -requestAttachmentData through the client layers.
+
+ Test coverage by augmenting existing API tests in WKAttachmentTests, and adding 3 new tests.
+
+ * UIProcess/API/APIAttachment.cpp:
+ (API::Attachment::requestData):
+ * UIProcess/API/APIAttachment.h:
+ * UIProcess/API/Cocoa/_WKAttachment.h:
+ * UIProcess/API/Cocoa/_WKAttachment.mm:
+ (-[_WKAttachment requestData:]):
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::sharedBufferCallback):
+
+ Add a new IPC callback helper type, SharedBufferCallback. This is similar to the existing DataCallback, but
+ instead of deserializing to an API::Data, we convert to a SharedBuffer instead. Additionally,
+ SharedBufferCallback is able to draw a distinction between null data and empty data. This allows -requestData:
+ to distinguish between cases where (for instance) the data for a given attachment is an empty blob, and when
+ the attachment doesn't exist at all.
+
+ (WebKit::WebPageProxy::dataCallback):
+ (WebKit::WebPageProxy::insertAttachment):
+ (WebKit::WebPage::invokeSharedBufferCallback):
+ (WebKit::WebPageProxy::requestAttachmentData):
+ * UIProcess/WebPageProxy.h:
+ * UIProcess/WebPageProxy.messages.in:
+ * WebProcess/WebPage/WebPage.cpp:
+ (WebKit::WebPage::requestAttachmentData):
+ * WebProcess/WebPage/WebPage.h:
+ * WebProcess/WebPage/WebPage.messages.in:
+
2017-11-12 Darin Adler <da...@apple.com>
More is<> and downcast<>, less static_cast<>
Modified: trunk/Source/WebKit/UIProcess/API/APIAttachment.cpp (224754 => 224755)
--- trunk/Source/WebKit/UIProcess/API/APIAttachment.cpp 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/UIProcess/API/APIAttachment.cpp 2017-11-13 15:30:39 UTC (rev 224755)
@@ -26,6 +26,8 @@
#include "config.h"
#include "APIAttachment.h"
+#include <WebCore/SharedBuffer.h>
+#include <wtf/BlockPtr.h>
#include <wtf/text/WTFString.h>
namespace API {
@@ -45,4 +47,12 @@
{
}
+void Attachment::requestData(Function<void(RefPtr<WebCore::SharedBuffer>, WebKit::CallbackBase::Error)>&& callback)
+{
+ if (m_webPage)
+ m_webPage->requestAttachmentData(m_identifier, WTFMove(callback));
+ else
+ callback(nullptr, WebKit::CallbackBase::Error::OwnerWasInvalidated);
}
+
+}
Modified: trunk/Source/WebKit/UIProcess/API/APIAttachment.h (224754 => 224755)
--- trunk/Source/WebKit/UIProcess/API/APIAttachment.h 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/UIProcess/API/APIAttachment.h 2017-11-13 15:30:39 UTC (rev 224755)
@@ -28,9 +28,14 @@
#include "APIObject.h"
#include <WebKit/WKBase.h>
#include <WebKit/WebPageProxy.h>
+#include <wtf/RefPtr.h>
#include <wtf/WeakPtr.h>
#include <wtf/text/WTFString.h>
+namespace WebCore {
+class SharedBuffer;
+}
+
namespace WebKit {
class WebPageProxy;
}
@@ -43,6 +48,7 @@
virtual ~Attachment();
const WTF::String& identifier() const { return m_identifier; }
+ void requestData(Function<void(RefPtr<WebCore::SharedBuffer>, WebKit::CallbackBase::Error)>&&);
private:
explicit Attachment(const WTF::String& identifier, WebKit::WebPageProxy&);
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.h (224754 => 224755)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.h 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.h 2017-11-13 15:30:39 UTC (rev 224755)
@@ -43,6 +43,7 @@
WK_CLASS_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA))
@interface _WKAttachment : NSObject
+- (void)requestData:(void(^)(NSData *, NSError *))completionHandler;
@end
#endif
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.mm (224754 => 224755)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.mm 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKAttachment.mm 2017-11-13 15:30:39 UTC (rev 224755)
@@ -29,7 +29,10 @@
#if WK_API_ENABLED
#import "APIAttachment.h"
+#import "WKErrorPrivate.h"
#import "_WKAttachmentInternal.h"
+#import <WebCore/SharedBuffer.h>
+#import <wtf/BlockPtr.h>
using namespace WebKit;
@@ -58,6 +61,19 @@
return [object isKindOfClass:[_WKAttachment class]] && [self.uniqueIdentifier isEqual:[(_WKAttachment *)object uniqueIdentifier]];
}
+- (void)requestData:(void(^)(NSData *, NSError *))completionHandler
+{
+ _attachment->requestData([ capturedBlock = makeBlockPtr(completionHandler) ] (RefPtr<WebCore::SharedBuffer> buffer, CallbackBase::Error error) {
+ if (!capturedBlock)
+ return;
+
+ if (buffer && error == CallbackBase::Error::None)
+ capturedBlock(buffer->createNSData().autorelease(), nil);
+ else
+ capturedBlock(nil, [NSError errorWithDomain:WKErrorDomain code:1 userInfo:nil]);
+ });
+}
+
- (NSString *)uniqueIdentifier
{
return _attachment->identifier();
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (224754 => 224755)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2017-11-13 15:30:39 UTC (rev 224755)
@@ -129,6 +129,7 @@
#include <WebCore/PublicSuffix.h>
#include <WebCore/RenderEmbeddedObject.h>
#include <WebCore/SerializedCryptoKeyWrap.h>
+#include <WebCore/SharedBuffer.h>
#include <WebCore/TextCheckerClient.h>
#include <WebCore/TextIndicator.h>
#include <WebCore/URL.h>
@@ -5161,14 +5162,29 @@
callback->performCallback();
}
-void WebPageProxy::dataCallback(const IPC::DataReference& dataReference, CallbackID callbackID)
+void WebPageProxy::sharedBufferCallback(const IPC::DataReference& dataReference, bool isNull, CallbackID callbackID)
{
- auto callback = m_callbacks.take<DataCallback>(callbackID);
+ auto callback = m_callbacks.take<SharedBufferCallback>(callbackID);
if (!callback) {
- // FIXME: Log error or assert.
+ ASSERT_NOT_REACHED();
return;
}
+ if (isNull) {
+ ASSERT(dataReference.isEmpty());
+ callback->performCallbackWithReturnValue(nullptr);
+ return;
+ }
+
+ callback->performCallbackWithReturnValue(SharedBuffer::create(dataReference.data(), dataReference.size()));
+}
+
+void WebPageProxy::dataCallback(const IPC::DataReference& dataReference, CallbackID callbackID)
+{
+ auto callback = m_callbacks.take<DataCallback>(callbackID);
+ if (!callback)
+ return;
+
callback->performCallbackWithReturnValue(API::Data::create(dataReference.data(), dataReference.size()).ptr());
}
@@ -7115,10 +7131,26 @@
void WebPageProxy::insertAttachment(const String& identifier, const String& filename, std::optional<String> contentType, SharedBuffer& data, Function<void(CallbackBase::Error)>&& callback)
{
+ if (!isValid()) {
+ callback(CallbackBase::Error::OwnerWasInvalidated);
+ return;
+ }
+
auto callbackID = m_callbacks.put(WTFMove(callback), m_process->throttler().backgroundActivityToken());
m_process->send(Messages::WebPage::InsertAttachment(identifier, filename, contentType, IPC::SharedBufferDataReference { &data }, callbackID), m_pageID);
}
+void WebPageProxy::requestAttachmentData(const String& identifier, Function<void(RefPtr<SharedBuffer>, CallbackBase::Error)>&& callback)
+{
+ if (!isValid()) {
+ callback(nullptr, CallbackBase::Error::OwnerWasInvalidated);
+ return;
+ }
+
+ auto callbackID = m_callbacks.put(WTFMove(callback), m_process->throttler().backgroundActivityToken());
+ m_process->send(Messages::WebPage::RequestAttachmentData(identifier, callbackID), m_pageID);
+}
+
void WebPageProxy::didInsertAttachment(const String& identifier)
{
m_pageClient.didInsertAttachment(identifier);
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (224754 => 224755)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.h 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h 2017-11-13 15:30:39 UTC (rev 224755)
@@ -236,6 +236,7 @@
typedef GenericCallback<EditingRange> EditingRangeCallback;
typedef GenericCallback<const String&> StringCallback;
typedef GenericCallback<API::SerializedScriptValue*, bool, const WebCore::ExceptionDetails&> ScriptValueCallback;
+typedef GenericCallback<RefPtr<WebCore::SharedBuffer>> SharedBufferCallback;
#if PLATFORM(GTK)
typedef GenericCallback<API::Error*> PrintFinishedCallback;
@@ -1213,6 +1214,7 @@
#if ENABLE(ATTACHMENT_ELEMENT)
void insertAttachment(const String& identifier, const String& filename, std::optional<String> contentType, WebCore::SharedBuffer& data, Function<void(CallbackBase::Error)>&&);
+ void requestAttachmentData(const String& identifier, Function<void(RefPtr<WebCore::SharedBuffer>, CallbackBase::Error)>&&);
#endif
private:
@@ -1466,6 +1468,7 @@
void voidCallback(CallbackID);
void dataCallback(const IPC::DataReference&, CallbackID);
+ void sharedBufferCallback(const IPC::DataReference&, bool isNull, CallbackID);
void imageCallback(const ShareableBitmap::Handle&, CallbackID);
void stringCallback(const String&, CallbackID);
void invalidateStringCallback(CallbackID);
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in (224754 => 224755)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in 2017-11-13 15:30:39 UTC (rev 224755)
@@ -157,6 +157,7 @@
# Callback messages
VoidCallback(WebKit::CallbackID callbackID)
DataCallback(IPC::DataReference resultData, WebKit::CallbackID callbackID)
+ SharedBufferCallback(IPC::DataReference resultData, bool isNull, WebKit::CallbackID callbackID);
ImageCallback(WebKit::ShareableBitmap::Handle bitmapHandle, WebKit::CallbackID callbackID)
StringCallback(String resultString, WebKit::CallbackID callbackID)
InvalidateStringCallback(WebKit::CallbackID callbackID)
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (224754 => 224755)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2017-11-13 15:30:39 UTC (rev 224755)
@@ -141,11 +141,13 @@
#include <WebCore/ElementIterator.h>
#include <WebCore/EventHandler.h>
#include <WebCore/EventNames.h>
+#include <WebCore/File.h>
#include <WebCore/FocusController.h>
#include <WebCore/FormState.h>
#include <WebCore/FrameLoadRequest.h>
#include <WebCore/FrameLoaderTypes.h>
#include <WebCore/FrameView.h>
+#include <WebCore/HTMLAttachmentElement.h>
#include <WebCore/HTMLFormElement.h>
#include <WebCore/HTMLImageElement.h>
#include <WebCore/HTMLInputElement.h>
@@ -5765,6 +5767,14 @@
callback(wasGranted);
}
+void WebPage::invokeSharedBufferCallback(RefPtr<SharedBuffer>&& buffer, CallbackID callbackID)
+{
+ if (buffer)
+ send(Messages::WebPageProxy::SharedBufferCallback(IPC::SharedBufferDataReference { buffer.get() }, false, callbackID));
+ else
+ send(Messages::WebPageProxy::SharedBufferCallback({ }, true, callbackID));
+}
+
#if ENABLE(ATTACHMENT_ELEMENT)
void WebPage::insertAttachment(const String& identifier, const String& filename, std::optional<String> contentType, const IPC::DataReference& data, CallbackID callbackID)
@@ -5774,6 +5784,29 @@
send(Messages::WebPageProxy::VoidCallback(callbackID));
}
+void WebPage::requestAttachmentData(const String& identifier, CallbackID callbackID)
+{
+ // FIXME: We don't currently handle attachment data requests for attachment elements in subframes.
+ auto* frame = mainFrame();
+ if (!frame || !frame->document()) {
+ invokeSharedBufferCallback({ }, callbackID);
+ return;
+ }
+
+ auto attachment = frame->document()->attachmentForIdentifier(identifier);
+ if (!attachment) {
+ invokeSharedBufferCallback({ }, callbackID);
+ return;
+ }
+
+ attachment->requestData([callbackID, protectedThis = makeRef(*this), protectedAttachment = WTFMove(attachment)] (RefPtr<SharedBuffer>&& buffer) {
+ if (buffer)
+ protectedThis->invokeSharedBufferCallback(WTFMove(buffer), callbackID);
+ else
+ protectedThis->invokeSharedBufferCallback({ }, callbackID);
+ });
+}
+
#endif // ENABLE(ATTACHMENT_ELEMENT)
} // namespace WebKit
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (224754 => 224755)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2017-11-13 15:30:39 UTC (rev 224755)
@@ -1001,6 +1001,7 @@
#if ENABLE(ATTACHMENT_ELEMENT)
void insertAttachment(const String& identifier, const String& filename, std::optional<String> contentType, const IPC::DataReference&, CallbackID);
+ void requestAttachmentData(const String& identifier, CallbackID);
#endif
private:
@@ -1106,6 +1107,8 @@
void restoreSession(const Vector<BackForwardListItemState>&);
void didRemoveBackForwardItem(uint64_t);
+ void invokeSharedBufferCallback(RefPtr<WebCore::SharedBuffer>&&, CallbackID);
+
#if ENABLE(REMOTE_INSPECTOR)
void setAllowsRemoteInspection(bool);
void setRemoteInspectionNameOverride(const String&);
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (224754 => 224755)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in 2017-11-13 15:30:39 UTC (rev 224755)
@@ -487,5 +487,6 @@
#if ENABLE(ATTACHMENT_ELEMENT)
InsertAttachment(String identifier, String filename, std::optional<String> contentType, IPC::DataReference data, WebKit::CallbackID callbackID)
+ RequestAttachmentData(String identifier, WebKit::CallbackID callbackID)
#endif
}
Modified: trunk/Tools/ChangeLog (224754 => 224755)
--- trunk/Tools/ChangeLog 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Tools/ChangeLog 2017-11-13 15:30:39 UTC (rev 224755)
@@ -1,3 +1,23 @@
+2017-11-13 Wenson Hsieh <wenson_hs...@apple.com>
+
+ [Attachment Support] Implement SPI for clients to request data for a given attachment
+ https://bugs.webkit.org/show_bug.cgi?id=179586
+ <rdar://problem/35355720>
+
+ Reviewed by Darin Adler.
+
+ Augments existing API tests in WKAttachmentTests to additionally check that -requestData: yields the correct
+ result when performing various editing operations. Also adds a new API test that cuts and pastes an attachment
+ inserted using WKWebView attachment SPI, and expects that the data of the attachment can still be fetched using
+ the _WKAttachment SPI, as well as another test that inserts an empty NSData and expects that requestData: also
+ yields an empty NSData result.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+ (-[NSData shortDescription]):
+ (-[_WKAttachment synchronouslyRequestData:]):
+ (-[_WKAttachment expectRequestedDataToBe:]):
+ (TestWebKitAPI::TEST):
+
2017-11-12 Gabriel Ivascu <giva...@igalia.com>
[GTK] Add functionality to handle font sizes in points
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm (224754 => 224755)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm 2017-11-13 14:50:30 UTC (rev 224754)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm 2017-11-13 15:30:39 UTC (rev 224755)
@@ -177,6 +177,50 @@
@end
+@implementation NSData (AttachmentTesting)
+
+- (NSString *)shortDescription
+{
+ return [NSString stringWithFormat:@"<%tu bytes>", self.length];
+}
+
+@end
+
+@implementation _WKAttachment (AttachmentTesting)
+
+- (NSData *)synchronouslyRequestData:(NSError **)error
+{
+ __block RetainPtr<NSData> result;
+ __block RetainPtr<NSError> resultError;
+ __block bool done = false;
+ [self requestData:^(NSData *data, NSError *error) {
+ result = retainPtr(data);
+ resultError = retainPtr(error);
+ done = true;
+ }];
+
+ TestWebKitAPI::Util::run(&done);
+
+ if (error)
+ *error = resultError.autorelease();
+
+ return result.autorelease();
+}
+
+- (void)expectRequestedDataToBe:(NSData *)expectedData
+{
+ NSError *dataRequestError = nil;
+ NSData *observedData = [self synchronouslyRequestData:&dataRequestError];
+ BOOL observedDataIsEqualToExpectedData = [observedData isEqualToData:expectedData] || observedData == expectedData;
+ EXPECT_TRUE(observedDataIsEqualToExpectedData);
+ if (!observedDataIsEqualToExpectedData) {
+ NSLog(@"Expected data: %@ but observed: %@ for %@", [expectedData shortDescription], [observedData shortDescription], self);
+ NSLog(@"Observed error: %@ while reading data for %@", dataRequestError, self);
+ }
+}
+
+@end
+
namespace TestWebKitAPI {
TEST(WKAttachmentTests, AttachmentElementInsertion)
@@ -193,6 +237,9 @@
EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
}
+
+ [firstAttachment expectRequestedDataToBe:testHTMLData()];
+
{
ObserveAttachmentUpdatesForScope scope(webView.get());
// Since no content type is explicitly specified, compute it from the file extension.
@@ -203,6 +250,9 @@
EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
scope.expectAttachmentUpdates(@[ firstAttachment.get() ], @[ secondAttachment.get() ]);
}
+
+ [firstAttachment expectRequestedDataToBe:nil];
+ [secondAttachment expectRequestedDataToBe:testImageData()];
}
TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingAndDeletingNewline)
@@ -218,12 +268,18 @@
[webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
[webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body)"];
[webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+
+ [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:testHTMLData()];
+
[webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:nil];
}
TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing)
{
auto webView = webViewForTestingAttachments();
+ RetainPtr<NSData> htmlData = retainPtr(testHTMLData());
RetainPtr<_WKAttachment> attachment;
{
ObserveAttachmentUpdatesForScope observer(webView.get());
@@ -231,10 +287,19 @@
observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
}
[webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:nil];
+
[webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
+ [attachment expectRequestedDataToBe:htmlData.get()];
+
[webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:nil];
+
[webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
+ [attachment expectRequestedDataToBe:htmlData.get()];
+
[webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:nil];
}
TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles)
@@ -252,9 +317,11 @@
[webView expectUpdatesAfterCommand:@"ToggleBold" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
[webView expectUpdatesAfterCommand:@"ToggleItalic" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
[webView expectUpdatesAfterCommand:@"ToggleUnderline" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:testHTMLData()];
// Inserting text should delete the current selection, removing the attachment in the process.
[webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"foo" expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:nil];
}
TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingLists)
@@ -271,6 +338,7 @@
[webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
[webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
[webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+ [attachment expectRequestedDataToBe:testHTMLData()];
}
TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingRichMarkup)
@@ -289,8 +357,81 @@
[webView waitForNextPresentationUpdate];
observer.expectAttachmentUpdates(@[attachment.get()], @[ ]);
}
+ [attachment expectRequestedDataToBe:nil];
}
+TEST(WKAttachmentTests, AttachmentUpdatesWhenCuttingAndPasting)
+{
+ auto webView = webViewForTestingAttachments();
+ RetainPtr<_WKAttachment> attachment;
+ {
+ ObserveAttachmentUpdatesForScope observer(webView.get());
+ attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]);
+ observer.expectAttachmentUpdates(@[], @[attachment.get()]);
+ }
+ [attachment expectRequestedDataToBe:testHTMLData()];
+ [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
+ {
+ ObserveAttachmentUpdatesForScope observer(webView.get());
+ [webView _synchronouslyExecuteEditCommand:@"Cut" argument:nil];
+ observer.expectAttachmentUpdates(@[attachment.get()], @[]);
+ }
+ [attachment expectRequestedDataToBe:nil];
+ {
+ ObserveAttachmentUpdatesForScope observer(webView.get());
+ [webView _synchronouslyExecuteEditCommand:@"Paste" argument:nil];
+ observer.expectAttachmentUpdates(@[], @[attachment.get()]);
+ }
+ [attachment expectRequestedDataToBe:testHTMLData()];
+}
+
+TEST(WKAttachmentTests, AttachmentDataForEmptyFile)
+{
+ auto webView = webViewForTestingAttachments();
+ RetainPtr<_WKAttachment> attachment;
+ {
+ ObserveAttachmentUpdatesForScope observer(webView.get());
+ attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"empty.txt" contentType:@"text/plain" data:[NSData data] options:nil]);
+ observer.expectAttachmentUpdates(@[], @[attachment.get()]);
+ }
+ [attachment expectRequestedDataToBe:[NSData data]];
+ {
+ ObserveAttachmentUpdatesForScope scope(webView.get());
+ [webView _synchronouslyExecuteEditCommand:@"DeleteBackward" argument:nil];
+ scope.expectAttachmentUpdates(@[attachment.get()], @[]);
+ }
+ [attachment expectRequestedDataToBe:nil];
+}
+
+TEST(WKAttachmentTests, MultipleSimultaneousAttachmentDataRequests)
+{
+ auto webView = webViewForTestingAttachments();
+ RetainPtr<NSData> htmlData = testHTMLData();
+ RetainPtr<_WKAttachment> attachment;
+ {
+ ObserveAttachmentUpdatesForScope observer(webView.get());
+ attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:htmlData.get() options:nil]);
+ observer.expectAttachmentUpdates(@[], @[attachment.get()]);
+ }
+ __block RetainPtr<NSData> dataForFirstRequest;
+ __block RetainPtr<NSData> dataForSecondRequest;
+ __block bool done = false;
+ [attachment requestData:^(NSData *data, NSError *error) {
+ EXPECT_TRUE(!error);
+ dataForFirstRequest = retainPtr(data);
+ }];
+ [attachment requestData:^(NSData *data, NSError *error) {
+ EXPECT_TRUE(!error);
+ dataForSecondRequest = retainPtr(data);
+ done = true;
+ }];
+
+ Util::run(&done);
+
+ EXPECT_TRUE([dataForFirstRequest isEqualToData:htmlData.get()]);
+ EXPECT_TRUE([dataForSecondRequest isEqualToData:htmlData.get()]);
+}
+
} // namespace TestWebKitAPI
#endif // WK_API_ENABLED