Streamline and speed up tokenizer and segmented string classes
[WebKit-https.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 "HTMLIFrameElement.h"
30 #include "JSDOMWindowCustom.h"
31 #include "JSHTMLCollection.h"
32 #include "SegmentedString.h"
33
34 using namespace JSC;
35
36 namespace WebCore {
37
38 using namespace HTMLNames;
39
40 JSValue toJSNewlyCreated(ExecState* state, JSDOMGlobalObject* globalObject, Ref<HTMLDocument>&& passedDocument)
41 {
42     auto& document = passedDocument.get();
43     auto* wrapper = createWrapper<HTMLDocument>(globalObject, WTFMove(passedDocument));
44     reportMemoryForDocumentIfFrameless(*state, document);
45     return wrapper;
46 }
47
48 JSValue toJS(ExecState* state, JSDOMGlobalObject* globalObject, HTMLDocument& document)
49 {
50     if (auto* wrapper = cachedDocumentWrapper(*state, *globalObject, document))
51         return wrapper;
52     return toJSNewlyCreated(state, globalObject, Ref<HTMLDocument>(document));
53 }
54
55 bool JSHTMLDocument::getOwnPropertySlot(JSObject* object, ExecState* state, PropertyName propertyName, PropertySlot& slot)
56 {
57     auto& thisObject = *jsCast<JSHTMLDocument*>(object);
58     ASSERT_GC_OBJECT_INHERITS((&thisObject), info());
59
60     if (propertyName == "open") {
61         if (Base::getOwnPropertySlot(&thisObject, state, propertyName, slot))
62             return true;
63         slot.setCustom(&thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsHTMLDocumentPrototypeFunctionOpen, 2>);
64         return true;
65     }
66
67     JSValue value;
68     if (thisObject.nameGetter(state, propertyName, value)) {
69         slot.setValue(&thisObject, ReadOnly | DontDelete | DontEnum, value);
70         return true;
71     }
72
73     return Base::getOwnPropertySlot(&thisObject, state, propertyName, slot);
74 }
75
76 bool JSHTMLDocument::nameGetter(ExecState* state, PropertyName propertyName, JSValue& value)
77 {
78     auto& document = wrapped();
79
80     auto* atomicPropertyName = propertyName.publicName();
81     if (!atomicPropertyName || !document.hasDocumentNamedItem(*atomicPropertyName))
82         return false;
83
84     if (UNLIKELY(document.documentNamedItemContainsMultipleElements(*atomicPropertyName))) {
85         auto collection = document.documentNamedItems(atomicPropertyName);
86         ASSERT(collection->length() > 1);
87         value = toJS(state, globalObject(), collection);
88         return true;
89     }
90
91     auto& element = *document.documentNamedItem(*atomicPropertyName);
92     if (UNLIKELY(is<HTMLIFrameElement>(element))) {
93         if (auto* frame = downcast<HTMLIFrameElement>(element).contentFrame()) {
94             value = toJS(state, frame);
95             return true;
96         }
97     }
98
99     value = toJS(state, globalObject(), element);
100     return true;
101 }
102
103 // Custom attributes
104
105 JSValue JSHTMLDocument::all(ExecState& state) const
106 {
107     // If "all" has been overwritten, return the overwritten value
108     if (auto overwrittenValue = getDirect(state.vm(), Identifier::fromString(&state, "all")))
109         return overwrittenValue;
110
111     return toJS(&state, globalObject(), wrapped().all());
112 }
113
114 void JSHTMLDocument::setAll(ExecState& state, JSValue value)
115 {
116     // Add "all" to the property map.
117     putDirect(state.vm(), Identifier::fromString(&state, "all"), value);
118 }
119
120 static inline Document* findCallingDocument(ExecState& state)
121 {
122     CallerFunctor functor;
123     state.iterate(functor);
124     auto* callerFrame = functor.callerFrame();
125     if (!callerFrame)
126         return nullptr;
127     return asJSDOMWindow(callerFrame->lexicalGlobalObject())->wrapped().document();
128 }
129
130 // Custom functions
131
132 JSValue JSHTMLDocument::open(ExecState& state)
133 {
134     VM& vm = state.vm();
135     auto scope = DECLARE_THROW_SCOPE(vm);
136
137     // For compatibility with other browsers, pass open calls with more than 2 parameters to the window.
138     if (state.argumentCount() > 2) {
139         if (auto* frame = wrapped().frame()) {
140             if (auto* wrapper = toJSDOMWindowShell(frame, currentWorld(&state))) {
141                 auto function = wrapper->get(&state, Identifier::fromString(&state, "open"));
142                 CallData callData;
143                 auto callType = ::getCallData(function, callData);
144                 if (callType == CallType::None)
145                     return throwTypeError(&state, scope);
146                 return JSC::call(&state, function, callType, callData, wrapper, ArgList(&state));
147             }
148         }
149         return jsUndefined();
150     }
151
152     // Calling document.open clobbers the security context of the document and aliases it with the active security context.
153     // FIXME: Is it correct that this does not use findCallingDocument as the write function below does?
154     wrapped().open(asJSDOMWindow(state.lexicalGlobalObject())->wrapped().document());
155     // FIXME: Why do we return the document instead of returning undefined?
156     return this;
157 }
158
159 enum NewlineRequirement { DoNotAddNewline, DoAddNewline };
160
161 static inline JSValue documentWrite(ExecState& state, JSHTMLDocument& document, NewlineRequirement addNewline)
162 {
163     VM& vm = state.vm();
164     auto scope = DECLARE_THROW_SCOPE(vm);
165
166     SegmentedString segmentedString;
167     size_t argumentCount = state.argumentCount();
168     for (size_t i = 0; i < argumentCount; ++i) {
169         segmentedString.append(state.uncheckedArgument(i).toWTFString(&state));
170         RETURN_IF_EXCEPTION(scope, { });
171     }
172     if (addNewline)
173         segmentedString.append(String { "\n" });
174
175     document.wrapped().write(WTFMove(segmentedString), findCallingDocument(state));
176     return jsUndefined();
177 }
178
179 JSValue JSHTMLDocument::write(ExecState& state)
180 {
181     return documentWrite(state, *this, DoNotAddNewline);
182 }
183
184 JSValue JSHTMLDocument::writeln(ExecState& state)
185 {
186     return documentWrite(state, *this, DoAddNewline);
187 }
188
189 } // namespace WebCore