476660fd5d63877fa56e13470051fa91e100eccd
[WebKit-https.git] / Source / WebCore / bindings / js / JSHTMLDocumentCustom.cpp
1 /*
2  * Copyright (C) 2007-2009, 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 "Frame.h"
30 #include "HTMLCollection.h"
31 #include "HTMLDocument.h"
32 #include "HTMLElement.h"
33 #include "HTMLIFrameElement.h"
34 #include "HTMLNames.h"
35 #include "JSDOMWindow.h"
36 #include "JSDOMWindowCustom.h"
37 #include "JSDOMWindowShell.h"
38 #include "JSDocumentCustom.h"
39 #include "JSHTMLCollection.h"
40 #include "JSMainThreadExecState.h"
41 #include "SegmentedString.h"
42 #include "DocumentParser.h"
43 #include <interpreter/StackVisitor.h>
44 #include <runtime/Error.h>
45 #include <runtime/JSCell.h>
46 #include <wtf/unicode/CharacterNames.h>
47
48 using namespace JSC;
49
50 namespace WebCore {
51
52 using namespace HTMLNames;
53
54 JSValue toJSNewlyCreated(ExecState* state, JSDOMGlobalObject* globalObject, Ref<HTMLDocument>&& passedDocument)
55 {
56     auto& document = passedDocument.get();
57     JSObject* wrapper = createWrapper<HTMLDocument>(globalObject, WTFMove(passedDocument));
58
59     reportMemoryForDocumentIfFrameless(*state, document);
60
61     return wrapper;
62 }
63
64 JSValue toJS(ExecState* state, JSDOMGlobalObject* globalObject, HTMLDocument& document)
65 {
66     if (auto* wrapper = cachedDocumentWrapper(*state, *globalObject, document))
67         return wrapper;
68     return toJSNewlyCreated(state, globalObject, Ref<HTMLDocument>(document));
69 }
70
71 bool JSHTMLDocument::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
72 {
73     JSHTMLDocument* thisObject = jsCast<JSHTMLDocument*>(object);
74     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
75
76     if (propertyName == "open") {
77         if (Base::getOwnPropertySlot(thisObject, exec, propertyName, slot))
78             return true;
79
80         slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsHTMLDocumentPrototypeFunctionOpen, 2>);
81         return true;
82     }
83
84     JSValue value;
85     if (thisObject->nameGetter(exec, propertyName, value)) {
86         slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, value);
87         return true;
88     }
89
90     return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
91 }
92
93 bool JSHTMLDocument::nameGetter(ExecState* exec, PropertyName propertyName, JSValue& value)
94 {
95     auto& document = wrapped();
96
97     AtomicStringImpl* atomicPropertyName = propertyName.publicName();
98     if (!atomicPropertyName || !document.hasDocumentNamedItem(*atomicPropertyName))
99         return false;
100
101     if (UNLIKELY(document.documentNamedItemContainsMultipleElements(*atomicPropertyName))) {
102         Ref<HTMLCollection> collection = document.documentNamedItems(atomicPropertyName);
103         ASSERT(collection->length() > 1);
104         value = toJS(exec, globalObject(), collection);
105         return true;
106     }
107
108     Element& element = *document.documentNamedItem(*atomicPropertyName);
109     if (UNLIKELY(is<HTMLIFrameElement>(element))) {
110         if (Frame* frame = downcast<HTMLIFrameElement>(element).contentFrame()) {
111             value = toJS(exec, frame);
112             return true;
113         }
114     }
115
116     value = toJS(exec, globalObject(), element);
117     return true;
118 }
119
120 // Custom attributes
121
122 JSValue JSHTMLDocument::all(ExecState& state) const
123 {
124     // If "all" has been overwritten, return the overwritten value
125     JSValue v = getDirect(state.vm(), Identifier::fromString(&state, "all"));
126     if (v)
127         return v;
128
129     return toJS(&state, globalObject(), wrapped().all());
130 }
131
132 void JSHTMLDocument::setAll(ExecState& state, JSValue value)
133 {
134     // Add "all" to the property map.
135     putDirect(state.vm(), Identifier::fromString(&state, "all"), value);
136 }
137
138 static Document* findCallingDocument(ExecState& state)
139 {
140     CallerFunctor functor;
141     state.iterate(functor);
142     CallFrame* callerFrame = functor.callerFrame();
143     if (!callerFrame)
144         return nullptr;
145
146     return asJSDOMWindow(functor.callerFrame()->lexicalGlobalObject())->wrapped().document();
147 }
148
149 // Custom functions
150
151 JSValue JSHTMLDocument::open(ExecState& state)
152 {
153     VM& vm = state.vm();
154     auto scope = DECLARE_THROW_SCOPE(vm);
155
156     // For compatibility with other browsers, pass open calls with more than 2 parameters to the window.
157     if (state.argumentCount() > 2) {
158         if (Frame* frame = wrapped().frame()) {
159             JSDOMWindowShell* wrapper = toJSDOMWindowShell(frame, currentWorld(&state));
160             if (wrapper) {
161                 JSValue function = wrapper->get(&state, Identifier::fromString(&state, "open"));
162                 CallData callData;
163                 CallType callType = ::getCallData(function, callData);
164                 if (callType == CallType::None)
165                     return throwTypeError(&state, scope);
166                 return JSC::call(&state, function, callType, callData, wrapper, ArgList(&state));
167             }
168         }
169         return jsUndefined();
170     }
171
172     // document.open clobbers the security context of the document and
173     // aliases it with the active security context.
174     Document* activeDocument = asJSDOMWindow(state.lexicalGlobalObject())->wrapped().document();
175
176     // In the case of two parameters or fewer, do a normal document open.
177     wrapped().open(activeDocument);
178     return this;
179 }
180
181 enum NewlineRequirement { DoNotAddNewline, DoAddNewline };
182
183 static inline void documentWrite(ExecState& state, JSHTMLDocument* thisDocument, NewlineRequirement addNewline)
184 {
185     HTMLDocument* document = &thisDocument->wrapped();
186     // DOM only specifies single string argument, but browsers allow multiple or no arguments.
187
188     size_t size = state.argumentCount();
189
190     String firstString = state.argument(0).toString(&state)->value(&state);
191     SegmentedString segmentedString = firstString;
192     if (size != 1) {
193         if (!size)
194             segmentedString.clear();
195         else {
196             for (size_t i = 1; i < size; ++i) {
197                 String subsequentString = state.uncheckedArgument(i).toString(&state)->value(&state);
198                 segmentedString.append(SegmentedString(subsequentString));
199             }
200         }
201     }
202     if (addNewline)
203         segmentedString.append(SegmentedString(String(&newlineCharacter, 1)));
204
205     Document* activeDocument = findCallingDocument(state);
206     document->write(segmentedString, activeDocument);
207 }
208
209 JSValue JSHTMLDocument::write(ExecState& state)
210 {
211     documentWrite(state, this, DoNotAddNewline);
212     return jsUndefined();
213 }
214
215 JSValue JSHTMLDocument::writeln(ExecState& state)
216 {
217     documentWrite(state, this, DoAddNewline);
218     return jsUndefined();
219 }
220
221 } // namespace WebCore