blob: ca1031483a2bb6e7f309197ee3a7638c902cd5a6 [file] [log] [blame] [edit]
/*
* Copyright (C) 2025 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. AND ITS CONTRIBUTORS ``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 ITS 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.
*/
#import "config.h"
#import "PlatformUtilities.h"
#import "TestNavigationDelegate.h"
#import "TestWKWebView.h"
#import <WebKit/WKContentWorldPrivate.h>
#import <WebKit/_WKContentWorldConfiguration.h>
#import <WebKit/_WKSerializedNode.h>
namespace TestWebKitAPI {
TEST(SerializedNode, Basic)
{
RetainPtr webView = adoptNS([WKWebView new]);
[webView loadHTMLString:@"<div id='testid'><div>test</div></div><template id='outerTemplate'><template id='innerTemplate'><span>Contents</span></template></template>" baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
[webView _test_waitForDidFinishNavigation];
RetainPtr worldConfiguration = adoptNS([_WKContentWorldConfiguration new]);
worldConfiguration.get().allowNodeSerialization = YES;
RetainPtr world = [WKContentWorld _worldWithConfiguration:worldConfiguration.get()];
auto verifyNodeSerialization = [world, webView] (const char* constructor, const char* accessor, const char* expected, const char* className, const char* init = "deep:true") {
RetainPtr serializedNode = [webView objectByEvaluatingJavaScript:[NSString stringWithFormat:@"window.webkit.serializeNode(%s, { %s })", constructor, init] inFrame:nil inContentWorld:world.get()];
EXPECT_TRUE([serializedNode isKindOfClass:_WKSerializedNode.class]);
RetainPtr other = adoptNS([TestWKWebView new]);
id instanceof = [other objectByCallingAsyncFunction:@"return Object.getPrototypeOf(n).toString()" withArguments:@{ @"n" : serializedNode.get() }];
NSString *expectedClass = [NSString stringWithFormat:@"[object %s]", className];
EXPECT_WK_STREQ(instanceof, expectedClass);
id result = [other objectByCallingAsyncFunction:[NSString stringWithFormat:@"return %s", accessor] withArguments:@{ @"n" : serializedNode.get() }];
EXPECT_WK_STREQ(result, expected);
};
auto textAccessor = "n.wholeText";
verifyNodeSerialization("document.createTextNode('text content')", textAccessor, "text content", "Text");
auto attrAccessor = "n.namespaceURI + ',' + n.prefix + ',' + n.localName + ',' + n.name + ',' + n.value";
verifyNodeSerialization("document.createAttributeNS('a', 'b')", attrAccessor, "a,null,b,b,", "Attr");
verifyNodeSerialization("document.createAttribute('c')", attrAccessor, "null,null,c,c,", "Attr");
verifyNodeSerialization("new Document().createCDATASection('test')", textAccessor, "test", "CDATASection");
verifyNodeSerialization("document.implementation.createDocumentType('a', 'b', 'c')", "n.name + ',' + n.publicId + ',' + n.systemId", "a,b,c", "DocumentType");
verifyNodeSerialization("document.createProcessingInstruction('a', 'b')", "n.target + ',' + n.data", "a,b", "ProcessingInstruction");
auto documentAccessor = "n.URL + ',' + n.documentURI + ',' + new XMLSerializer().serializeToString(n)";
verifyNodeSerialization("document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', null)", documentAccessor, "about:blank,about:blank,<svg xmlns=\"http://www.w3.org/2000/svg\"/>", "XMLDocument");
verifyNodeSerialization("document.implementation.createHTMLDocument('test title')", documentAccessor, "about:blank,about:blank,<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>test title</title></head><body></body></html>", "HTMLDocument");
verifyNodeSerialization("document", documentAccessor, "https://webkit.org/,https://webkit.org/,<html xmlns=\"http://www.w3.org/1999/xhtml\"><head></head><body><div id=\"testid\"><div>test</div></div><template id=\"outerTemplate\"><template id=\"innerTemplate\"><span>Contents</span></template></template></body></html>", "HTMLDocument");
verifyNodeSerialization("document.getElementById('testid')", documentAccessor, "undefined,undefined,<div xmlns=\"http://www.w3.org/1999/xhtml\" id=\"testid\"></div>", "HTMLDivElement", "");
verifyNodeSerialization("document.createDocumentFragment()", "n.nodeName", "#document-fragment", "DocumentFragment");
NSString *shadowRootJS = @""
"<div id='host'>"
" <template shadowrootmode='open' shadowrootclonable='true' shadowrootcustomelements>"
" <a-b>"
" <template shadowrootmode='open' shadowrootclonable='true' shadowrootcustomelements>"
" <c-d/><c-d>"
" </template>"
" </a-b>"
" <ef></ef>"
" </template>"
"</div>";
[webView loadHTMLString:shadowRootJS baseURL:[NSURL URLWithString:@"https://webkit.org/"]];
[webView _test_waitForDidFinishNavigation];
verifyNodeSerialization("document.getElementById('host')", "n.shadowRoot.mode + ' ' + n.shadowRoot.delegatesFocus + ' ' + n.shadowRoot.serializable + ' ' + n.shadowRoot.host.tagName + ' ' + n.shadowRoot.querySelector('c-d')", "open false false DIV null", "HTMLDivElement");
__block bool done { false };
[webView evaluateJavaScript:@"window.webkit.serializeNode(document.getElementById('host').shadowRoot)" inFrame:nil inContentWorld:world.get() completionHandler:^(id, NSError *error) {
EXPECT_WK_STREQ(error.domain, WKErrorDomain);
EXPECT_EQ(error.code, WKErrorJavaScriptExceptionOccurred);
done = true;
}];
Util::run(&done);
}
}