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