| /* |
| * 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 <JavaScriptCore/APICast.h> |
| #include <JavaScriptCore/JSContextRef.h> |
| #include <JavaScriptCore/JSLock.h> |
| #include <JavaScriptCore/JSValueRef.h> |
| #include <WebCore/AnimationController.h> |
| #include <WebCore/ArchiveResource.h> |
| #include <WebCore/CSSComputedStyleDeclaration.h> |
| #include <WebCore/Chrome.h> |
| #include <WebCore/DocumentLoader.h> |
| #include <WebCore/Frame.h> |
| #include <WebCore/FrameView.h> |
| #include <WebCore/HTMLFrameOwnerElement.h> |
| #include <WebCore/JSCSSStyleDeclaration.h> |
| #include <WebCore/JSElement.h> |
| #include <WebCore/JSRange.h> |
| #include <WebCore/Page.h> |
| #include <WebCore/RenderTreeAsText.h> |
| #include <WebCore/TextIterator.h> |
| #include <WebCore/TextResourceDecoder.h> |
| #include <wtf/text/StringBuilder.h> |
| |
| #ifndef NDEBUG |
| #include <wtf/RefCountedLeakCounter.h> |
| #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> WebFrame::createMainFrame(WebPage* page) |
| { |
| RefPtr<WebFrame> frame = create(); |
| |
| page->send(Messages::WebPageProxy::DidCreateMainFrame(frame->frameID())); |
| |
| frame->init(page, String(), 0); |
| |
| return frame.release(); |
| } |
| |
| PassRefPtr<WebFrame> WebFrame::createSubframe(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement) |
| { |
| RefPtr<WebFrame> frame = create(); |
| |
| WebFrame* parentFrame = static_cast<WebFrameLoaderClient*>(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> WebFrame::create() |
| { |
| RefPtr<WebFrame> 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 = 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<WebChromeClient*>(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: <rdar://5634381> 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<SharedBuffer> 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<WebFrameLoaderClient*>(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<Element> documentElement = document->documentElement(); |
| if (!documentElement) |
| return String(); |
| |
| RefPtr<Range> 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<ImmutableArray> WebFrame::childFrames() |
| { |
| if (!m_coreFrame) |
| return ImmutableArray::create(); |
| |
| size_t size = m_coreFrame->tree()->childCount(); |
| if (!size) |
| return ImmutableArray::create(); |
| |
| Vector<RefPtr<APIObject> > vector; |
| vector.reserveInitialCapacity(size); |
| |
| for (Frame* child = m_coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { |
| WebFrame* webFrame = static_cast<WebFrameLoaderClient*>(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::hasHorizontalScrollbar() const |
| { |
| if (!m_coreFrame) |
| return false; |
| |
| FrameView* view = m_coreFrame->view(); |
| if (!view) |
| return false; |
| |
| return view->horizontalScrollbar(); |
| } |
| |
| bool WebFrame::hasVerticalScrollbar() const |
| { |
| if (!m_coreFrame) |
| return false; |
| |
| FrameView* view = m_coreFrame->view(); |
| if (!view) |
| return false; |
| |
| return view->verticalScrollbar(); |
| } |
| |
| 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<JSDOMWindowShell*>(globalObjectObj)->window()->impl()->frame(); |
| return static_cast<WebFrameLoaderClient*>(coreFrame->loader()->client())->webFrame(); |
| } |
| |
| JSValueRef WebFrame::jsWrapperForWorld(InjectedBundleNodeHandle* nodeHandle, InjectedBundleScriptWorld* world) |
| { |
| if (!m_coreFrame) |
| return 0; |
| |
| 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) |
| { |
| if (!m_coreFrame) |
| return 0; |
| |
| 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<CSSComputedStyleDeclaration> style = computedStyle(static_cast<JSElement*>(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<JSElement*>(toJS(element))->impl()); |
| } |
| |
| String WebFrame::markerText(JSObjectRef element) |
| { |
| if (!toJS(element)->inherits(&JSElement::s_info)) |
| return String(); |
| |
| return markerTextForListItem(static_cast<JSElement*>(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<ArchiveResource> 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<ArchiveResource> resource = loader->subresource(url); |
| if (resource) |
| return resource->mimeType(); |
| |
| return page()->cachedResponseMIMETypeForURL(url); |
| } |
| |
| } // namespace WebKit |