• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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