1 /*
2 * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "JSDOMWindowCustom.h"
22
23 #include "AtomicString.h"
24 #include "Base64.h"
25 #include "DOMWindow.h"
26 #include "Document.h"
27 #include "ExceptionCode.h"
28 #include "FloatRect.h"
29 #include "Frame.h"
30 #include "FrameLoadRequest.h"
31 #include "FrameLoader.h"
32 #include "FrameTree.h"
33 #include "FrameView.h"
34 #include "HTMLCollection.h"
35 #include "HTMLDocument.h"
36 #include "History.h"
37 #include "JSAudioConstructor.h"
38 #include "JSDOMWindowShell.h"
39 #include "JSEvent.h"
40 #include "JSEventListener.h"
41 #include "JSHTMLCollection.h"
42 #include "JSHistory.h"
43 #include "JSImageConstructor.h"
44 #include "JSLocation.h"
45 #include "JSMessageChannelConstructor.h"
46 #include "JSMessagePort.h"
47 #include "JSOptionConstructor.h"
48 #include "JSSharedWorkerConstructor.h"
49 #include "JSWebKitCSSMatrixConstructor.h"
50 #include "JSWebKitPointConstructor.h"
51 #include "JSWorkerConstructor.h"
52 #include "JSXMLHttpRequestConstructor.h"
53 #include "JSXSLTProcessorConstructor.h"
54 #include "Location.h"
55 #include "MediaPlayer.h"
56 #include "MessagePort.h"
57 #include "Page.h"
58 #include "PlatformScreen.h"
59 #include "RegisteredEventListener.h"
60 #include "ScheduledAction.h"
61 #include "ScriptController.h"
62 #include "Settings.h"
63 #include "WindowFeatures.h"
64 #include <runtime/JSObject.h>
65 #include <runtime/PrototypeFunction.h>
66
67 using namespace JSC;
68
69 namespace WebCore {
70
markChildren(MarkStack & markStack)71 void JSDOMWindow::markChildren(MarkStack& markStack)
72 {
73 Base::markChildren(markStack);
74
75 markEventListeners(markStack, impl()->eventListeners());
76
77 JSGlobalData& globalData = *Heap::heap(this)->globalData();
78
79 markDOMObjectWrapper(markStack, globalData, impl()->optionalConsole());
80 markDOMObjectWrapper(markStack, globalData, impl()->optionalHistory());
81 markDOMObjectWrapper(markStack, globalData, impl()->optionalLocationbar());
82 markDOMObjectWrapper(markStack, globalData, impl()->optionalMenubar());
83 markDOMObjectWrapper(markStack, globalData, impl()->optionalNavigator());
84 markDOMObjectWrapper(markStack, globalData, impl()->optionalPersonalbar());
85 markDOMObjectWrapper(markStack, globalData, impl()->optionalScreen());
86 markDOMObjectWrapper(markStack, globalData, impl()->optionalScrollbars());
87 markDOMObjectWrapper(markStack, globalData, impl()->optionalSelection());
88 markDOMObjectWrapper(markStack, globalData, impl()->optionalStatusbar());
89 markDOMObjectWrapper(markStack, globalData, impl()->optionalToolbar());
90 markDOMObjectWrapper(markStack, globalData, impl()->optionalLocation());
91 #if ENABLE(DOM_STORAGE)
92 markDOMObjectWrapper(markStack, globalData, impl()->optionalSessionStorage());
93 markDOMObjectWrapper(markStack, globalData, impl()->optionalLocalStorage());
94 #endif
95 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
96 markDOMObjectWrapper(markStack, globalData, impl()->optionalApplicationCache());
97 #endif
98 }
99
100 template<NativeFunction nativeFunction, int length>
nonCachingStaticFunctionGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot &)101 JSValue nonCachingStaticFunctionGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot&)
102 {
103 return new (exec) NativeFunctionWrapper(exec, exec->lexicalGlobalObject()->prototypeFunctionStructure(), length, propertyName, nativeFunction);
104 }
105
childFrameGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot & slot)106 static JSValue childFrameGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
107 {
108 return toJS(exec, static_cast<JSDOMWindow*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(AtomicString(propertyName))->domWindow());
109 }
110
indexGetter(ExecState * exec,const Identifier &,const PropertySlot & slot)111 static JSValue indexGetter(ExecState* exec, const Identifier&, const PropertySlot& slot)
112 {
113 return toJS(exec, static_cast<JSDOMWindow*>(asObject(slot.slotBase()))->impl()->frame()->tree()->child(slot.index())->domWindow());
114 }
115
namedItemGetter(ExecState * exec,const Identifier & propertyName,const PropertySlot & slot)116 static JSValue namedItemGetter(ExecState* exec, const Identifier& propertyName, const PropertySlot& slot)
117 {
118 JSDOMWindowBase* thisObj = static_cast<JSDOMWindow*>(asObject(slot.slotBase()));
119 Document* document = thisObj->impl()->frame()->document();
120
121 ASSERT(thisObj->allowsAccessFrom(exec));
122 ASSERT(document);
123 ASSERT(document->isHTMLDocument());
124
125 RefPtr<HTMLCollection> collection = document->windowNamedItems(propertyName);
126 if (collection->length() == 1)
127 return toJS(exec, collection->firstItem());
128 return toJS(exec, collection.get());
129 }
130
getOwnPropertySlot(ExecState * exec,const Identifier & propertyName,PropertySlot & slot)131 bool JSDOMWindow::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
132 {
133 // When accessing a Window cross-domain, functions are always the native built-in ones, and they
134 // are not affected by properties changed on the Window or anything in its prototype chain.
135 // This is consistent with the behavior of Firefox.
136
137 const HashEntry* entry;
138
139 // We don't want any properties other than "close" and "closed" on a closed window.
140 if (!impl()->frame()) {
141 // The following code is safe for cross-domain and same domain use.
142 // It ignores any custom properties that might be set on the DOMWindow (including a custom prototype).
143 entry = s_info.propHashTable(exec)->entry(exec, propertyName);
144 if (entry && !(entry->attributes() & Function) && entry->propertyGetter() == jsDOMWindowClosed) {
145 slot.setCustom(this, entry->propertyGetter());
146 return true;
147 }
148 entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
149 if (entry && (entry->attributes() & Function) && entry->function() == jsDOMWindowPrototypeFunctionClose) {
150 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
151 return true;
152 }
153
154 // FIXME: We should have a message here that explains why the property access/function call was
155 // not allowed.
156 slot.setUndefined();
157 return true;
158 }
159
160 // We need to check for cross-domain access here without printing the generic warning message
161 // because we always allow access to some function, just different ones depending whether access
162 // is allowed.
163 String errorMessage;
164 bool allowsAccess = allowsAccessFrom(exec, errorMessage);
165
166 // Look for overrides before looking at any of our own properties, but ignore overrides completely
167 // if this is cross-domain access.
168 if (allowsAccess && JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot))
169 return true;
170
171 // We need this code here because otherwise JSDOMWindowBase will stop the search before we even get to the
172 // prototype due to the blanket same origin (allowsAccessFrom) check at the end of getOwnPropertySlot.
173 // Also, it's important to get the implementation straight out of the DOMWindow prototype regardless of
174 // what prototype is actually set on this object.
175 entry = JSDOMWindowPrototype::s_info.propHashTable(exec)->entry(exec, propertyName);
176 if (entry) {
177 if (entry->attributes() & Function) {
178 if (entry->function() == jsDOMWindowPrototypeFunctionBlur) {
179 if (!allowsAccess) {
180 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionBlur, 0>);
181 return true;
182 }
183 } else if (entry->function() == jsDOMWindowPrototypeFunctionClose) {
184 if (!allowsAccess) {
185 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionClose, 0>);
186 return true;
187 }
188 } else if (entry->function() == jsDOMWindowPrototypeFunctionFocus) {
189 if (!allowsAccess) {
190 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionFocus, 0>);
191 return true;
192 }
193 } else if (entry->function() == jsDOMWindowPrototypeFunctionPostMessage) {
194 if (!allowsAccess) {
195 slot.setCustom(this, nonCachingStaticFunctionGetter<jsDOMWindowPrototypeFunctionPostMessage, 2>);
196 return true;
197 }
198 } else if (entry->function() == jsDOMWindowPrototypeFunctionShowModalDialog) {
199 if (!DOMWindow::canShowModalDialog(impl()->frame())) {
200 slot.setUndefined();
201 return true;
202 }
203 }
204 }
205 } else {
206 // Allow access to toString() cross-domain, but always Object.prototype.toString.
207 if (propertyName == exec->propertyNames().toString) {
208 if (!allowsAccess) {
209 slot.setCustom(this, objectToStringFunctionGetter);
210 return true;
211 }
212 }
213 }
214
215 entry = JSDOMWindow::s_info.propHashTable(exec)->entry(exec, propertyName);
216 if (entry) {
217 slot.setCustom(this, entry->propertyGetter());
218 return true;
219 }
220
221 // Check for child frames by name before built-in properties to
222 // match Mozilla. This does not match IE, but some sites end up
223 // naming frames things that conflict with window properties that
224 // are in Moz but not IE. Since we have some of these, we have to do
225 // it the Moz way.
226 if (impl()->frame()->tree()->child(propertyName)) {
227 slot.setCustom(this, childFrameGetter);
228 return true;
229 }
230
231 // Do prototype lookup early so that functions and attributes in the prototype can have
232 // precedence over the index and name getters.
233 JSValue proto = prototype();
234 if (proto.isObject()) {
235 if (asObject(proto)->getPropertySlot(exec, propertyName, slot)) {
236 if (!allowsAccess) {
237 printErrorMessage(errorMessage);
238 slot.setUndefined();
239 }
240 return true;
241 }
242 }
243
244 // FIXME: Search the whole frame hierachy somewhere around here.
245 // We need to test the correct priority order.
246
247 // allow window[1] or parent[1] etc. (#56983)
248 bool ok;
249 unsigned i = propertyName.toArrayIndex(&ok);
250 if (ok && i < impl()->frame()->tree()->childCount()) {
251 slot.setCustomIndex(this, i, indexGetter);
252 return true;
253 }
254
255 if (!allowsAccess) {
256 printErrorMessage(errorMessage);
257 slot.setUndefined();
258 return true;
259 }
260
261 // Allow shortcuts like 'Image1' instead of document.images.Image1
262 Document* document = impl()->frame()->document();
263 if (document->isHTMLDocument()) {
264 AtomicStringImpl* atomicPropertyName = AtomicString::find(propertyName);
265 if (atomicPropertyName && (static_cast<HTMLDocument*>(document)->hasNamedItem(atomicPropertyName) || document->hasElementWithId(atomicPropertyName))) {
266 slot.setCustom(this, namedItemGetter);
267 return true;
268 }
269 }
270
271 return Base::getOwnPropertySlot(exec, propertyName, slot);
272 }
273
put(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)274 void JSDOMWindow::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
275 {
276 if (!impl()->frame())
277 return;
278
279 // Optimization: access JavaScript global variables directly before involving the DOM.
280 if (JSGlobalObject::hasOwnPropertyForWrite(exec, propertyName)) {
281 if (allowsAccessFrom(exec))
282 JSGlobalObject::put(exec, propertyName, value, slot);
283 return;
284 }
285
286 if (lookupPut<JSDOMWindow>(exec, propertyName, value, s_info.propHashTable(exec), this))
287 return;
288
289 if (allowsAccessFrom(exec))
290 Base::put(exec, propertyName, value, slot);
291 }
292
deleteProperty(ExecState * exec,const Identifier & propertyName)293 bool JSDOMWindow::deleteProperty(ExecState* exec, const Identifier& propertyName)
294 {
295 // Only allow deleting properties by frames in the same origin.
296 if (!allowsAccessFrom(exec))
297 return false;
298 return Base::deleteProperty(exec, propertyName);
299 }
300
getPropertyNames(ExecState * exec,PropertyNameArray & propertyNames)301 void JSDOMWindow::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
302 {
303 // Only allow the window to enumerated by frames in the same origin.
304 if (!allowsAccessFrom(exec))
305 return;
306 Base::getPropertyNames(exec, propertyNames);
307 }
308
getPropertyAttributes(ExecState * exec,const Identifier & propertyName,unsigned & attributes) const309 bool JSDOMWindow::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
310 {
311 // Only allow getting property attributes properties by frames in the same origin.
312 if (!allowsAccessFrom(exec))
313 return false;
314 return Base::getPropertyAttributes(exec, propertyName, attributes);
315 }
316
defineGetter(ExecState * exec,const Identifier & propertyName,JSObject * getterFunction)317 void JSDOMWindow::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
318 {
319 // Only allow defining getters by frames in the same origin.
320 if (!allowsAccessFrom(exec))
321 return;
322
323 // Don't allow shadowing location using defineGetter.
324 if (propertyName == "location")
325 return;
326
327 Base::defineGetter(exec, propertyName, getterFunction);
328 }
329
defineSetter(ExecState * exec,const Identifier & propertyName,JSObject * setterFunction)330 void JSDOMWindow::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
331 {
332 // Only allow defining setters by frames in the same origin.
333 if (!allowsAccessFrom(exec))
334 return;
335 Base::defineSetter(exec, propertyName, setterFunction);
336 }
337
lookupGetter(ExecState * exec,const Identifier & propertyName)338 JSValue JSDOMWindow::lookupGetter(ExecState* exec, const Identifier& propertyName)
339 {
340 // Only allow looking-up getters by frames in the same origin.
341 if (!allowsAccessFrom(exec))
342 return jsUndefined();
343 return Base::lookupGetter(exec, propertyName);
344 }
345
lookupSetter(ExecState * exec,const Identifier & propertyName)346 JSValue JSDOMWindow::lookupSetter(ExecState* exec, const Identifier& propertyName)
347 {
348 // Only allow looking-up setters by frames in the same origin.
349 if (!allowsAccessFrom(exec))
350 return jsUndefined();
351 return Base::lookupSetter(exec, propertyName);
352 }
353
354 // Custom Attributes
355
history(ExecState * exec) const356 JSValue JSDOMWindow::history(ExecState* exec) const
357 {
358 History* history = impl()->history();
359 if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec->globalData(), history))
360 return wrapper;
361
362 JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
363 JSHistory* jsHistory = new (exec) JSHistory(getDOMStructure<JSHistory>(exec, window), window, history);
364 cacheDOMObjectWrapper(exec->globalData(), history, jsHistory);
365 return jsHistory;
366 }
367
location(ExecState * exec) const368 JSValue JSDOMWindow::location(ExecState* exec) const
369 {
370 Location* location = impl()->location();
371 if (DOMObject* wrapper = getCachedDOMObjectWrapper(exec->globalData(), location))
372 return wrapper;
373
374 JSDOMWindow* window = const_cast<JSDOMWindow*>(this);
375 JSLocation* jsLocation = new (exec) JSLocation(getDOMStructure<JSLocation>(exec, window), window, location);
376 cacheDOMObjectWrapper(exec->globalData(), location, jsLocation);
377 return jsLocation;
378 }
379
setLocation(ExecState * exec,JSValue value)380 void JSDOMWindow::setLocation(ExecState* exec, JSValue value)
381 {
382 Frame* lexicalFrame = toLexicalFrame(exec);
383 if (!lexicalFrame)
384 return;
385
386 #if ENABLE(DASHBOARD_SUPPORT)
387 // To avoid breaking old widgets, make "var location =" in a top-level frame create
388 // a property named "location" instead of performing a navigation (<rdar://problem/5688039>).
389 if (Settings* settings = lexicalFrame->settings()) {
390 if (settings->usesDashboardBackwardCompatibilityMode() && !lexicalFrame->tree()->parent()) {
391 if (allowsAccessFrom(exec))
392 putDirect(Identifier(exec, "location"), value);
393 return;
394 }
395 }
396 #endif
397
398 Frame* frame = impl()->frame();
399 ASSERT(frame);
400
401 if (!shouldAllowNavigation(exec, frame))
402 return;
403
404 KURL url = completeURL(exec, value.toString(exec));
405 if (url.isNull())
406 return;
407
408 if (!protocolIsJavaScript(url) || allowsAccessFrom(exec)) {
409 // We want a new history item if this JS was called via a user gesture
410 frame->loader()->scheduleLocationChange(url, lexicalFrame->loader()->outgoingReferrer(), !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, processingUserGesture(exec));
411 }
412 }
413
crypto(ExecState *) const414 JSValue JSDOMWindow::crypto(ExecState*) const
415 {
416 return jsUndefined();
417 }
418
event(ExecState * exec) const419 JSValue JSDOMWindow::event(ExecState* exec) const
420 {
421 Event* event = currentEvent();
422 if (!event)
423 return jsUndefined();
424 return toJS(exec, event);
425 }
426
image(ExecState * exec) const427 JSValue JSDOMWindow::image(ExecState* exec) const
428 {
429 return getDOMConstructor<JSImageConstructor>(exec, this);
430 }
431
option(ExecState * exec) const432 JSValue JSDOMWindow::option(ExecState* exec) const
433 {
434 return getDOMConstructor<JSOptionConstructor>(exec, this);
435 }
436
437 #if ENABLE(VIDEO)
audio(ExecState * exec) const438 JSValue JSDOMWindow::audio(ExecState* exec) const
439 {
440 if (!MediaPlayer::isAvailable())
441 return jsUndefined();
442 return getDOMConstructor<JSAudioConstructor>(exec, this);
443 }
444 #endif
445
webKitPoint(ExecState * exec) const446 JSValue JSDOMWindow::webKitPoint(ExecState* exec) const
447 {
448 return getDOMConstructor<JSWebKitPointConstructor>(exec, this);
449 }
450
webKitCSSMatrix(ExecState * exec) const451 JSValue JSDOMWindow::webKitCSSMatrix(ExecState* exec) const
452 {
453 return getDOMConstructor<JSWebKitCSSMatrixConstructor>(exec, this);
454 }
455
xmlHttpRequest(ExecState * exec) const456 JSValue JSDOMWindow::xmlHttpRequest(ExecState* exec) const
457 {
458 return getDOMConstructor<JSXMLHttpRequestConstructor>(exec, this);
459 }
460
461 #if ENABLE(XSLT)
xsltProcessor(ExecState * exec) const462 JSValue JSDOMWindow::xsltProcessor(ExecState* exec) const
463 {
464 return getDOMConstructor<JSXSLTProcessorConstructor>(exec, this);
465 }
466 #endif
467
468 #if ENABLE(CHANNEL_MESSAGING)
messageChannel(ExecState * exec) const469 JSValue JSDOMWindow::messageChannel(ExecState* exec) const
470 {
471 return getDOMConstructor<JSMessageChannelConstructor>(exec, this);
472 }
473 #endif
474
475 #if ENABLE(WORKERS)
worker(ExecState * exec) const476 JSValue JSDOMWindow::worker(ExecState* exec) const
477 {
478 return getDOMConstructor<JSWorkerConstructor>(exec, this);
479 }
480 #endif
481
482 #if ENABLE(SHARED_WORKERS)
sharedWorker(ExecState * exec) const483 JSValue JSDOMWindow::sharedWorker(ExecState* exec) const
484 {
485 return getDOMConstructor<JSSharedWorkerConstructor>(exec, this);
486 }
487 #endif
488
489 // Custom functions
490
491 // Helper for window.open() and window.showModalDialog()
createWindow(ExecState * exec,Frame * lexicalFrame,Frame * dynamicFrame,Frame * openerFrame,const String & url,const String & frameName,const WindowFeatures & windowFeatures,JSValue dialogArgs)492 static Frame* createWindow(ExecState* exec, Frame* lexicalFrame, Frame* dynamicFrame,
493 Frame* openerFrame, const String& url, const String& frameName,
494 const WindowFeatures& windowFeatures, JSValue dialogArgs)
495 {
496 ASSERT(lexicalFrame);
497 ASSERT(dynamicFrame);
498
499 ResourceRequest request;
500
501 // For whatever reason, Firefox uses the dynamicGlobalObject to determine
502 // the outgoingReferrer. We replicate that behavior here.
503 String referrer = dynamicFrame->loader()->outgoingReferrer();
504 request.setHTTPReferrer(referrer);
505 FrameLoader::addHTTPOriginIfNeeded(request, dynamicFrame->loader()->outgoingOrigin());
506 FrameLoadRequest frameRequest(request, frameName);
507
508 // FIXME: It's much better for client API if a new window starts with a URL, here where we
509 // know what URL we are going to open. Unfortunately, this code passes the empty string
510 // for the URL, but there's a reason for that. Before loading we have to set up the opener,
511 // openedByDOM, and dialogArguments values. Also, to decide whether to use the URL we currently
512 // do an allowsAccessFrom call using the window we create, which can't be done before creating it.
513 // We'd have to resolve all those issues to pass the URL instead of "".
514
515 bool created;
516 // We pass in the opener frame here so it can be used for looking up the frame name, in case the active frame
517 // is different from the opener frame, and the name references a frame relative to the opener frame, for example
518 // "_self" or "_parent".
519 Frame* newFrame = lexicalFrame->loader()->createWindow(openerFrame->loader(), frameRequest, windowFeatures, created);
520 if (!newFrame)
521 return 0;
522
523 newFrame->loader()->setOpener(openerFrame);
524 newFrame->loader()->setOpenedByDOM();
525
526 JSDOMWindow* newWindow = toJSDOMWindow(newFrame);
527
528 if (dialogArgs)
529 newWindow->putDirect(Identifier(exec, "dialogArguments"), dialogArgs);
530
531 if (!protocolIsJavaScript(url) || newWindow->allowsAccessFrom(exec)) {
532 KURL completedURL = url.isEmpty() ? KURL("") : completeURL(exec, url);
533 bool userGesture = processingUserGesture(exec);
534
535 if (created)
536 newFrame->loader()->changeLocation(completedURL, referrer, false, false, userGesture);
537 else if (!url.isEmpty())
538 newFrame->loader()->scheduleLocationChange(completedURL.string(), referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
539 }
540
541 return newFrame;
542 }
543
open(ExecState * exec,const ArgList & args)544 JSValue JSDOMWindow::open(ExecState* exec, const ArgList& args)
545 {
546 Frame* frame = impl()->frame();
547 if (!frame)
548 return jsUndefined();
549 Frame* lexicalFrame = toLexicalFrame(exec);
550 if (!lexicalFrame)
551 return jsUndefined();
552 Frame* dynamicFrame = toDynamicFrame(exec);
553 if (!dynamicFrame)
554 return jsUndefined();
555
556 Page* page = frame->page();
557
558 String urlString = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
559 AtomicString frameName = args.at(1).isUndefinedOrNull() ? "_blank" : AtomicString(args.at(1).toString(exec));
560
561 // Because FrameTree::find() returns true for empty strings, we must check for empty framenames.
562 // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker.
563 if (!DOMWindow::allowPopUp(dynamicFrame) && (frameName.isEmpty() || !frame->tree()->find(frameName)))
564 return jsUndefined();
565
566 // Get the target frame for the special cases of _top and _parent. In those
567 // cases, we can schedule a location change right now and return early.
568 bool topOrParent = false;
569 if (frameName == "_top") {
570 frame = frame->tree()->top();
571 topOrParent = true;
572 } else if (frameName == "_parent") {
573 if (Frame* parent = frame->tree()->parent())
574 frame = parent;
575 topOrParent = true;
576 }
577 if (topOrParent) {
578 if (!shouldAllowNavigation(exec, frame))
579 return jsUndefined();
580
581 String completedURL;
582 if (!urlString.isEmpty())
583 completedURL = completeURL(exec, urlString).string();
584
585 const JSDOMWindow* targetedWindow = toJSDOMWindow(frame);
586 if (!completedURL.isEmpty() && (!protocolIsJavaScript(completedURL) || (targetedWindow && targetedWindow->allowsAccessFrom(exec)))) {
587 bool userGesture = processingUserGesture(exec);
588
589 // For whatever reason, Firefox uses the dynamicGlobalObject to
590 // determine the outgoingReferrer. We replicate that behavior
591 // here.
592 String referrer = dynamicFrame->loader()->outgoingReferrer();
593
594 frame->loader()->scheduleLocationChange(completedURL, referrer, !lexicalFrame->script()->anyPageIsProcessingUserGesture(), false, userGesture);
595 }
596 return toJS(exec, frame->domWindow());
597 }
598
599 // In the case of a named frame or a new window, we'll use the createWindow() helper
600 WindowFeatures windowFeatures(valueToStringWithUndefinedOrNullCheck(exec, args.at(2)));
601 FloatRect windowRect(windowFeatures.xSet ? windowFeatures.x : 0, windowFeatures.ySet ? windowFeatures.y : 0,
602 windowFeatures.widthSet ? windowFeatures.width : 0, windowFeatures.heightSet ? windowFeatures.height : 0);
603 DOMWindow::adjustWindowRect(screenAvailableRect(page ? page->mainFrame()->view() : 0), windowRect, windowRect);
604
605 windowFeatures.x = windowRect.x();
606 windowFeatures.y = windowRect.y();
607 windowFeatures.height = windowRect.height();
608 windowFeatures.width = windowRect.width();
609
610 frame = createWindow(exec, lexicalFrame, dynamicFrame, frame, urlString, frameName, windowFeatures, JSValue());
611
612 if (!frame)
613 return jsUndefined();
614
615 return toJS(exec, frame->domWindow());
616 }
617
showModalDialog(ExecState * exec,const ArgList & args)618 JSValue JSDOMWindow::showModalDialog(ExecState* exec, const ArgList& args)
619 {
620 Frame* frame = impl()->frame();
621 if (!frame)
622 return jsUndefined();
623 Frame* lexicalFrame = toLexicalFrame(exec);
624 if (!lexicalFrame)
625 return jsUndefined();
626 Frame* dynamicFrame = toDynamicFrame(exec);
627 if (!dynamicFrame)
628 return jsUndefined();
629
630 if (!DOMWindow::canShowModalDialogNow(frame) || !DOMWindow::allowPopUp(dynamicFrame))
631 return jsUndefined();
632
633 String url = valueToStringWithUndefinedOrNullCheck(exec, args.at(0));
634 JSValue dialogArgs = args.at(1);
635 String featureArgs = valueToStringWithUndefinedOrNullCheck(exec, args.at(2));
636
637 HashMap<String, String> features;
638 DOMWindow::parseModalDialogFeatures(featureArgs, features);
639
640 const bool trusted = false;
641
642 // The following features from Microsoft's documentation are not implemented:
643 // - default font settings
644 // - width, height, left, and top specified in units other than "px"
645 // - edge (sunken or raised, default is raised)
646 // - dialogHide: trusted && boolFeature(features, "dialoghide"), makes dialog hide when you print
647 // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?)
648 // - unadorned: trusted && boolFeature(features, "unadorned");
649
650 FloatRect screenRect = screenAvailableRect(frame->view());
651
652 WindowFeatures wargs;
653 wargs.width = WindowFeatures::floatFeature(features, "dialogwidth", 100, screenRect.width(), 620); // default here came from frame size of dialog in MacIE
654 wargs.widthSet = true;
655 wargs.height = WindowFeatures::floatFeature(features, "dialogheight", 100, screenRect.height(), 450); // default here came from frame size of dialog in MacIE
656 wargs.heightSet = true;
657
658 wargs.x = WindowFeatures::floatFeature(features, "dialogleft", screenRect.x(), screenRect.right() - wargs.width, -1);
659 wargs.xSet = wargs.x > 0;
660 wargs.y = WindowFeatures::floatFeature(features, "dialogtop", screenRect.y(), screenRect.bottom() - wargs.height, -1);
661 wargs.ySet = wargs.y > 0;
662
663 if (WindowFeatures::boolFeature(features, "center", true)) {
664 if (!wargs.xSet) {
665 wargs.x = screenRect.x() + (screenRect.width() - wargs.width) / 2;
666 wargs.xSet = true;
667 }
668 if (!wargs.ySet) {
669 wargs.y = screenRect.y() + (screenRect.height() - wargs.height) / 2;
670 wargs.ySet = true;
671 }
672 }
673
674 wargs.dialog = true;
675 wargs.resizable = WindowFeatures::boolFeature(features, "resizable");
676 wargs.scrollbarsVisible = WindowFeatures::boolFeature(features, "scroll", true);
677 wargs.statusBarVisible = WindowFeatures::boolFeature(features, "status", !trusted);
678 wargs.menuBarVisible = false;
679 wargs.toolBarVisible = false;
680 wargs.locationBarVisible = false;
681 wargs.fullscreen = false;
682
683 Frame* dialogFrame = createWindow(exec, lexicalFrame, dynamicFrame, frame, url, "", wargs, dialogArgs);
684 if (!dialogFrame)
685 return jsUndefined();
686
687 JSDOMWindow* dialogWindow = toJSDOMWindow(dialogFrame);
688 dialogFrame->page()->chrome()->runModal();
689
690 return dialogWindow->getDirect(Identifier(exec, "returnValue"));
691 }
692
postMessage(ExecState * exec,const ArgList & args)693 JSValue JSDOMWindow::postMessage(ExecState* exec, const ArgList& args)
694 {
695 DOMWindow* window = impl();
696
697 DOMWindow* source = asJSDOMWindow(exec->lexicalGlobalObject())->impl();
698 String message = args.at(0).toString(exec);
699
700 if (exec->hadException())
701 return jsUndefined();
702
703 MessagePort* messagePort = (args.size() == 2) ? 0 : toMessagePort(args.at(1));
704
705 String targetOrigin = valueToStringWithUndefinedOrNullCheck(exec, args.at((args.size() == 2) ? 1 : 2));
706 if (exec->hadException())
707 return jsUndefined();
708
709 ExceptionCode ec = 0;
710 window->postMessage(message, messagePort, targetOrigin, source, ec);
711 setDOMException(exec, ec);
712
713 return jsUndefined();
714 }
715
setTimeout(ExecState * exec,const ArgList & args)716 JSValue JSDOMWindow::setTimeout(ExecState* exec, const ArgList& args)
717 {
718 ScheduledAction* action = ScheduledAction::create(exec, args);
719 if (exec->hadException())
720 return jsUndefined();
721 int delay = args.at(1).toInt32(exec);
722 return jsNumber(exec, impl()->setTimeout(action, delay));
723 }
724
setInterval(ExecState * exec,const ArgList & args)725 JSValue JSDOMWindow::setInterval(ExecState* exec, const ArgList& args)
726 {
727 ScheduledAction* action = ScheduledAction::create(exec, args);
728 if (exec->hadException())
729 return jsUndefined();
730 int delay = args.at(1).toInt32(exec);
731 return jsNumber(exec, impl()->setInterval(action, delay));
732 }
733
atob(ExecState * exec,const ArgList & args)734 JSValue JSDOMWindow::atob(ExecState* exec, const ArgList& args)
735 {
736 if (args.size() < 1)
737 return throwError(exec, SyntaxError, "Not enough arguments");
738
739 JSValue v = args.at(0);
740 if (v.isNull())
741 return jsEmptyString(exec);
742
743 UString s = v.toString(exec);
744 if (!s.is8Bit()) {
745 setDOMException(exec, INVALID_CHARACTER_ERR);
746 return jsUndefined();
747 }
748
749 Vector<char> in(s.size());
750 for (int i = 0; i < s.size(); ++i)
751 in[i] = static_cast<char>(s.data()[i]);
752 Vector<char> out;
753
754 if (!base64Decode(in, out))
755 return throwError(exec, GeneralError, "Cannot decode base64");
756
757 return jsString(exec, String(out.data(), out.size()));
758 }
759
btoa(ExecState * exec,const ArgList & args)760 JSValue JSDOMWindow::btoa(ExecState* exec, const ArgList& args)
761 {
762 if (args.size() < 1)
763 return throwError(exec, SyntaxError, "Not enough arguments");
764
765 JSValue v = args.at(0);
766 if (v.isNull())
767 return jsEmptyString(exec);
768
769 UString s = v.toString(exec);
770 if (!s.is8Bit()) {
771 setDOMException(exec, INVALID_CHARACTER_ERR);
772 return jsUndefined();
773 }
774
775 Vector<char> in(s.size());
776 for (int i = 0; i < s.size(); ++i)
777 in[i] = static_cast<char>(s.data()[i]);
778 Vector<char> out;
779
780 base64Encode(in, out);
781
782 return jsString(exec, String(out.data(), out.size()));
783 }
784
addEventListener(ExecState * exec,const ArgList & args)785 JSValue JSDOMWindow::addEventListener(ExecState* exec, const ArgList& args)
786 {
787 Frame* frame = impl()->frame();
788 if (!frame)
789 return jsUndefined();
790
791 if (RefPtr<JSEventListener> listener = findOrCreateJSEventListener(args.at(1)))
792 impl()->addEventListener(AtomicString(args.at(0).toString(exec)), listener.release(), args.at(2).toBoolean(exec));
793
794 return jsUndefined();
795 }
796
removeEventListener(ExecState * exec,const ArgList & args)797 JSValue JSDOMWindow::removeEventListener(ExecState* exec, const ArgList& args)
798 {
799 Frame* frame = impl()->frame();
800 if (!frame)
801 return jsUndefined();
802
803 if (JSEventListener* listener = findJSEventListener(args.at(1)))
804 impl()->removeEventListener(AtomicString(args.at(0).toString(exec)), listener, args.at(2).toBoolean(exec));
805
806 return jsUndefined();
807 }
808
toDOMWindow(JSValue value)809 DOMWindow* toDOMWindow(JSValue value)
810 {
811 if (!value.isObject())
812 return 0;
813 JSObject* object = asObject(value);
814 if (object->inherits(&JSDOMWindow::s_info))
815 return static_cast<JSDOMWindow*>(object)->impl();
816 if (object->inherits(&JSDOMWindowShell::s_info))
817 return static_cast<JSDOMWindowShell*>(object)->impl();
818 return 0;
819 }
820
821 } // namespace WebCore
822