1/*
2 * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebFrame.h"
28
29#include "DownloadManager.h"
30#include "InjectedBundleNodeHandle.h"
31#include "InjectedBundleRangeHandle.h"
32#include "InjectedBundleScriptWorld.h"
33#include "WebChromeClient.h"
34#include "WebPage.h"
35#include "WebPageProxyMessages.h"
36#include "WebProcess.h"
37#include <JavaScriptCore/APICast.h>
38#include <JavaScriptCore/JSContextRef.h>
39#include <JavaScriptCore/JSLock.h>
40#include <JavaScriptCore/JSValueRef.h>
41#include <WebCore/AnimationController.h>
42#include <WebCore/ArchiveResource.h>
43#include <WebCore/CSSComputedStyleDeclaration.h>
44#include <WebCore/Chrome.h>
45#include <WebCore/DocumentLoader.h>
46#include <WebCore/Frame.h>
47#include <WebCore/FrameView.h>
48#include <WebCore/HTMLFrameOwnerElement.h>
49#include <WebCore/JSCSSStyleDeclaration.h>
50#include <WebCore/JSElement.h>
51#include <WebCore/JSRange.h>
52#include <WebCore/Page.h>
53#include <WebCore/RenderTreeAsText.h>
54#include <WebCore/TextIterator.h>
55#include <WebCore/TextResourceDecoder.h>
56#include <wtf/text/StringBuilder.h>
57
58#ifndef NDEBUG
59#include <wtf/RefCountedLeakCounter.h>
60#endif
61
62using namespace JSC;
63using namespace WebCore;
64
65namespace WebKit {
66
67#ifndef NDEBUG
68static WTF::RefCountedLeakCounter webFrameCounter("WebFrame");
69#endif
70
71static uint64_t generateFrameID()
72{
73    static uint64_t uniqueFrameID = 1;
74    return uniqueFrameID++;
75}
76
77static uint64_t generateListenerID()
78{
79    static uint64_t uniqueListenerID = 1;
80    return uniqueListenerID++;
81}
82
83PassRefPtr<WebFrame> WebFrame::createMainFrame(WebPage* page)
84{
85    RefPtr<WebFrame> frame = create();
86
87    page->send(Messages::WebPageProxy::DidCreateMainFrame(frame->frameID()));
88
89    frame->init(page, String(), 0);
90
91    return frame.release();
92}
93
94PassRefPtr<WebFrame> WebFrame::createSubframe(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement)
95{
96    RefPtr<WebFrame> frame = create();
97
98    WebFrame* parentFrame = static_cast<WebFrameLoaderClient*>(ownerElement->document()->frame()->loader()->client())->webFrame();
99    page->send(Messages::WebPageProxy::DidCreateSubframe(frame->frameID(), parentFrame->frameID()));
100
101    frame->init(page, frameName, ownerElement);
102
103    return frame.release();
104}
105
106PassRefPtr<WebFrame> WebFrame::create()
107{
108    RefPtr<WebFrame> frame = adoptRef(new WebFrame);
109
110    // Add explict ref() that will be balanced in WebFrameLoaderClient::frameLoaderDestroyed().
111    frame->ref();
112
113    return frame.release();
114}
115
116WebFrame::WebFrame()
117    : m_coreFrame(0)
118    , m_policyListenerID(0)
119    , m_policyFunction(0)
120    , m_policyDownloadID(0)
121    , m_frameLoaderClient(this)
122    , m_loadListener(0)
123    , m_frameID(generateFrameID())
124{
125    WebProcess::shared().addWebFrame(m_frameID, this);
126
127#ifndef NDEBUG
128    webFrameCounter.increment();
129#endif
130}
131
132WebFrame::~WebFrame()
133{
134    ASSERT(!m_coreFrame);
135
136#ifndef NDEBUG
137    webFrameCounter.decrement();
138#endif
139}
140
141void WebFrame::init(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement)
142{
143    RefPtr<Frame> frame = Frame::create(page->corePage(), ownerElement, &m_frameLoaderClient);
144    m_coreFrame = frame.get();
145
146    frame->tree()->setName(frameName);
147
148    if (ownerElement) {
149        ASSERT(ownerElement->document()->frame());
150        ownerElement->document()->frame()->tree()->appendChild(frame);
151    }
152
153    frame->init();
154}
155
156WebPage* WebFrame::page() const
157{
158    if (!m_coreFrame)
159        return 0;
160
161    if (WebCore::Page* page = m_coreFrame->page())
162        return static_cast<WebChromeClient*>(page->chrome()->client())->page();
163
164    return 0;
165}
166
167void WebFrame::invalidate()
168{
169    WebProcess::shared().removeWebFrame(m_frameID);
170    m_coreFrame = 0;
171}
172
173uint64_t WebFrame::setUpPolicyListener(WebCore::FramePolicyFunction policyFunction)
174{
175    // FIXME: <rdar://5634381> We need to support multiple active policy listeners.
176
177    invalidatePolicyListener();
178
179    m_policyListenerID = generateListenerID();
180    m_policyFunction = policyFunction;
181    return m_policyListenerID;
182}
183
184void WebFrame::invalidatePolicyListener()
185{
186    if (!m_policyListenerID)
187        return;
188
189    m_policyDownloadID = 0;
190    m_policyListenerID = 0;
191    m_policyFunction = 0;
192}
193
194void WebFrame::didReceivePolicyDecision(uint64_t listenerID, PolicyAction action, uint64_t downloadID)
195{
196    if (!m_coreFrame)
197        return;
198
199    if (!m_policyListenerID)
200        return;
201
202    if (listenerID != m_policyListenerID)
203        return;
204
205    ASSERT(m_policyFunction);
206
207    FramePolicyFunction function = m_policyFunction;
208
209    invalidatePolicyListener();
210
211    m_policyDownloadID = downloadID;
212
213    (m_coreFrame->loader()->policyChecker()->*function)(action);
214}
215
216void WebFrame::startDownload(const WebCore::ResourceRequest& request)
217{
218    ASSERT(m_policyDownloadID);
219
220    DownloadManager::shared().startDownload(m_policyDownloadID, page(), request);
221
222    m_policyDownloadID = 0;
223}
224
225void WebFrame::convertHandleToDownload(ResourceHandle* handle, const ResourceRequest& request, const ResourceRequest& initialRequest, const ResourceResponse& response)
226{
227    ASSERT(m_policyDownloadID);
228
229    DownloadManager::shared().convertHandleToDownload(m_policyDownloadID, page(), handle, request, initialRequest, response);
230    m_policyDownloadID = 0;
231}
232
233String WebFrame::source() const
234{
235    if (!m_coreFrame)
236        return String();
237    Document* document = m_coreFrame->document();
238    if (!document)
239        return String();
240    TextResourceDecoder* decoder = document->decoder();
241    if (!decoder)
242        return String();
243    DocumentLoader* documentLoader = m_coreFrame->loader()->activeDocumentLoader();
244    if (!documentLoader)
245        return String();
246    RefPtr<SharedBuffer> mainResourceData = documentLoader->mainResourceData();
247    if (!mainResourceData)
248        return String();
249    return decoder->encoding().decode(mainResourceData->data(), mainResourceData->size());
250}
251
252String WebFrame::contentsAsString() const
253{
254    if (!m_coreFrame)
255        return String();
256
257    if (isFrameSet()) {
258        StringBuilder builder;
259        for (Frame* child = m_coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
260            if (!builder.isEmpty())
261                builder.append(' ');
262            builder.append(static_cast<WebFrameLoaderClient*>(child->loader()->client())->webFrame()->contentsAsString());
263        }
264        // FIXME: It may make sense to use toStringPreserveCapacity() here.
265        return builder.toString();
266    }
267
268    Document* document = m_coreFrame->document();
269    if (!document)
270        return String();
271
272    RefPtr<Element> documentElement = document->documentElement();
273    if (!documentElement)
274        return String();
275
276    RefPtr<Range> range = document->createRange();
277
278    ExceptionCode ec = 0;
279    range->selectNode(documentElement.get(), ec);
280    if (ec)
281        return String();
282
283    return plainText(range.get());
284}
285
286String WebFrame::selectionAsString() const
287{
288    if (!m_coreFrame)
289        return String();
290
291    return m_coreFrame->displayStringModifiedByEncoding(m_coreFrame->editor()->selectedText());
292}
293
294IntSize WebFrame::size() const
295{
296    if (!m_coreFrame)
297        return IntSize();
298
299    FrameView* frameView = m_coreFrame->view();
300    if (!frameView)
301        return IntSize();
302
303    return frameView->contentsSize();
304}
305
306bool WebFrame::isFrameSet() const
307{
308    if (!m_coreFrame)
309        return false;
310
311    Document* document = m_coreFrame->document();
312    if (!document)
313        return false;
314    return document->isFrameSet();
315}
316
317bool WebFrame::isMainFrame() const
318{
319    if (WebPage* p = page())
320        return p->mainFrame() == this;
321
322    return false;
323}
324
325String WebFrame::name() const
326{
327    if (!m_coreFrame)
328        return String();
329
330    return m_coreFrame->tree()->uniqueName();
331}
332
333String WebFrame::url() const
334{
335    if (!m_coreFrame)
336        return String();
337
338    DocumentLoader* documentLoader = m_coreFrame->loader()->documentLoader();
339    if (!documentLoader)
340        return String();
341
342    return documentLoader->url().string();
343}
344
345String WebFrame::innerText() const
346{
347    if (!m_coreFrame)
348        return String();
349
350    if (!m_coreFrame->document()->documentElement())
351        return String();
352
353    return m_coreFrame->document()->documentElement()->innerText();
354}
355
356PassRefPtr<ImmutableArray> WebFrame::childFrames()
357{
358    if (!m_coreFrame)
359        return ImmutableArray::create();
360
361    size_t size = m_coreFrame->tree()->childCount();
362    if (!size)
363        return ImmutableArray::create();
364
365    Vector<RefPtr<APIObject> > vector;
366    vector.reserveInitialCapacity(size);
367
368    for (Frame* child = m_coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
369        WebFrame* webFrame = static_cast<WebFrameLoaderClient*>(child->loader()->client())->webFrame();
370        vector.uncheckedAppend(webFrame);
371    }
372
373    return ImmutableArray::adopt(vector);
374}
375
376unsigned WebFrame::numberOfActiveAnimations() const
377{
378    if (!m_coreFrame)
379        return 0;
380
381    AnimationController* controller = m_coreFrame->animation();
382    if (!controller)
383        return 0;
384
385    return controller->numberOfActiveAnimations();
386}
387
388bool WebFrame::pauseAnimationOnElementWithId(const String& animationName, const String& elementID, double time)
389{
390    if (!m_coreFrame)
391        return false;
392
393    AnimationController* controller = m_coreFrame->animation();
394    if (!controller)
395        return false;
396
397    if (!m_coreFrame->document())
398        return false;
399
400    Node* coreNode = m_coreFrame->document()->getElementById(elementID);
401    if (!coreNode || !coreNode->renderer())
402        return false;
403
404    return controller->pauseAnimationAtTime(coreNode->renderer(), animationName, time);
405}
406
407void WebFrame::suspendAnimations()
408{
409    if (!m_coreFrame)
410        return;
411
412    AnimationController* controller = m_coreFrame->animation();
413    if (!controller)
414        return;
415
416    controller->suspendAnimations();
417}
418
419void WebFrame::resumeAnimations()
420{
421    if (!m_coreFrame)
422        return;
423
424    AnimationController* controller = m_coreFrame->animation();
425    if (!controller)
426        return;
427
428    controller->resumeAnimations();
429}
430
431String WebFrame::layerTreeAsText() const
432{
433    if (!m_coreFrame)
434        return "";
435
436    return m_coreFrame->layerTreeAsText();
437}
438
439unsigned WebFrame::pendingUnloadCount() const
440{
441    if (!m_coreFrame)
442        return 0;
443
444    return m_coreFrame->domWindow()->pendingUnloadEventListeners();
445}
446
447bool WebFrame::allowsFollowingLink(const WebCore::KURL& url) const
448{
449    if (!m_coreFrame)
450        return true;
451
452    return m_coreFrame->document()->securityOrigin()->canDisplay(url);
453}
454
455JSGlobalContextRef WebFrame::jsContext()
456{
457    return toGlobalRef(m_coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec());
458}
459
460JSGlobalContextRef WebFrame::jsContextForWorld(InjectedBundleScriptWorld* world)
461{
462    return toGlobalRef(m_coreFrame->script()->globalObject(world->coreWorld())->globalExec());
463}
464
465IntRect WebFrame::contentBounds() const
466{
467    if (!m_coreFrame)
468        return IntRect();
469
470    FrameView* view = m_coreFrame->view();
471    if (!view)
472        return IntRect();
473
474    return IntRect(0, 0, view->contentsWidth(), view->contentsHeight());
475}
476
477IntRect WebFrame::visibleContentBounds() const
478{
479    if (!m_coreFrame)
480        return IntRect();
481
482    FrameView* view = m_coreFrame->view();
483    if (!view)
484        return IntRect();
485
486    IntRect contentRect = view->visibleContentRect(true);
487    return IntRect(0, 0, contentRect.width(), contentRect.height());
488}
489
490IntRect WebFrame::visibleContentBoundsExcludingScrollbars() const
491{
492    if (!m_coreFrame)
493        return IntRect();
494
495    FrameView* view = m_coreFrame->view();
496    if (!view)
497        return IntRect();
498
499    IntRect contentRect = view->visibleContentRect(false);
500    return IntRect(0, 0, contentRect.width(), contentRect.height());
501}
502
503IntSize WebFrame::scrollOffset() const
504{
505    if (!m_coreFrame)
506        return IntSize();
507
508    FrameView* view = m_coreFrame->view();
509    if (!view)
510        return IntSize();
511
512    return view->scrollOffset();
513}
514
515bool WebFrame::hasHorizontalScrollbar() const
516{
517    if (!m_coreFrame)
518        return false;
519
520    FrameView* view = m_coreFrame->view();
521    if (!view)
522        return false;
523
524    return view->horizontalScrollbar();
525}
526
527bool WebFrame::hasVerticalScrollbar() const
528{
529    if (!m_coreFrame)
530        return false;
531
532    FrameView* view = m_coreFrame->view();
533    if (!view)
534        return false;
535
536    return view->verticalScrollbar();
537}
538
539bool WebFrame::getDocumentBackgroundColor(double* red, double* green, double* blue, double* alpha)
540{
541    if (!m_coreFrame)
542        return false;
543    Document* document = m_coreFrame->document();
544    if (!document)
545        return false;
546
547    Element* rootElementToUse = document->body();
548    if (!rootElementToUse)
549        rootElementToUse = document->documentElement();
550    if (!rootElementToUse)
551        return false;
552
553    RenderObject* renderer = rootElementToUse->renderer();
554    if (!renderer)
555        return false;
556    Color color = renderer->style()->visitedDependentColor(CSSPropertyBackgroundColor);
557    if (!color.isValid())
558        return false;
559
560    color.getRGBA(*red, *green, *blue, *alpha);
561    return true;
562}
563
564WebFrame* WebFrame::frameForContext(JSContextRef context)
565{
566    JSObjectRef globalObjectRef = JSContextGetGlobalObject(context);
567    JSC::JSObject* globalObjectObj = toJS(globalObjectRef);
568    if (strcmp(globalObjectObj->classInfo()->className, "JSDOMWindowShell") != 0)
569        return 0;
570
571    Frame* coreFrame = static_cast<JSDOMWindowShell*>(globalObjectObj)->window()->impl()->frame();
572    return static_cast<WebFrameLoaderClient*>(coreFrame->loader()->client())->webFrame();
573}
574
575JSValueRef WebFrame::jsWrapperForWorld(InjectedBundleNodeHandle* nodeHandle, InjectedBundleScriptWorld* world)
576{
577    if (!m_coreFrame)
578        return 0;
579
580    JSDOMWindow* globalObject = m_coreFrame->script()->globalObject(world->coreWorld());
581    ExecState* exec = globalObject->globalExec();
582
583    JSLock lock(SilenceAssertionsOnly);
584    return toRef(exec, toJS(exec, globalObject, nodeHandle->coreNode()));
585}
586
587JSValueRef WebFrame::jsWrapperForWorld(InjectedBundleRangeHandle* rangeHandle, InjectedBundleScriptWorld* world)
588{
589    if (!m_coreFrame)
590        return 0;
591
592    JSDOMWindow* globalObject = m_coreFrame->script()->globalObject(world->coreWorld());
593    ExecState* exec = globalObject->globalExec();
594
595    JSLock lock(SilenceAssertionsOnly);
596    return toRef(exec, toJS(exec, globalObject, rangeHandle->coreRange()));
597}
598
599JSValueRef WebFrame::computedStyleIncludingVisitedInfo(JSObjectRef element)
600{
601    if (!m_coreFrame)
602        return 0;
603
604    JSDOMWindow* globalObject = m_coreFrame->script()->globalObject(mainThreadNormalWorld());
605    ExecState* exec = globalObject->globalExec();
606
607    if (!toJS(element)->inherits(&JSElement::s_info))
608        return JSValueMakeUndefined(toRef(exec));
609
610    RefPtr<CSSComputedStyleDeclaration> style = computedStyle(static_cast<JSElement*>(toJS(element))->impl(), true);
611
612    JSLock lock(SilenceAssertionsOnly);
613    return toRef(exec, toJS(exec, globalObject, style.get()));
614}
615
616String WebFrame::counterValue(JSObjectRef element)
617{
618    if (!toJS(element)->inherits(&JSElement::s_info))
619        return String();
620
621    return counterValueForElement(static_cast<JSElement*>(toJS(element))->impl());
622}
623
624String WebFrame::markerText(JSObjectRef element)
625{
626    if (!toJS(element)->inherits(&JSElement::s_info))
627        return String();
628
629    return markerTextForListItem(static_cast<JSElement*>(toJS(element))->impl());
630}
631
632String WebFrame::provisionalURL() const
633{
634    if (!m_coreFrame)
635        return String();
636
637    return m_coreFrame->loader()->provisionalDocumentLoader()->url().string();
638}
639
640String WebFrame::suggestedFilenameForResourceWithURL(const KURL& url) const
641{
642    if (!m_coreFrame)
643        return String();
644
645    DocumentLoader* loader = m_coreFrame->loader()->documentLoader();
646    if (!loader)
647        return String();
648
649    // First, try the main resource.
650    if (loader->url() == url)
651        return loader->response().suggestedFilename();
652
653    // Next, try subresources.
654    RefPtr<ArchiveResource> resource = loader->subresource(url);
655    if (!resource)
656        return String();
657
658    return resource->response().suggestedFilename();
659}
660
661String WebFrame::mimeTypeForResourceWithURL(const KURL& url) const
662{
663    if (!m_coreFrame)
664        return String();
665
666    DocumentLoader* loader = m_coreFrame->loader()->documentLoader();
667    if (!loader)
668        return String();
669
670    // First, try the main resource.
671    if (loader->url() == url)
672        return loader->response().mimeType();
673
674    // Next, try subresources.
675    RefPtr<ArchiveResource> resource = loader->subresource(url);
676    if (resource)
677        return resource->mimeType();
678
679    return page()->cachedResponseMIMETypeForURL(url);
680}
681
682} // namespace WebKit
683