[WebIDL] Replace general inclusion of JSDOMConvert.h with inclusion of individual...
[WebKit.git] / Source / WebCore / bindings / js / JSHTMLDocumentCustom.cpp
1 /*
2  * Copyright (C) 2007-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "JSHTMLDocument.h"
28
29 #include "JSDOMAbstractOperations.h"
30 #include "JSDOMConvertInterface.h"
31 #include "JSDOMConvertUnion.h"
32 #include "JSDOMWindowCustom.h"
33 #include "JSHTMLCollection.h"
34 #include "SegmentedString.h"
35 #include <runtime/Lookup.h>
36
37 using namespace JSC;
38
39 namespace WebCore {
40
41 using namespace HTMLNames;
42
43 JSValue toJSNewlyCreated(ExecState* state, JSDOMGlobalObject* globalObject, Ref<HTMLDocument>&& passedDocument)
44 {
45     auto& document = passedDocument.get();
46     auto* wrapper = createWrapper<HTMLDocument>(globalObject, WTFMove(passedDocument));
47     reportMemoryForDocumentIfFrameless(*state, document);
48     return wrapper;
49 }
50
51 JSValue toJS(ExecState* state, JSDOMGlobalObject* globalObject, HTMLDocument& document)
52 {
53     if (auto* wrapper = cachedDocumentWrapper(*state, *globalObject, document))
54         return wrapper;
55     return toJSNewlyCreated(state, globalObject, Ref<HTMLDocument>(document));
56 }
57
58 bool JSHTMLDocument::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)
59 {
60     auto* thisObject = jsCast<JSHTMLDocument*>(object);
61     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
62     
63     if (propertyName == "open") {
64         if (Base::getOwnPropertySlot(thisObject, state, propertyName, slot))
65             return true;
66         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsHTMLDocumentPrototypeFunctionOpen, 2>);
67         return true;
68     }
69
70     using GetterIDLType = IDLUnion<IDLInterface<DOMWindow>, IDLInterface<Element>, IDLInterface<HTMLCollection>>;
71     auto getterFunctor = [] (auto& thisObject, auto propertyName) -> std::optional<typename GetterIDLType::ImplementationType> {
72         auto result = thisObject.wrapped().namedItem(propertyNameToAtomicString(propertyName));
73         if (!GetterIDLType::isNullValue(result))
74             return typename GetterIDLType::ImplementationType { GetterIDLType::extractValueFromNullable(result) };
75         return std::nullopt;
76     };
77     if (auto namedProperty = accessVisibleNamedProperty<OverrideBuiltins::Yes>(*state, *thisObject, propertyName, getterFunctor)) {
78         slot.setValue(thisObject, ReadOnly, toJS<GetterIDLType>(*state, *thisObject->globalObject(), WTFMove(namedProperty.value())));
79         return true;
80     }
81     return JSObject::getOwnPropertySlot(object, state, propertyName, slot);
82 }
83
84 // Custom attributes
85
86 JSValue JSHTMLDocument::all(ExecState& state) const
87 {
88     // If "all" has been overwritten, return the overwritten value
89     if (auto overwrittenValue = getDirect(state.vm(), Identifier::fromString(&state, "all")))
90         return overwrittenValue;
91
92     return toJS(&state, globalObject(), wrapped().all());
93 }
94
95 void JSHTMLDocument::setAll(ExecState& state, JSValue value)
96 {
97     // Add "all" to the property map.
98     putDirect(state.vm(), Identifier::fromString(&state, "all"), value);
99 }
100
101 static inline Document* findCallingDocument(ExecState& state)
102 {
103     CallerFunctor functor;
104     state.iterate(functor);
105     auto* callerFrame = functor.callerFrame();
106     if (!callerFrame)
107         return nullptr;
108     return asJSDOMWindow(callerFrame->lexicalGlobalObject())->wrapped().document();
109 }
110
111 // Custom functions
112
113 JSValue JSHTMLDocument::open(ExecState& state)
114 {
115     VM& vm = state.vm();
116     auto scope = DECLARE_THROW_SCOPE(vm);
117
118     // For compatibility with other browsers, pass open calls with more than 2 parameters to the window.
119     if (state.argumentCount() > 2) {
120         if (auto* frame = wrapped().frame()) {
121             if (auto* wrapper = toJSDOMWindowProxy(frame, currentWorld(&state))) {
122                 auto function = wrapper->get(&state, Identifier::fromString(&state, "open"));
123                 CallData callData;
124                 auto callType = ::getCallData(function, callData);
125                 if (callType == CallType::None)
126                     return throwTypeError(&state, scope);
127                 return JSC::call(&state, function, callType, callData, wrapper, ArgList(&state));
128             }
129         }
130         return jsUndefined();
131     }
132
133     // Calling document.open clobbers the security context of the document and aliases it with the active security context.
134     // FIXME: Is it correct that this does not use findCallingDocument as the write function below does?
135     wrapped().open(asJSDOMWindow(state.lexicalGlobalObject())->wrapped().document());
136     // FIXME: Why do we return the document instead of returning undefined?
137     return this;
138 }
139
140 enum NewlineRequirement { DoNotAddNewline, DoAddNewline };
141
142 static inline JSValue documentWrite(ExecState& state, JSHTMLDocument& document, NewlineRequirement addNewline)
143 {
144     VM& vm = state.vm();
145     auto scope = DECLARE_THROW_SCOPE(vm);
146
147     SegmentedString segmentedString;
148     size_t argumentCount = state.argumentCount();
149     for (size_t i = 0; i < argumentCount; ++i) {
150         segmentedString.append(state.uncheckedArgument(i).toWTFString(&state));
151         RETURN_IF_EXCEPTION(scope, { });
152     }
153     if (addNewline)
154         segmentedString.append(String { "\n" });
155
156     document.wrapped().write(WTFMove(segmentedString), findCallingDocument(state));
157     return jsUndefined();
158 }
159
160 JSValue JSHTMLDocument::write(ExecState& state)
161 {
162     return documentWrite(state, *this, DoNotAddNewline);
163 }
164
165 JSValue JSHTMLDocument::writeln(ExecState& state)
166 {
167     return documentWrite(state, *this, DoAddNewline);
168 }
169
170 } // namespace WebCore