/* * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WebFrame.h" #include "DownloadManager.h" #include "InjectedBundleNodeHandle.h" #include "InjectedBundleRangeHandle.h" #include "InjectedBundleScriptWorld.h" #include "WebChromeClient.h" #include "WebPage.h" #include "WebPageProxyMessages.h" #include "WebProcess.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NDEBUG #include #endif using namespace JSC; using namespace WebCore; namespace WebKit { #ifndef NDEBUG static WTF::RefCountedLeakCounter webFrameCounter("WebFrame"); #endif static uint64_t generateFrameID() { static uint64_t uniqueFrameID = 1; return uniqueFrameID++; } static uint64_t generateListenerID() { static uint64_t uniqueListenerID = 1; return uniqueListenerID++; } PassRefPtr WebFrame::createMainFrame(WebPage* page) { RefPtr frame = create(); page->send(Messages::WebPageProxy::DidCreateMainFrame(frame->frameID())); frame->init(page, String(), 0); return frame.release(); } PassRefPtr WebFrame::createSubframe(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement) { RefPtr frame = create(); WebFrame* parentFrame = static_cast(ownerElement->document()->frame()->loader()->client())->webFrame(); page->send(Messages::WebPageProxy::DidCreateSubframe(frame->frameID(), parentFrame->frameID())); frame->init(page, frameName, ownerElement); return frame.release(); } PassRefPtr WebFrame::create() { RefPtr frame = adoptRef(new WebFrame); // Add explict ref() that will be balanced in WebFrameLoaderClient::frameLoaderDestroyed(). frame->ref(); return frame.release(); } WebFrame::WebFrame() : m_coreFrame(0) , m_policyListenerID(0) , m_policyFunction(0) , m_policyDownloadID(0) , m_frameLoaderClient(this) , m_loadListener(0) , m_frameID(generateFrameID()) { WebProcess::shared().addWebFrame(m_frameID, this); #ifndef NDEBUG webFrameCounter.increment(); #endif } WebFrame::~WebFrame() { ASSERT(!m_coreFrame); #ifndef NDEBUG webFrameCounter.decrement(); #endif } void WebFrame::init(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement) { RefPtr frame = Frame::create(page->corePage(), ownerElement, &m_frameLoaderClient); m_coreFrame = frame.get(); frame->tree()->setName(frameName); if (ownerElement) { ASSERT(ownerElement->document()->frame()); ownerElement->document()->frame()->tree()->appendChild(frame); } frame->init(); } WebPage* WebFrame::page() const { if (!m_coreFrame) return 0; if (WebCore::Page* page = m_coreFrame->page()) return static_cast(page->chrome()->client())->page(); return 0; } void WebFrame::invalidate() { WebProcess::shared().removeWebFrame(m_frameID); m_coreFrame = 0; } uint64_t WebFrame::setUpPolicyListener(WebCore::FramePolicyFunction policyFunction) { // FIXME: We need to support multiple active policy listeners. invalidatePolicyListener(); m_policyListenerID = generateListenerID(); m_policyFunction = policyFunction; return m_policyListenerID; } void WebFrame::invalidatePolicyListener() { if (!m_policyListenerID) return; m_policyDownloadID = 0; m_policyListenerID = 0; m_policyFunction = 0; } void WebFrame::didReceivePolicyDecision(uint64_t listenerID, PolicyAction action, uint64_t downloadID) { if (!m_coreFrame) return; if (!m_policyListenerID) return; if (listenerID != m_policyListenerID) return; ASSERT(m_policyFunction); FramePolicyFunction function = m_policyFunction; invalidatePolicyListener(); m_policyDownloadID = downloadID; (m_coreFrame->loader()->policyChecker()->*function)(action); } void WebFrame::startDownload(const WebCore::ResourceRequest& request) { ASSERT(m_policyDownloadID); DownloadManager::shared().startDownload(m_policyDownloadID, page(), request); m_policyDownloadID = 0; } void WebFrame::convertHandleToDownload(ResourceHandle* handle, const ResourceRequest& request, const ResourceRequest& initialRequest, const ResourceResponse& response) { ASSERT(m_policyDownloadID); DownloadManager::shared().convertHandleToDownload(m_policyDownloadID, page(), handle, request, initialRequest, response); m_policyDownloadID = 0; } String WebFrame::source() const { if (!m_coreFrame) return String(); Document* document = m_coreFrame->document(); if (!document) return String(); TextResourceDecoder* decoder = document->decoder(); if (!decoder) return String(); DocumentLoader* documentLoader = m_coreFrame->loader()->activeDocumentLoader(); if (!documentLoader) return String(); RefPtr mainResourceData = documentLoader->mainResourceData(); if (!mainResourceData) return String(); return decoder->encoding().decode(mainResourceData->data(), mainResourceData->size()); } String WebFrame::contentsAsString() const { if (!m_coreFrame) return String(); if (isFrameSet()) { StringBuilder builder; for (Frame* child = m_coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { if (!builder.isEmpty()) builder.append(' '); builder.append(static_cast(child->loader()->client())->webFrame()->contentsAsString()); } // FIXME: It may make sense to use toStringPreserveCapacity() here. return builder.toString(); } Document* document = m_coreFrame->document(); if (!document) return String(); RefPtr documentElement = document->documentElement(); if (!documentElement) return String(); RefPtr range = document->createRange(); ExceptionCode ec = 0; range->selectNode(documentElement.get(), ec); if (ec) return String(); return plainText(range.get()); } String WebFrame::selectionAsString() const { if (!m_coreFrame) return String(); return m_coreFrame->displayStringModifiedByEncoding(m_coreFrame->editor()->selectedText()); } IntSize WebFrame::size() const { if (!m_coreFrame) return IntSize(); FrameView* frameView = m_coreFrame->view(); if (!frameView) return IntSize(); return frameView->contentsSize(); } bool WebFrame::isFrameSet() const { if (!m_coreFrame) return false; Document* document = m_coreFrame->document(); if (!document) return false; return document->isFrameSet(); } bool WebFrame::isMainFrame() const { if (WebPage* p = page()) return p->mainFrame() == this; return false; } String WebFrame::name() const { if (!m_coreFrame) return String(); return m_coreFrame->tree()->uniqueName(); } String WebFrame::url() const { if (!m_coreFrame) return String(); DocumentLoader* documentLoader = m_coreFrame->loader()->documentLoader(); if (!documentLoader) return String(); return documentLoader->url().string(); } String WebFrame::innerText() const { if (!m_coreFrame) return String(); if (!m_coreFrame->document()->documentElement()) return String(); return m_coreFrame->document()->documentElement()->innerText(); } PassRefPtr WebFrame::childFrames() { if (!m_coreFrame) return ImmutableArray::create(); size_t size = m_coreFrame->tree()->childCount(); if (!size) return ImmutableArray::create(); Vector > vector; vector.reserveInitialCapacity(size); for (Frame* child = m_coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { WebFrame* webFrame = static_cast(child->loader()->client())->webFrame(); vector.uncheckedAppend(webFrame); } return ImmutableArray::adopt(vector); } unsigned WebFrame::numberOfActiveAnimations() const { if (!m_coreFrame) return 0; AnimationController* controller = m_coreFrame->animation(); if (!controller) return 0; return controller->numberOfActiveAnimations(); } bool WebFrame::pauseAnimationOnElementWithId(const String& animationName, const String& elementID, double time) { if (!m_coreFrame) return false; AnimationController* controller = m_coreFrame->animation(); if (!controller) return false; if (!m_coreFrame->document()) return false; Node* coreNode = m_coreFrame->document()->getElementById(elementID); if (!coreNode || !coreNode->renderer()) return false; return controller->pauseAnimationAtTime(coreNode->renderer(), animationName, time); } void WebFrame::suspendAnimations() { if (!m_coreFrame) return; AnimationController* controller = m_coreFrame->animation(); if (!controller) return; controller->suspendAnimations(); } void WebFrame::resumeAnimations() { if (!m_coreFrame) return; AnimationController* controller = m_coreFrame->animation(); if (!controller) return; controller->resumeAnimations(); } String WebFrame::layerTreeAsText() const { if (!m_coreFrame) return ""; return m_coreFrame->layerTreeAsText(); } unsigned WebFrame::pendingUnloadCount() const { if (!m_coreFrame) return 0; return m_coreFrame->domWindow()->pendingUnloadEventListeners(); } bool WebFrame::allowsFollowingLink(const WebCore::KURL& url) const { if (!m_coreFrame) return true; return m_coreFrame->document()->securityOrigin()->canDisplay(url); } JSGlobalContextRef WebFrame::jsContext() { return toGlobalRef(m_coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec()); } JSGlobalContextRef WebFrame::jsContextForWorld(InjectedBundleScriptWorld* world) { return toGlobalRef(m_coreFrame->script()->globalObject(world->coreWorld())->globalExec()); } IntRect WebFrame::contentBounds() const { if (!m_coreFrame) return IntRect(); FrameView* view = m_coreFrame->view(); if (!view) return IntRect(); return IntRect(0, 0, view->contentsWidth(), view->contentsHeight()); } IntRect WebFrame::visibleContentBounds() const { if (!m_coreFrame) return IntRect(); FrameView* view = m_coreFrame->view(); if (!view) return IntRect(); IntRect contentRect = view->visibleContentRect(true); return IntRect(0, 0, contentRect.width(), contentRect.height()); } IntRect WebFrame::visibleContentBoundsExcludingScrollbars() const { if (!m_coreFrame) return IntRect(); FrameView* view = m_coreFrame->view(); if (!view) return IntRect(); IntRect contentRect = view->visibleContentRect(false); return IntRect(0, 0, contentRect.width(), contentRect.height()); } IntSize WebFrame::scrollOffset() const { if (!m_coreFrame) return IntSize(); FrameView* view = m_coreFrame->view(); if (!view) return IntSize(); return view->scrollOffset(); } bool WebFrame::getDocumentBackgroundColor(double* red, double* green, double* blue, double* alpha) { if (!m_coreFrame) return false; Document* document = m_coreFrame->document(); if (!document) return false; Element* rootElementToUse = document->body(); if (!rootElementToUse) rootElementToUse = document->documentElement(); if (!rootElementToUse) return false; RenderObject* renderer = rootElementToUse->renderer(); if (!renderer) return false; Color color = renderer->style()->visitedDependentColor(CSSPropertyBackgroundColor); if (!color.isValid()) return false; color.getRGBA(*red, *green, *blue, *alpha); return true; } WebFrame* WebFrame::frameForContext(JSContextRef context) { JSObjectRef globalObjectRef = JSContextGetGlobalObject(context); JSC::JSObject* globalObjectObj = toJS(globalObjectRef); if (strcmp(globalObjectObj->classInfo()->className, "JSDOMWindowShell") != 0) return 0; Frame* coreFrame = static_cast(globalObjectObj)->window()->impl()->frame(); return static_cast(coreFrame->loader()->client())->webFrame(); } JSValueRef WebFrame::jsWrapperForWorld(InjectedBundleNodeHandle* nodeHandle, InjectedBundleScriptWorld* world) { JSDOMWindow* globalObject = m_coreFrame->script()->globalObject(world->coreWorld()); ExecState* exec = globalObject->globalExec(); JSLock lock(SilenceAssertionsOnly); return toRef(exec, toJS(exec, globalObject, nodeHandle->coreNode())); } JSValueRef WebFrame::jsWrapperForWorld(InjectedBundleRangeHandle* rangeHandle, InjectedBundleScriptWorld* world) { JSDOMWindow* globalObject = m_coreFrame->script()->globalObject(world->coreWorld()); ExecState* exec = globalObject->globalExec(); JSLock lock(SilenceAssertionsOnly); return toRef(exec, toJS(exec, globalObject, rangeHandle->coreRange())); } JSValueRef WebFrame::computedStyleIncludingVisitedInfo(JSObjectRef element) { if (!m_coreFrame) return 0; JSDOMWindow* globalObject = m_coreFrame->script()->globalObject(mainThreadNormalWorld()); ExecState* exec = globalObject->globalExec(); if (!toJS(element)->inherits(&JSElement::s_info)) return JSValueMakeUndefined(toRef(exec)); RefPtr style = computedStyle(static_cast(toJS(element))->impl(), true); JSLock lock(SilenceAssertionsOnly); return toRef(exec, toJS(exec, globalObject, style.get())); } String WebFrame::counterValue(JSObjectRef element) { if (!toJS(element)->inherits(&JSElement::s_info)) return String(); return counterValueForElement(static_cast(toJS(element))->impl()); } String WebFrame::markerText(JSObjectRef element) { if (!toJS(element)->inherits(&JSElement::s_info)) return String(); return markerTextForListItem(static_cast(toJS(element))->impl()); } String WebFrame::provisionalURL() const { if (!m_coreFrame) return String(); return m_coreFrame->loader()->provisionalDocumentLoader()->url().string(); } String WebFrame::suggestedFilenameForResourceWithURL(const KURL& url) const { if (!m_coreFrame) return String(); DocumentLoader* loader = m_coreFrame->loader()->documentLoader(); if (!loader) return String(); // First, try the main resource. if (loader->url() == url) return loader->response().suggestedFilename(); // Next, try subresources. RefPtr resource = loader->subresource(url); if (!resource) return String(); return resource->response().suggestedFilename(); } String WebFrame::mimeTypeForResourceWithURL(const KURL& url) const { if (!m_coreFrame) return String(); DocumentLoader* loader = m_coreFrame->loader()->documentLoader(); if (!loader) return String(); // First, try the main resource. if (loader->url() == url) return loader->response().mimeType(); // Next, try subresources. RefPtr resource = loader->subresource(url); if (resource) return resource->mimeType(); return page()->cachedResponseMIMETypeForURL(url); } } // namespace WebKit