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