| 1 | /* |
| 2 | * Copyright (C) 2010-2019 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 "APIArray.h" |
| 30 | #include "DownloadManager.h" |
| 31 | #include "FrameInfoData.h" |
| 32 | #include "InjectedBundleHitTestResult.h" |
| 33 | #include "InjectedBundleNodeHandle.h" |
| 34 | #include "InjectedBundleRangeHandle.h" |
| 35 | #include "InjectedBundleScriptWorld.h" |
| 36 | #include "NetworkConnectionToWebProcessMessages.h" |
| 37 | #include "NetworkProcessConnection.h" |
| 38 | #include "PluginView.h" |
| 39 | #include "WKAPICast.h" |
| 40 | #include "WKBundleAPICast.h" |
| 41 | #include "WebChromeClient.h" |
| 42 | #include "WebCoreArgumentCoders.h" |
| 43 | #include "WebDocumentLoader.h" |
| 44 | #include "WebPage.h" |
| 45 | #include "WebPageProxyMessages.h" |
| 46 | #include "WebProcess.h" |
| 47 | #include "WebsitePoliciesData.h" |
| 48 | #include <JavaScriptCore/APICast.h> |
| 49 | #include <JavaScriptCore/JSContextRef.h> |
| 50 | #include <JavaScriptCore/JSLock.h> |
| 51 | #include <JavaScriptCore/JSValueRef.h> |
| 52 | #include <WebCore/ArchiveResource.h> |
| 53 | #include <WebCore/CertificateInfo.h> |
| 54 | #include <WebCore/Chrome.h> |
| 55 | #include <WebCore/DocumentLoader.h> |
| 56 | #include <WebCore/Editor.h> |
| 57 | #include <WebCore/EventHandler.h> |
| 58 | #include <WebCore/File.h> |
| 59 | #include <WebCore/Frame.h> |
| 60 | #include <WebCore/FrameSnapshotting.h> |
| 61 | #include <WebCore/FrameView.h> |
| 62 | #include <WebCore/HTMLFormElement.h> |
| 63 | #include <WebCore/HTMLFrameOwnerElement.h> |
| 64 | #include <WebCore/HTMLInputElement.h> |
| 65 | #include <WebCore/HTMLNames.h> |
| 66 | #include <WebCore/HTMLSelectElement.h> |
| 67 | #include <WebCore/HTMLTextAreaElement.h> |
| 68 | #include <WebCore/ImageBuffer.h> |
| 69 | #include <WebCore/JSCSSStyleDeclaration.h> |
| 70 | #include <WebCore/JSElement.h> |
| 71 | #include <WebCore/JSFile.h> |
| 72 | #include <WebCore/JSRange.h> |
| 73 | #include <WebCore/NodeTraversal.h> |
| 74 | #include <WebCore/Page.h> |
| 75 | #include <WebCore/PluginDocument.h> |
| 76 | #include <WebCore/RenderTreeAsText.h> |
| 77 | #include <WebCore/ScriptController.h> |
| 78 | #include <WebCore/SecurityOrigin.h> |
| 79 | #include <WebCore/SubresourceLoader.h> |
| 80 | #include <WebCore/TextIterator.h> |
| 81 | #include <WebCore/TextResourceDecoder.h> |
| 82 | #include <wtf/text/StringBuilder.h> |
| 83 | |
| 84 | #if PLATFORM(COCOA) |
| 85 | #include <WebCore/LegacyWebArchive.h> |
| 86 | #endif |
| 87 | |
| 88 | #ifndef NDEBUG |
| 89 | #include <wtf/RefCountedLeakCounter.h> |
| 90 | #endif |
| 91 | |
| 92 | namespace WebKit { |
| 93 | using namespace JSC; |
| 94 | using namespace WebCore; |
| 95 | |
| 96 | DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, webFrameCounter, ("WebFrame" )); |
| 97 | |
| 98 | static uint64_t generateFrameID() |
| 99 | { |
| 100 | static uint64_t uniqueFrameID = 1; |
| 101 | return uniqueFrameID++; |
| 102 | } |
| 103 | |
| 104 | static uint64_t generateListenerID() |
| 105 | { |
| 106 | static uint64_t uniqueListenerID = 1; |
| 107 | return uniqueListenerID++; |
| 108 | } |
| 109 | |
| 110 | Ref<WebFrame> WebFrame::createWithCoreMainFrame(WebPage* page, WebCore::Frame* coreFrame) |
| 111 | { |
| 112 | auto frame = create(std::unique_ptr<WebFrameLoaderClient>(static_cast<WebFrameLoaderClient*>(&coreFrame->loader().client()))); |
| 113 | page->send(Messages::WebPageProxy::DidCreateMainFrame(frame->frameID()), page->pageID()); |
| 114 | |
| 115 | frame->m_coreFrame = coreFrame; |
| 116 | frame->m_coreFrame->tree().setName(String()); |
| 117 | frame->m_coreFrame->init(); |
| 118 | return frame; |
| 119 | } |
| 120 | |
| 121 | Ref<WebFrame> WebFrame::createSubframe(WebPage* page, const String& frameName, HTMLFrameOwnerElement* ownerElement) |
| 122 | { |
| 123 | auto frame = create(std::make_unique<WebFrameLoaderClient>()); |
| 124 | page->send(Messages::WebPageProxy::DidCreateSubframe(frame->frameID()), page->pageID()); |
| 125 | |
| 126 | auto coreFrame = Frame::create(page->corePage(), ownerElement, frame->m_frameLoaderClient.get()); |
| 127 | frame->m_coreFrame = coreFrame.ptr(); |
| 128 | |
| 129 | coreFrame->tree().setName(frameName); |
| 130 | if (ownerElement) { |
| 131 | ASSERT(ownerElement->document().frame()); |
| 132 | ownerElement->document().frame()->tree().appendChild(coreFrame.get()); |
| 133 | } |
| 134 | coreFrame->init(); |
| 135 | |
| 136 | return frame; |
| 137 | } |
| 138 | |
| 139 | Ref<WebFrame> WebFrame::create(std::unique_ptr<WebFrameLoaderClient> frameLoaderClient) |
| 140 | { |
| 141 | auto frame = adoptRef(*new WebFrame(WTFMove(frameLoaderClient))); |
| 142 | |
| 143 | // Add explict ref() that will be balanced in WebFrameLoaderClient::frameLoaderDestroyed(). |
| 144 | frame->ref(); |
| 145 | |
| 146 | return frame; |
| 147 | } |
| 148 | |
| 149 | WebFrame::WebFrame(std::unique_ptr<WebFrameLoaderClient> frameLoaderClient) |
| 150 | : m_frameLoaderClient(WTFMove(frameLoaderClient)) |
| 151 | , m_frameID(generateFrameID()) |
| 152 | { |
| 153 | m_frameLoaderClient->setWebFrame(this); |
| 154 | WebProcess::singleton().addWebFrame(m_frameID, this); |
| 155 | |
| 156 | #ifndef NDEBUG |
| 157 | webFrameCounter.increment(); |
| 158 | #endif |
| 159 | } |
| 160 | |
| 161 | WebFrame::~WebFrame() |
| 162 | { |
| 163 | ASSERT(!m_coreFrame); |
| 164 | |
| 165 | auto willSubmitFormCompletionHandlers = WTFMove(m_willSubmitFormCompletionHandlers); |
| 166 | for (auto& completionHandler : willSubmitFormCompletionHandlers.values()) |
| 167 | completionHandler(); |
| 168 | |
| 169 | #ifndef NDEBUG |
| 170 | webFrameCounter.decrement(); |
| 171 | #endif |
| 172 | } |
| 173 | |
| 174 | WebPage* WebFrame::page() const |
| 175 | { |
| 176 | if (!m_coreFrame) |
| 177 | return nullptr; |
| 178 | |
| 179 | if (Page* page = m_coreFrame->page()) |
| 180 | return WebPage::fromCorePage(page); |
| 181 | |
| 182 | return nullptr; |
| 183 | } |
| 184 | |
| 185 | WebFrame* WebFrame::fromCoreFrame(Frame& frame) |
| 186 | { |
| 187 | auto* webFrameLoaderClient = toWebFrameLoaderClient(frame.loader().client()); |
| 188 | if (!webFrameLoaderClient) |
| 189 | return nullptr; |
| 190 | |
| 191 | return webFrameLoaderClient->webFrame(); |
| 192 | } |
| 193 | |
| 194 | FrameInfoData WebFrame::info() const |
| 195 | { |
| 196 | FrameInfoData info; |
| 197 | |
| 198 | info.isMainFrame = isMainFrame(); |
| 199 | // FIXME: This should use the full request. |
| 200 | info.request = ResourceRequest(URL(URL(), url())); |
| 201 | info.securityOrigin = SecurityOriginData::fromFrame(m_coreFrame); |
| 202 | info.frameID = m_frameID; |
| 203 | |
| 204 | return info; |
| 205 | } |
| 206 | |
| 207 | void WebFrame::invalidate() |
| 208 | { |
| 209 | WebProcess::singleton().removeWebFrame(m_frameID); |
| 210 | m_coreFrame = 0; |
| 211 | } |
| 212 | |
| 213 | uint64_t WebFrame::setUpPolicyListener(WebCore::PolicyCheckIdentifier identifier, WebCore::FramePolicyFunction&& policyFunction, ForNavigationAction forNavigationAction) |
| 214 | { |
| 215 | // FIXME: <rdar://5634381> We need to support multiple active policy listeners. |
| 216 | |
| 217 | invalidatePolicyListener(); |
| 218 | |
| 219 | m_policyIdentifier = identifier; |
| 220 | m_policyListenerID = generateListenerID(); |
| 221 | m_policyFunction = WTFMove(policyFunction); |
| 222 | m_policyFunctionForNavigationAction = forNavigationAction; |
| 223 | return m_policyListenerID; |
| 224 | } |
| 225 | |
| 226 | uint64_t WebFrame::setUpWillSubmitFormListener(CompletionHandler<void()>&& completionHandler) |
| 227 | { |
| 228 | uint64_t identifier = generateListenerID(); |
| 229 | invalidatePolicyListener(); |
| 230 | m_willSubmitFormCompletionHandlers.set(identifier, WTFMove(completionHandler)); |
| 231 | return identifier; |
| 232 | } |
| 233 | |
| 234 | void WebFrame::continueWillSubmitForm(uint64_t listenerID) |
| 235 | { |
| 236 | Ref<WebFrame> protectedThis(*this); |
| 237 | if (auto completionHandler = m_willSubmitFormCompletionHandlers.take(listenerID)) |
| 238 | completionHandler(); |
| 239 | invalidatePolicyListener(); |
| 240 | } |
| 241 | |
| 242 | void WebFrame::invalidatePolicyListener() |
| 243 | { |
| 244 | if (!m_policyListenerID) |
| 245 | return; |
| 246 | |
| 247 | m_policyDownloadID = { }; |
| 248 | m_policyListenerID = 0; |
| 249 | auto identifier = m_policyIdentifier; |
| 250 | m_policyIdentifier = WTF::nullopt; |
| 251 | if (auto function = std::exchange(m_policyFunction, nullptr)) |
| 252 | function(PolicyAction::Ignore, *identifier); |
| 253 | m_policyFunctionForNavigationAction = ForNavigationAction::No; |
| 254 | |
| 255 | auto willSubmitFormCompletionHandlers = WTFMove(m_willSubmitFormCompletionHandlers); |
| 256 | for (auto& completionHandler : willSubmitFormCompletionHandlers.values()) |
| 257 | completionHandler(); |
| 258 | } |
| 259 | |
| 260 | void WebFrame::didReceivePolicyDecision(uint64_t listenerID, WebCore::PolicyCheckIdentifier identifier, PolicyAction action, uint64_t navigationID, DownloadID downloadID, Optional<WebsitePoliciesData>&& websitePolicies) |
| 261 | { |
| 262 | if (!m_coreFrame || !m_policyListenerID || listenerID != m_policyListenerID || !m_policyFunction) |
| 263 | return; |
| 264 | |
| 265 | ASSERT(identifier == m_policyIdentifier); |
| 266 | m_policyIdentifier = WTF::nullopt; |
| 267 | |
| 268 | FramePolicyFunction function = WTFMove(m_policyFunction); |
| 269 | bool forNavigationAction = m_policyFunctionForNavigationAction == ForNavigationAction::Yes; |
| 270 | |
| 271 | invalidatePolicyListener(); |
| 272 | |
| 273 | if (forNavigationAction && m_frameLoaderClient && websitePolicies) |
| 274 | m_frameLoaderClient->applyToDocumentLoader(WTFMove(*websitePolicies)); |
| 275 | |
| 276 | m_policyDownloadID = downloadID; |
| 277 | if (navigationID) { |
| 278 | if (WebDocumentLoader* documentLoader = static_cast<WebDocumentLoader*>(m_coreFrame->loader().policyDocumentLoader())) |
| 279 | documentLoader->setNavigationID(navigationID); |
| 280 | } |
| 281 | |
| 282 | function(action, identifier); |
| 283 | } |
| 284 | |
| 285 | void WebFrame::startDownload(const WebCore::ResourceRequest& request, const String& suggestedName) |
| 286 | { |
| 287 | ASSERT(m_policyDownloadID.downloadID()); |
| 288 | |
| 289 | auto policyDownloadID = m_policyDownloadID; |
| 290 | m_policyDownloadID = { }; |
| 291 | |
| 292 | auto& webProcess = WebProcess::singleton(); |
| 293 | PAL::SessionID sessionID = page() ? page()->sessionID() : PAL::SessionID::defaultSessionID(); |
| 294 | webProcess.ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::StartDownload(sessionID, policyDownloadID, request, suggestedName), 0); |
| 295 | } |
| 296 | |
| 297 | void WebFrame::convertMainResourceLoadToDownload(DocumentLoader* documentLoader, PAL::SessionID sessionID, const ResourceRequest& request, const ResourceResponse& response) |
| 298 | { |
| 299 | ASSERT(m_policyDownloadID.downloadID()); |
| 300 | |
| 301 | auto policyDownloadID = m_policyDownloadID; |
| 302 | m_policyDownloadID = { }; |
| 303 | |
| 304 | SubresourceLoader* mainResourceLoader = documentLoader->mainResourceLoader(); |
| 305 | |
| 306 | auto& webProcess = WebProcess::singleton(); |
| 307 | // Use 0 to indicate that the resource load can't be converted and a new download must be started. |
| 308 | // This can happen if there is no loader because the main resource is in the WebCore memory cache, |
| 309 | // or because the conversion was attempted when not calling SubresourceLoader::didReceiveResponse(). |
| 310 | uint64_t mainResourceLoadIdentifier; |
| 311 | if (mainResourceLoader) |
| 312 | mainResourceLoadIdentifier = mainResourceLoader->identifier(); |
| 313 | else |
| 314 | mainResourceLoadIdentifier = 0; |
| 315 | |
| 316 | webProcess.ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::ConvertMainResourceLoadToDownload(sessionID, mainResourceLoadIdentifier, policyDownloadID, request, response), 0); |
| 317 | } |
| 318 | |
| 319 | void WebFrame::addConsoleMessage(MessageSource messageSource, MessageLevel messageLevel, const String& message, uint64_t requestID) |
| 320 | { |
| 321 | if (!m_coreFrame) |
| 322 | return; |
| 323 | if (auto* document = m_coreFrame->document()) |
| 324 | document->addConsoleMessage(messageSource, messageLevel, message, requestID); |
| 325 | } |
| 326 | |
| 327 | String WebFrame::source() const |
| 328 | { |
| 329 | if (!m_coreFrame) |
| 330 | return String(); |
| 331 | Document* document = m_coreFrame->document(); |
| 332 | if (!document) |
| 333 | return String(); |
| 334 | TextResourceDecoder* decoder = document->decoder(); |
| 335 | if (!decoder) |
| 336 | return String(); |
| 337 | DocumentLoader* documentLoader = m_coreFrame->loader().activeDocumentLoader(); |
| 338 | if (!documentLoader) |
| 339 | return String(); |
| 340 | RefPtr<SharedBuffer> mainResourceData = documentLoader->mainResourceData(); |
| 341 | if (!mainResourceData) |
| 342 | return String(); |
| 343 | return decoder->encoding().decode(mainResourceData->data(), mainResourceData->size()); |
| 344 | } |
| 345 | |
| 346 | String WebFrame::contentsAsString() const |
| 347 | { |
| 348 | if (!m_coreFrame) |
| 349 | return String(); |
| 350 | |
| 351 | if (isFrameSet()) { |
| 352 | StringBuilder builder; |
| 353 | for (Frame* child = m_coreFrame->tree().firstChild(); child; child = child->tree().nextSibling()) { |
| 354 | if (!builder.isEmpty()) |
| 355 | builder.append(' '); |
| 356 | |
| 357 | WebFrame* webFrame = WebFrame::fromCoreFrame(*child); |
| 358 | ASSERT(webFrame); |
| 359 | |
| 360 | builder.append(webFrame->contentsAsString()); |
| 361 | } |
| 362 | // FIXME: It may make sense to use toStringPreserveCapacity() here. |
| 363 | return builder.toString(); |
| 364 | } |
| 365 | |
| 366 | Document* document = m_coreFrame->document(); |
| 367 | if (!document) |
| 368 | return String(); |
| 369 | |
| 370 | RefPtr<Element> documentElement = document->documentElement(); |
| 371 | if (!documentElement) |
| 372 | return String(); |
| 373 | |
| 374 | RefPtr<Range> range = document->createRange(); |
| 375 | |
| 376 | if (range->selectNode(*documentElement).hasException()) |
| 377 | return String(); |
| 378 | |
| 379 | return plainText(range.get()); |
| 380 | } |
| 381 | |
| 382 | String WebFrame::selectionAsString() const |
| 383 | { |
| 384 | if (!m_coreFrame) |
| 385 | return String(); |
| 386 | |
| 387 | return m_coreFrame->displayStringModifiedByEncoding(m_coreFrame->editor().selectedText()); |
| 388 | } |
| 389 | |
| 390 | IntSize WebFrame::size() const |
| 391 | { |
| 392 | if (!m_coreFrame) |
| 393 | return IntSize(); |
| 394 | |
| 395 | FrameView* frameView = m_coreFrame->view(); |
| 396 | if (!frameView) |
| 397 | return IntSize(); |
| 398 | |
| 399 | return frameView->contentsSize(); |
| 400 | } |
| 401 | |
| 402 | bool WebFrame::isFrameSet() const |
| 403 | { |
| 404 | if (!m_coreFrame) |
| 405 | return false; |
| 406 | |
| 407 | Document* document = m_coreFrame->document(); |
| 408 | if (!document) |
| 409 | return false; |
| 410 | return document->isFrameSet(); |
| 411 | } |
| 412 | |
| 413 | bool WebFrame::isMainFrame() const |
| 414 | { |
| 415 | if (!m_coreFrame) |
| 416 | return false; |
| 417 | |
| 418 | return m_coreFrame->isMainFrame(); |
| 419 | } |
| 420 | |
| 421 | String WebFrame::name() const |
| 422 | { |
| 423 | if (!m_coreFrame) |
| 424 | return String(); |
| 425 | |
| 426 | return m_coreFrame->tree().uniqueName(); |
| 427 | } |
| 428 | |
| 429 | URL WebFrame::url() const |
| 430 | { |
| 431 | if (!m_coreFrame) |
| 432 | return { }; |
| 433 | |
| 434 | auto* documentLoader = m_coreFrame->loader().documentLoader(); |
| 435 | if (!documentLoader) |
| 436 | return { }; |
| 437 | |
| 438 | return documentLoader->url(); |
| 439 | } |
| 440 | |
| 441 | CertificateInfo WebFrame::certificateInfo() const |
| 442 | { |
| 443 | if (!m_coreFrame) |
| 444 | return { }; |
| 445 | |
| 446 | DocumentLoader* documentLoader = m_coreFrame->loader().documentLoader(); |
| 447 | if (!documentLoader) |
| 448 | return { }; |
| 449 | |
| 450 | return valueOrCompute(documentLoader->response().certificateInfo(), [] { return CertificateInfo(); }); |
| 451 | } |
| 452 | |
| 453 | String WebFrame::innerText() const |
| 454 | { |
| 455 | if (!m_coreFrame) |
| 456 | return String(); |
| 457 | |
| 458 | if (!m_coreFrame->document()->documentElement()) |
| 459 | return String(); |
| 460 | |
| 461 | return m_coreFrame->document()->documentElement()->innerText(); |
| 462 | } |
| 463 | |
| 464 | WebFrame* WebFrame::parentFrame() const |
| 465 | { |
| 466 | if (!m_coreFrame || !m_coreFrame->ownerElement()) |
| 467 | return nullptr; |
| 468 | |
| 469 | auto* frame = m_coreFrame->ownerElement()->document().frame(); |
| 470 | if (!frame) |
| 471 | return nullptr; |
| 472 | |
| 473 | return WebFrame::fromCoreFrame(*frame); |
| 474 | } |
| 475 | |
| 476 | Ref<API::Array> WebFrame::childFrames() |
| 477 | { |
| 478 | if (!m_coreFrame) |
| 479 | return API::Array::create(); |
| 480 | |
| 481 | size_t size = m_coreFrame->tree().childCount(); |
| 482 | if (!size) |
| 483 | return API::Array::create(); |
| 484 | |
| 485 | Vector<RefPtr<API::Object>> vector; |
| 486 | vector.reserveInitialCapacity(size); |
| 487 | |
| 488 | for (Frame* child = m_coreFrame->tree().firstChild(); child; child = child->tree().nextSibling()) { |
| 489 | WebFrame* webFrame = WebFrame::fromCoreFrame(*child); |
| 490 | ASSERT(webFrame); |
| 491 | vector.uncheckedAppend(webFrame); |
| 492 | } |
| 493 | |
| 494 | return API::Array::create(WTFMove(vector)); |
| 495 | } |
| 496 | |
| 497 | String WebFrame::layerTreeAsText() const |
| 498 | { |
| 499 | if (!m_coreFrame) |
| 500 | return "" ; |
| 501 | |
| 502 | return m_coreFrame->layerTreeAsText(0); |
| 503 | } |
| 504 | |
| 505 | unsigned WebFrame::pendingUnloadCount() const |
| 506 | { |
| 507 | if (!m_coreFrame) |
| 508 | return 0; |
| 509 | |
| 510 | return m_coreFrame->document()->domWindow()->pendingUnloadEventListeners(); |
| 511 | } |
| 512 | |
| 513 | bool WebFrame::allowsFollowingLink(const URL& url) const |
| 514 | { |
| 515 | if (!m_coreFrame) |
| 516 | return true; |
| 517 | |
| 518 | return m_coreFrame->document()->securityOrigin().canDisplay(url); |
| 519 | } |
| 520 | |
| 521 | JSGlobalContextRef WebFrame::jsContext() |
| 522 | { |
| 523 | return toGlobalRef(m_coreFrame->script().globalObject(mainThreadNormalWorld())->globalExec()); |
| 524 | } |
| 525 | |
| 526 | JSGlobalContextRef WebFrame::jsContextForWorld(InjectedBundleScriptWorld* world) |
| 527 | { |
| 528 | return toGlobalRef(m_coreFrame->script().globalObject(world->coreWorld())->globalExec()); |
| 529 | } |
| 530 | |
| 531 | bool WebFrame::handlesPageScaleGesture() const |
| 532 | { |
| 533 | auto* pluginView = WebPage::pluginViewForFrame(m_coreFrame); |
| 534 | return pluginView && pluginView->handlesPageScaleFactor(); |
| 535 | } |
| 536 | |
| 537 | bool WebFrame::requiresUnifiedScaleFactor() const |
| 538 | { |
| 539 | auto* pluginView = WebPage::pluginViewForFrame(m_coreFrame); |
| 540 | return pluginView && pluginView->requiresUnifiedScaleFactor(); |
| 541 | } |
| 542 | |
| 543 | void WebFrame::setAccessibleName(const String& accessibleName) |
| 544 | { |
| 545 | if (!AXObjectCache::accessibilityEnabled()) |
| 546 | return; |
| 547 | |
| 548 | if (!m_coreFrame) |
| 549 | return; |
| 550 | |
| 551 | auto* document = m_coreFrame->document(); |
| 552 | if (!document) |
| 553 | return; |
| 554 | |
| 555 | auto* rootObject = document->axObjectCache()->rootObject(); |
| 556 | if (!rootObject) |
| 557 | return; |
| 558 | |
| 559 | rootObject->setAccessibleName(accessibleName); |
| 560 | } |
| 561 | |
| 562 | IntRect WebFrame::contentBounds() const |
| 563 | { |
| 564 | if (!m_coreFrame) |
| 565 | return IntRect(); |
| 566 | |
| 567 | FrameView* view = m_coreFrame->view(); |
| 568 | if (!view) |
| 569 | return IntRect(); |
| 570 | |
| 571 | return IntRect(0, 0, view->contentsWidth(), view->contentsHeight()); |
| 572 | } |
| 573 | |
| 574 | IntRect WebFrame::visibleContentBounds() const |
| 575 | { |
| 576 | if (!m_coreFrame) |
| 577 | return IntRect(); |
| 578 | |
| 579 | FrameView* view = m_coreFrame->view(); |
| 580 | if (!view) |
| 581 | return IntRect(); |
| 582 | |
| 583 | IntRect contentRect = view->visibleContentRectIncludingScrollbars(); |
| 584 | return IntRect(0, 0, contentRect.width(), contentRect.height()); |
| 585 | } |
| 586 | |
| 587 | IntRect WebFrame::visibleContentBoundsExcludingScrollbars() const |
| 588 | { |
| 589 | if (!m_coreFrame) |
| 590 | return IntRect(); |
| 591 | |
| 592 | FrameView* view = m_coreFrame->view(); |
| 593 | if (!view) |
| 594 | return IntRect(); |
| 595 | |
| 596 | IntRect contentRect = view->visibleContentRect(); |
| 597 | return IntRect(0, 0, contentRect.width(), contentRect.height()); |
| 598 | } |
| 599 | |
| 600 | IntSize WebFrame::scrollOffset() const |
| 601 | { |
| 602 | if (!m_coreFrame) |
| 603 | return IntSize(); |
| 604 | |
| 605 | FrameView* view = m_coreFrame->view(); |
| 606 | if (!view) |
| 607 | return IntSize(); |
| 608 | |
| 609 | return toIntSize(view->scrollPosition()); |
| 610 | } |
| 611 | |
| 612 | bool WebFrame::hasHorizontalScrollbar() const |
| 613 | { |
| 614 | if (!m_coreFrame) |
| 615 | return false; |
| 616 | |
| 617 | FrameView* view = m_coreFrame->view(); |
| 618 | if (!view) |
| 619 | return false; |
| 620 | |
| 621 | return view->horizontalScrollbar(); |
| 622 | } |
| 623 | |
| 624 | bool WebFrame::hasVerticalScrollbar() const |
| 625 | { |
| 626 | if (!m_coreFrame) |
| 627 | return false; |
| 628 | |
| 629 | FrameView* view = m_coreFrame->view(); |
| 630 | if (!view) |
| 631 | return false; |
| 632 | |
| 633 | return view->verticalScrollbar(); |
| 634 | } |
| 635 | |
| 636 | RefPtr<InjectedBundleHitTestResult> WebFrame::hitTest(const IntPoint point) const |
| 637 | { |
| 638 | if (!m_coreFrame) |
| 639 | return nullptr; |
| 640 | |
| 641 | return InjectedBundleHitTestResult::create(m_coreFrame->eventHandler().hitTestResultAtPoint(point, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowUserAgentShadowContent)); |
| 642 | } |
| 643 | |
| 644 | bool WebFrame::getDocumentBackgroundColor(double* red, double* green, double* blue, double* alpha) |
| 645 | { |
| 646 | if (!m_coreFrame) |
| 647 | return false; |
| 648 | |
| 649 | FrameView* view = m_coreFrame->view(); |
| 650 | if (!view) |
| 651 | return false; |
| 652 | |
| 653 | Color bgColor = view->documentBackgroundColor(); |
| 654 | if (!bgColor.isValid()) |
| 655 | return false; |
| 656 | |
| 657 | bgColor.getRGBA(*red, *green, *blue, *alpha); |
| 658 | return true; |
| 659 | } |
| 660 | |
| 661 | bool WebFrame::containsAnyFormElements() const |
| 662 | { |
| 663 | if (!m_coreFrame) |
| 664 | return false; |
| 665 | |
| 666 | Document* document = m_coreFrame->document(); |
| 667 | if (!document) |
| 668 | return false; |
| 669 | |
| 670 | for (Node* node = document->documentElement(); node; node = NodeTraversal::next(*node)) { |
| 671 | if (!is<Element>(*node)) |
| 672 | continue; |
| 673 | if (is<HTMLFormElement>(*node)) |
| 674 | return true; |
| 675 | } |
| 676 | return false; |
| 677 | } |
| 678 | |
| 679 | bool WebFrame::containsAnyFormControls() const |
| 680 | { |
| 681 | if (!m_coreFrame) |
| 682 | return false; |
| 683 | |
| 684 | Document* document = m_coreFrame->document(); |
| 685 | if (!document) |
| 686 | return false; |
| 687 | |
| 688 | for (Node* node = document->documentElement(); node; node = NodeTraversal::next(*node)) { |
| 689 | if (!is<Element>(*node)) |
| 690 | continue; |
| 691 | if (is<HTMLInputElement>(*node) || is<HTMLSelectElement>(*node) || is<HTMLTextAreaElement>(*node)) |
| 692 | return true; |
| 693 | } |
| 694 | return false; |
| 695 | } |
| 696 | |
| 697 | void WebFrame::stopLoading() |
| 698 | { |
| 699 | if (!m_coreFrame) |
| 700 | return; |
| 701 | |
| 702 | m_coreFrame->loader().stopForUserCancel(); |
| 703 | } |
| 704 | |
| 705 | WebFrame* WebFrame::frameForContext(JSContextRef context) |
| 706 | { |
| 707 | |
| 708 | JSC::JSGlobalObject* globalObjectObj = toJS(context)->lexicalGlobalObject(); |
| 709 | JSDOMWindow* window = jsDynamicCast<JSDOMWindow*>(globalObjectObj->vm(), globalObjectObj); |
| 710 | if (!window) |
| 711 | return nullptr; |
| 712 | return WebFrame::fromCoreFrame(*(window->wrapped().frame())); |
| 713 | } |
| 714 | |
| 715 | JSValueRef WebFrame::jsWrapperForWorld(InjectedBundleNodeHandle* nodeHandle, InjectedBundleScriptWorld* world) |
| 716 | { |
| 717 | if (!m_coreFrame) |
| 718 | return 0; |
| 719 | |
| 720 | JSDOMWindow* globalObject = m_coreFrame->script().globalObject(world->coreWorld()); |
| 721 | ExecState* exec = globalObject->globalExec(); |
| 722 | |
| 723 | JSLockHolder lock(exec); |
| 724 | return toRef(exec, toJS(exec, globalObject, nodeHandle->coreNode())); |
| 725 | } |
| 726 | |
| 727 | JSValueRef WebFrame::jsWrapperForWorld(InjectedBundleRangeHandle* rangeHandle, InjectedBundleScriptWorld* world) |
| 728 | { |
| 729 | if (!m_coreFrame) |
| 730 | return 0; |
| 731 | |
| 732 | JSDOMWindow* globalObject = m_coreFrame->script().globalObject(world->coreWorld()); |
| 733 | ExecState* exec = globalObject->globalExec(); |
| 734 | |
| 735 | JSLockHolder lock(exec); |
| 736 | return toRef(exec, toJS(exec, globalObject, rangeHandle->coreRange())); |
| 737 | } |
| 738 | |
| 739 | String WebFrame::counterValue(JSObjectRef element) |
| 740 | { |
| 741 | if (!toJS(element)->inherits<JSElement>(*toJS(element)->vm())) |
| 742 | return String(); |
| 743 | |
| 744 | return counterValueForElement(&jsCast<JSElement*>(toJS(element))->wrapped()); |
| 745 | } |
| 746 | |
| 747 | String WebFrame::provisionalURL() const |
| 748 | { |
| 749 | if (!m_coreFrame) |
| 750 | return String(); |
| 751 | |
| 752 | DocumentLoader* provisionalDocumentLoader = m_coreFrame->loader().provisionalDocumentLoader(); |
| 753 | if (!provisionalDocumentLoader) |
| 754 | return String(); |
| 755 | |
| 756 | return provisionalDocumentLoader->url().string(); |
| 757 | } |
| 758 | |
| 759 | String WebFrame::suggestedFilenameForResourceWithURL(const URL& url) const |
| 760 | { |
| 761 | if (!m_coreFrame) |
| 762 | return String(); |
| 763 | |
| 764 | DocumentLoader* loader = m_coreFrame->loader().documentLoader(); |
| 765 | if (!loader) |
| 766 | return String(); |
| 767 | |
| 768 | // First, try the main resource. |
| 769 | if (loader->url() == url) |
| 770 | return loader->response().suggestedFilename(); |
| 771 | |
| 772 | // Next, try subresources. |
| 773 | RefPtr<ArchiveResource> resource = loader->subresource(url); |
| 774 | if (resource) |
| 775 | return resource->response().suggestedFilename(); |
| 776 | |
| 777 | return String(); |
| 778 | } |
| 779 | |
| 780 | String WebFrame::mimeTypeForResourceWithURL(const URL& url) const |
| 781 | { |
| 782 | if (!m_coreFrame) |
| 783 | return String(); |
| 784 | |
| 785 | DocumentLoader* loader = m_coreFrame->loader().documentLoader(); |
| 786 | if (!loader) |
| 787 | return String(); |
| 788 | |
| 789 | // First, try the main resource. |
| 790 | if (loader->url() == url) |
| 791 | return loader->response().mimeType(); |
| 792 | |
| 793 | // Next, try subresources. |
| 794 | RefPtr<ArchiveResource> resource = loader->subresource(url); |
| 795 | if (resource) |
| 796 | return resource->mimeType(); |
| 797 | |
| 798 | return String(); |
| 799 | } |
| 800 | |
| 801 | void WebFrame::setTextDirection(const String& direction) |
| 802 | { |
| 803 | if (!m_coreFrame) |
| 804 | return; |
| 805 | |
| 806 | if (direction == "auto" ) |
| 807 | m_coreFrame->editor().setBaseWritingDirection(WritingDirection::Natural); |
| 808 | else if (direction == "ltr" ) |
| 809 | m_coreFrame->editor().setBaseWritingDirection(WritingDirection::LeftToRight); |
| 810 | else if (direction == "rtl" ) |
| 811 | m_coreFrame->editor().setBaseWritingDirection(WritingDirection::RightToLeft); |
| 812 | } |
| 813 | |
| 814 | void WebFrame::documentLoaderDetached(uint64_t navigationID) |
| 815 | { |
| 816 | if (auto* page = this->page()) |
| 817 | page->send(Messages::WebPageProxy::DidDestroyNavigation(navigationID)); |
| 818 | } |
| 819 | |
| 820 | #if PLATFORM(COCOA) |
| 821 | RetainPtr<CFDataRef> WebFrame::webArchiveData(FrameFilterFunction callback, void* context) |
| 822 | { |
| 823 | auto archive = LegacyWebArchive::create(*coreFrame()->document(), [this, callback, context](Frame& frame) -> bool { |
| 824 | if (!callback) |
| 825 | return true; |
| 826 | |
| 827 | WebFrame* webFrame = WebFrame::fromCoreFrame(frame); |
| 828 | ASSERT(webFrame); |
| 829 | |
| 830 | return callback(toAPI(this), toAPI(webFrame), context); |
| 831 | }); |
| 832 | |
| 833 | if (!archive) |
| 834 | return nullptr; |
| 835 | |
| 836 | return archive->rawDataRepresentation(); |
| 837 | } |
| 838 | #endif |
| 839 | |
| 840 | RefPtr<ShareableBitmap> WebFrame::createSelectionSnapshot() const |
| 841 | { |
| 842 | std::unique_ptr<ImageBuffer> snapshot = snapshotSelection(*coreFrame(), WebCore::SnapshotOptionsForceBlackText); |
| 843 | if (!snapshot) |
| 844 | return nullptr; |
| 845 | |
| 846 | auto sharedSnapshot = ShareableBitmap::createShareable(snapshot->internalSize(), { }); |
| 847 | if (!sharedSnapshot) |
| 848 | return nullptr; |
| 849 | |
| 850 | // FIXME: We should consider providing a way to use subpixel antialiasing for the snapshot |
| 851 | // if we're compositing this image onto a solid color (e.g. the modern find indicator style). |
| 852 | auto graphicsContext = sharedSnapshot->createGraphicsContext(); |
| 853 | float deviceScaleFactor = coreFrame()->page()->deviceScaleFactor(); |
| 854 | graphicsContext->scale(deviceScaleFactor); |
| 855 | graphicsContext->drawConsumingImageBuffer(WTFMove(snapshot), FloatPoint()); |
| 856 | |
| 857 | return sharedSnapshot; |
| 858 | } |
| 859 | |
| 860 | } // namespace WebKit |
| 861 | |