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