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
62 using namespace JSC;
63 using namespace WebCore;
64
65 namespace WebKit {
66
67 #ifndef NDEBUG
68 static WTF::RefCountedLeakCounter webFrameCounter("WebFrame");
69 #endif
70
generateFrameID()71 static uint64_t generateFrameID()
72 {
73 static uint64_t uniqueFrameID = 1;
74 return uniqueFrameID++;
75 }
76
generateListenerID()77 static uint64_t generateListenerID()
78 {
79 static uint64_t uniqueListenerID = 1;
80 return uniqueListenerID++;
81 }
82
createMainFrame(WebPage * page)83 PassRefPtr<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
createSubframe(WebPage * page,const String & frameName,HTMLFrameOwnerElement * ownerElement)94 PassRefPtr<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
create()106 PassRefPtr<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
WebFrame()116 WebFrame::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
~WebFrame()132 WebFrame::~WebFrame()
133 {
134 ASSERT(!m_coreFrame);
135
136 #ifndef NDEBUG
137 webFrameCounter.decrement();
138 #endif
139 }
140
init(WebPage * page,const String & frameName,HTMLFrameOwnerElement * ownerElement)141 void 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
page() const156 WebPage* 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
invalidate()167 void WebFrame::invalidate()
168 {
169 WebProcess::shared().removeWebFrame(m_frameID);
170 m_coreFrame = 0;
171 }
172
setUpPolicyListener(WebCore::FramePolicyFunction policyFunction)173 uint64_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
invalidatePolicyListener()184 void WebFrame::invalidatePolicyListener()
185 {
186 if (!m_policyListenerID)
187 return;
188
189 m_policyDownloadID = 0;
190 m_policyListenerID = 0;
191 m_policyFunction = 0;
192 }
193
didReceivePolicyDecision(uint64_t listenerID,PolicyAction action,uint64_t downloadID)194 void 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
startDownload(const WebCore::ResourceRequest & request)216 void 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
convertHandleToDownload(ResourceHandle * handle,const ResourceRequest & request,const ResourceRequest & initialRequest,const ResourceResponse & response)225 void 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
source() const233 String 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
contentsAsString() const252 String 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
selectionAsString() const286 String WebFrame::selectionAsString() const
287 {
288 if (!m_coreFrame)
289 return String();
290
291 return m_coreFrame->displayStringModifiedByEncoding(m_coreFrame->editor()->selectedText());
292 }
293
size() const294 IntSize 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
isFrameSet() const306 bool 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
isMainFrame() const317 bool WebFrame::isMainFrame() const
318 {
319 if (WebPage* p = page())
320 return p->mainFrame() == this;
321
322 return false;
323 }
324
name() const325 String WebFrame::name() const
326 {
327 if (!m_coreFrame)
328 return String();
329
330 return m_coreFrame->tree()->uniqueName();
331 }
332
url() const333 String 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
innerText() const345 String 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
childFrames()356 PassRefPtr<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
numberOfActiveAnimations() const376 unsigned 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
pauseAnimationOnElementWithId(const String & animationName,const String & elementID,double time)388 bool 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
suspendAnimations()407 void 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
resumeAnimations()419 void 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
layerTreeAsText() const431 String WebFrame::layerTreeAsText() const
432 {
433 if (!m_coreFrame)
434 return "";
435
436 return m_coreFrame->layerTreeAsText();
437 }
438
pendingUnloadCount() const439 unsigned WebFrame::pendingUnloadCount() const
440 {
441 if (!m_coreFrame)
442 return 0;
443
444 return m_coreFrame->domWindow()->pendingUnloadEventListeners();
445 }
446
allowsFollowingLink(const WebCore::KURL & url) const447 bool 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
jsContext()455 JSGlobalContextRef WebFrame::jsContext()
456 {
457 return toGlobalRef(m_coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec());
458 }
459
jsContextForWorld(InjectedBundleScriptWorld * world)460 JSGlobalContextRef WebFrame::jsContextForWorld(InjectedBundleScriptWorld* world)
461 {
462 return toGlobalRef(m_coreFrame->script()->globalObject(world->coreWorld())->globalExec());
463 }
464
contentBounds() const465 IntRect 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
visibleContentBounds() const477 IntRect 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
visibleContentBoundsExcludingScrollbars() const490 IntRect 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
scrollOffset() const503 IntSize 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
hasHorizontalScrollbar() const515 bool 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
hasVerticalScrollbar() const527 bool 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
getDocumentBackgroundColor(double * red,double * green,double * blue,double * alpha)539 bool 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
frameForContext(JSContextRef context)564 WebFrame* 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
jsWrapperForWorld(InjectedBundleNodeHandle * nodeHandle,InjectedBundleScriptWorld * world)575 JSValueRef 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
jsWrapperForWorld(InjectedBundleRangeHandle * rangeHandle,InjectedBundleScriptWorld * world)587 JSValueRef 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
computedStyleIncludingVisitedInfo(JSObjectRef element)599 JSValueRef 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
counterValue(JSObjectRef element)616 String 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
markerText(JSObjectRef element)624 String 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
provisionalURL() const632 String WebFrame::provisionalURL() const
633 {
634 if (!m_coreFrame)
635 return String();
636
637 return m_coreFrame->loader()->provisionalDocumentLoader()->url().string();
638 }
639
suggestedFilenameForResourceWithURL(const KURL & url) const640 String 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
mimeTypeForResourceWithURL(const KURL & url) const661 String 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