flutter_flutter/engine/web/WebLocalFrameImpl.cpp
Eric Seidel 31f36426b2 Fix display of 404s in SkyShell to show error text.
In MojoShell we never hit this path since the NetworkFetcher
aborts earlier on, not knowing what content_handler to use.

R=ojan@chromium.org, ianh@google.com

Review URL: https://codereview.chromium.org/1080993005
2015-04-21 15:38:12 -07:00

620 lines
20 KiB
C++

/*
* Copyright (C) 2009 Google 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
* OWNER OR 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.
*/
// How ownership works
// -------------------
//
// Big oh represents a refcounted relationship: owner O--- ownee
//
// WebView (for the toplevel frame only)
// O
// | WebFrame
// | O
// | |
// Page O------- LocalFrame (m_mainFrame) O-------O FrameView
// ||
// ||
// FrameLoader
//
// FrameLoader and LocalFrame are formerly one object that was split apart because
// it got too big. They basically have the same lifetime, hence the double line.
//
// From the perspective of the embedder, WebFrame is simply an object that it
// allocates by calling WebFrame::create() and must be freed by calling close().
// Internally, WebFrame is actually refcounted and it holds a reference to its
// corresponding LocalFrame in WebCore.
//
// How frames are destroyed
// ------------------------
//
// The main frame is never destroyed and is re-used. The FrameLoader is re-used
// and a reference to the main frame is kept by the Page.
//
// When frame content is replaced, all subframes are destroyed. This happens
// in FrameLoader::detachFromParent for each subframe in a pre-order depth-first
// traversal. Note that child node order may not match DOM node order!
// detachFromParent() calls FrameLoaderClient::detachedFromParent(), which calls
// WebFrame::frameDetached(). This triggers WebFrame to clear its reference to
// LocalFrame, and also notifies the embedder via WebFrameClient that the frame is
// detached. Most embedders will invoke close() on the WebFrame at this point,
// triggering its deletion unless something else is still retaining a reference.
//
// Thie client is expected to be set whenever the WebLocalFrameImpl is attached to
// the DOM.
#include "sky/engine/config.h"
#include "sky/engine/web/WebLocalFrameImpl.h"
#include <algorithm>
#include "base/strings/stringprintf.h"
#include "mojo/common/data_pipe_utils.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "sky/engine/bindings/exception_state.h"
#include "sky/engine/bindings/exception_state_placeholder.h"
#include "sky/engine/core/dom/Document.h"
#include "sky/engine/core/dom/Node.h"
#include "sky/engine/core/dom/NodeTraversal.h"
#include "sky/engine/core/dom/shadow/ShadowRoot.h"
#include "sky/engine/core/editing/Editor.h"
#include "sky/engine/core/editing/FrameSelection.h"
#include "sky/engine/core/editing/htmlediting.h"
#include "sky/engine/core/editing/InputMethodController.h"
#include "sky/engine/core/editing/PlainTextRange.h"
#include "sky/engine/core/editing/SpellChecker.h"
#include "sky/engine/core/editing/TextAffinity.h"
#include "sky/engine/core/editing/TextIterator.h"
#include "sky/engine/core/frame/FrameHost.h"
#include "sky/engine/core/frame/FrameView.h"
#include "sky/engine/core/frame/LocalDOMWindow.h"
#include "sky/engine/core/frame/Settings.h"
#include "sky/engine/core/html/HTMLAnchorElement.h"
#include "sky/engine/core/inspector/ConsoleMessage.h"
#include "sky/engine/core/loader/MojoLoader.h"
#include "sky/engine/core/page/EventHandler.h"
#include "sky/engine/core/page/FocusController.h"
#include "sky/engine/core/page/Page.h"
#include "sky/engine/core/rendering/HitTestResult.h"
#include "sky/engine/core/rendering/RenderBox.h"
#include "sky/engine/core/rendering/RenderLayer.h"
#include "sky/engine/core/rendering/RenderObject.h"
#include "sky/engine/core/rendering/RenderTreeAsText.h"
#include "sky/engine/core/rendering/RenderView.h"
#include "sky/engine/core/rendering/style/StyleInheritedData.h"
#include "sky/engine/platform/clipboard/ClipboardUtilities.h"
#include "sky/engine/platform/fonts/FontCache.h"
#include "sky/engine/platform/graphics/GraphicsContext.h"
#include "sky/engine/platform/graphics/skia/SkiaUtils.h"
#include "sky/engine/platform/heap/Handle.h"
#include "sky/engine/platform/network/ResourceRequest.h"
#include "sky/engine/platform/TraceEvent.h"
#include "sky/engine/platform/weborigin/KURL.h"
#include "sky/engine/platform/weborigin/SecurityPolicy.h"
#include "sky/engine/public/platform/Platform.h"
#include "sky/engine/public/platform/WebFloatPoint.h"
#include "sky/engine/public/platform/WebFloatRect.h"
#include "sky/engine/public/platform/WebLayer.h"
#include "sky/engine/public/platform/WebPoint.h"
#include "sky/engine/public/platform/WebRect.h"
#include "sky/engine/public/platform/WebSize.h"
#include "sky/engine/public/platform/WebURLError.h"
#include "sky/engine/public/platform/WebVector.h"
#include "sky/engine/public/web/WebConsoleMessage.h"
#include "sky/engine/public/web/WebDocument.h"
#include "sky/engine/public/web/WebElement.h"
#include "sky/engine/public/web/WebFrameClient.h"
#include "sky/engine/public/web/WebNode.h"
#include "sky/engine/public/web/WebRange.h"
#include "sky/engine/public/web/WebScriptSource.h"
#include "sky/engine/web/CompositionUnderlineVectorBuilder.h"
#include "sky/engine/web/WebViewImpl.h"
#include "sky/engine/wtf/CurrentTime.h"
#include "sky/engine/wtf/HashMap.h"
namespace blink {
static int frameCount = 0;
// Key for a StatsCounter tracking how many WebFrames are active.
static const char webFrameActiveCount[] = "WebFrameActiveCount";
static void frameContentAsPlainText(size_t maxChars, LocalFrame* frame, StringBuilder& output)
{
Document* document = frame->document();
if (!document)
return;
if (!frame->view())
return;
// Select the document body.
RefPtr<Range> range(document->createRange());
TrackExceptionState exceptionState;
range->selectNodeContents(document, exceptionState);
if (!exceptionState.had_exception()) {
// The text iterator will walk nodes giving us text. This is similar to
// the plainText() function in core/editing/TextIterator.h, but we implement the maximum
// size and also copy the results directly into a wstring, avoiding the
// string conversion.
for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
it.appendTextToStringBuilder(output, 0, maxChars - output.length());
if (output.length() >= maxChars)
return; // Filled up the buffer.
}
}
}
// WebFrame -------------------------------------------------------------------
int WebFrame::instanceCount()
{
return frameCount;
}
bool WebLocalFrameImpl::isWebLocalFrame() const
{
return true;
}
WebLocalFrame* WebLocalFrameImpl::toWebLocalFrame()
{
return this;
}
void WebLocalFrameImpl::close()
{
m_client = 0;
deref(); // Balances ref() acquired in WebFrame::create
}
WebSize WebLocalFrameImpl::contentsSize() const
{
return frame()->view()->size();
}
bool WebLocalFrameImpl::hasVisibleContent() const
{
return frame()->view()->width() > 0 && frame()->view()->height() > 0;
}
WebRect WebLocalFrameImpl::visibleContentRect() const
{
return frame()->view()->frameRect();
}
WebView* WebLocalFrameImpl::view() const
{
return viewImpl();
}
WebDocument WebLocalFrameImpl::document() const
{
if (!frame() || !frame()->document())
return WebDocument();
return WebDocument(frame()->document());
}
void WebLocalFrameImpl::executeScript(const WebScriptSource& source)
{
ASSERT(frame());
// TODO(dart)
}
void WebLocalFrameImpl::addMessageToConsole(const WebConsoleMessage& message)
{
ASSERT(frame());
MessageLevel webCoreMessageLevel;
switch (message.level) {
case WebConsoleMessage::LevelDebug:
webCoreMessageLevel = DebugMessageLevel;
break;
case WebConsoleMessage::LevelLog:
webCoreMessageLevel = LogMessageLevel;
break;
case WebConsoleMessage::LevelWarning:
webCoreMessageLevel = WarningMessageLevel;
break;
case WebConsoleMessage::LevelError:
webCoreMessageLevel = ErrorMessageLevel;
break;
default:
ASSERT_NOT_REACHED();
return;
}
frame()->document()->addConsoleMessage(ConsoleMessage::create(OtherMessageSource, webCoreMessageLevel, message.text));
}
void WebLocalFrameImpl::collectGarbage()
{
// TODO(dart): Implement.
}
void WebLocalFrameImpl::loadFromDataPipeWithURL(mojo::ScopedDataPipeConsumerHandle responseStream, const WebURL& url)
{
frame()->mojoLoader().init(url);
frame()->mojoLoader().parse(responseStream.Pass());
}
void WebLocalFrameImpl::load(const WebURL& url)
{
frame()->mojoLoader().init(url);
m_fetcher = adoptPtr(new MojoFetcher(this, url));
}
void WebLocalFrameImpl::OnReceivedResponse(mojo::URLResponsePtr response)
{
m_fetcher.clear();
if (response->body.is_valid()) {
frame()->mojoLoader().parse(response->body.Pass());
return;
}
LOG(ERROR) << "Response for " << response->url
<< " (status " << response->status_code << ") has no body.";
// TODO(eseidel): This is a hack, but makes debugging way easier.
mojo::DataPipe pipe;
frame()->mojoLoader().parse(pipe.consumer_handle.Pass());
std::string error_response = base::StringPrintf(
"<error><h style='display: paragraph'>Empty Body</h>"
"<l style='display: paragraph'>%d %s</l>"
"<m style='display: paragraph'>%s</m></t></error>",
response->status_code, response->status_line.get().c_str(),
response->error->description.get().c_str());
uint32_t length = error_response.length();
MojoWriteData(pipe.producer_handle.get().value(),
error_response.data(),
&length,
MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
}
void WebLocalFrameImpl::replaceSelection(const WebString& text)
{
bool selectReplacement = false;
bool smartReplace = true;
frame()->editor().replaceSelectionWithText(text, selectReplacement, smartReplace);
}
void WebLocalFrameImpl::insertText(const WebString& text)
{
if (frame()->inputMethodController().hasComposition())
frame()->inputMethodController().confirmComposition(text);
else
frame()->editor().insertText(text, 0);
}
void WebLocalFrameImpl::setMarkedText(const WebString& text, unsigned location, unsigned length)
{
Vector<CompositionUnderline> decorations;
frame()->inputMethodController().setComposition(text, decorations, location, length);
}
void WebLocalFrameImpl::unmarkText()
{
frame()->inputMethodController().cancelComposition();
}
bool WebLocalFrameImpl::hasMarkedText() const
{
return frame()->inputMethodController().hasComposition();
}
bool WebLocalFrameImpl::executeCommand(const WebString& name, const WebNode& node)
{
ASSERT(frame());
if (name.length() <= 2)
return false;
// Since we don't have NSControl, we will convert the format of command
// string and call the function on Editor directly.
String command = name;
// Make sure the first letter is upper case.
command.replace(0, 1, command.substring(0, 1).upper());
// Remove the trailing ':' if existing.
if (command[command.length() - 1] == UChar(':'))
command = command.substring(0, command.length() - 1);
return frame()->editor().executeCommand(command);
}
bool WebLocalFrameImpl::executeCommand(const WebString& name, const WebString& value, const WebNode& node)
{
ASSERT(frame());
return frame()->editor().executeCommand(name, value);
}
bool WebLocalFrameImpl::isCommandEnabled(const WebString& name) const
{
ASSERT(frame());
return frame()->editor().command(name).isEnabled();
}
void WebLocalFrameImpl::enableContinuousSpellChecking(bool enable)
{
if (enable == isContinuousSpellCheckingEnabled())
return;
frame()->spellChecker().toggleContinuousSpellChecking();
}
bool WebLocalFrameImpl::isContinuousSpellCheckingEnabled() const
{
return frame()->spellChecker().isContinuousSpellCheckingEnabled();
}
void WebLocalFrameImpl::requestTextChecking(const WebElement& webElement)
{
if (webElement.isNull())
return;
frame()->spellChecker().requestTextChecking(*webElement.constUnwrap<Element>());
}
void WebLocalFrameImpl::replaceMisspelledRange(const WebString& text)
{
frame()->spellChecker().replaceMisspelledRange(text);
}
void WebLocalFrameImpl::removeSpellingMarkers()
{
frame()->spellChecker().removeSpellingMarkers();
}
bool WebLocalFrameImpl::hasSelection() const
{
// frame()->selection()->isNone() never returns true.
return frame()->selection().start() != frame()->selection().end();
}
WebRange WebLocalFrameImpl::selectionRange() const
{
return frame()->selection().toNormalizedRange();
}
WebString WebLocalFrameImpl::selectionAsText() const
{
RefPtr<Range> range = frame()->selection().toNormalizedRange();
if (!range)
return WebString();
String text = range->text();
replaceNBSPWithSpace(text);
return text;
}
void WebLocalFrameImpl::selectWordAroundPosition(LocalFrame* frame, VisiblePosition position)
{
VisibleSelection selection(position);
selection.expandUsingGranularity(WordGranularity);
TextGranularity granularity = selection.isRange() ? WordGranularity : CharacterGranularity;
frame->selection().setSelection(selection, granularity);
}
bool WebLocalFrameImpl::selectWordAroundCaret()
{
FrameSelection& selection = frame()->selection();
if (selection.isNone() || selection.isRange())
return false;
selectWordAroundPosition(frame(), selection.selection().visibleStart());
return true;
}
void WebLocalFrameImpl::selectRange(const WebPoint& base, const WebPoint& extent)
{
moveRangeSelection(base, extent);
}
void WebLocalFrameImpl::selectRange(const WebRange& webRange)
{
if (RefPtr<Range> range = static_cast<PassRefPtr<Range> >(webRange))
frame()->selection().setSelectedRange(range.get(), VP_DEFAULT_AFFINITY, FrameSelection::NonDirectional, NotUserTriggered);
}
void WebLocalFrameImpl::moveRangeSelection(const WebPoint& base, const WebPoint& extent)
{
VisiblePosition basePosition = visiblePositionForWindowPoint(base);
VisiblePosition extentPosition = visiblePositionForWindowPoint(extent);
VisibleSelection newSelection = VisibleSelection(basePosition, extentPosition);
frame()->selection().setSelection(newSelection, CharacterGranularity);
}
void WebLocalFrameImpl::moveCaretSelection(const WebPoint& point)
{
Element* editable = frame()->selection().rootEditableElement();
if (!editable)
return;
VisiblePosition position = visiblePositionForWindowPoint(point);
frame()->selection().moveTo(position, UserTriggered);
}
bool WebLocalFrameImpl::setEditableSelectionOffsets(int start, int end)
{
return frame()->inputMethodController().setEditableSelectionOffsets(PlainTextRange(start, end));
}
bool WebLocalFrameImpl::setCompositionFromExistingText(int compositionStart, int compositionEnd, const WebVector<WebCompositionUnderline>& underlines)
{
if (!frame()->editor().canEdit())
return false;
InputMethodController& inputMethodController = frame()->inputMethodController();
inputMethodController.cancelComposition();
if (compositionStart == compositionEnd)
return true;
inputMethodController.setCompositionFromExistingText(CompositionUnderlineVectorBuilder(underlines), compositionStart, compositionEnd);
return true;
}
void WebLocalFrameImpl::extendSelectionAndDelete(int before, int after)
{
frame()->inputMethodController().extendSelectionAndDelete(before, after);
}
void WebLocalFrameImpl::setCaretVisible(bool visible)
{
frame()->selection().setCaretVisible(visible);
}
VisiblePosition WebLocalFrameImpl::visiblePositionForWindowPoint(const WebPoint& point)
{
HitTestRequest request = HitTestRequest::Move | HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping;
HitTestResult result(frame()->view()->windowToContents(roundedIntPoint(FloatPoint(point))));
frame()->document()->renderView()->hitTest(request, result.hitTestLocation(), result);
if (Node* node = result.targetNode())
return frame()->selection().selection().visiblePositionRespectingEditingBoundary(result.localPoint(), node);
return VisiblePosition();
}
WebString WebLocalFrameImpl::contentAsText(size_t maxChars) const
{
if (!frame())
return WebString();
StringBuilder text;
frameContentAsPlainText(maxChars, frame(), text);
return text.toString();
}
WebString WebLocalFrameImpl::renderTreeAsText(RenderAsTextControls toShow) const
{
RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal;
if (toShow & RenderAsTextDebug)
behavior |= RenderAsTextShowCompositedLayers | RenderAsTextShowAddresses | RenderAsTextShowIDAndClass | RenderAsTextShowLayerNesting;
return externalRepresentation(frame(), behavior);
}
bool WebLocalFrameImpl::selectionStartHasSpellingMarkerFor(int from, int length) const
{
if (!frame())
return false;
return frame()->spellChecker().selectionStartHasSpellingMarkerFor(from, length);
}
// WebLocalFrameImpl public ---------------------------------------------------------
WebLocalFrame* WebLocalFrame::create(WebFrameClient* client)
{
return WebLocalFrameImpl::create(client);
}
WebLocalFrameImpl* WebLocalFrameImpl::create(WebFrameClient* client)
{
return adoptRef(new WebLocalFrameImpl(client)).leakRef();
}
WebLocalFrameImpl::WebLocalFrameImpl(WebFrameClient* client)
: m_frameLoaderClientImpl(this)
, m_client(client)
, m_inputEventsScaleFactorForEmulation(1)
{
Platform::current()->incrementStatsCounter(webFrameActiveCount);
frameCount++;
}
WebLocalFrameImpl::~WebLocalFrameImpl()
{
Platform::current()->decrementStatsCounter(webFrameActiveCount);
frameCount--;
}
void WebLocalFrameImpl::setCoreFrame(PassRefPtr<LocalFrame> frame)
{
m_frame = frame;
}
PassRefPtr<LocalFrame> WebLocalFrameImpl::initializeCoreFrame(FrameHost* host)
{
RefPtr<LocalFrame> frame = LocalFrame::create(&m_frameLoaderClientImpl, host);
setCoreFrame(frame);
return frame;
}
void WebLocalFrameImpl::createFrameView()
{
TRACE_EVENT0("blink", "WebLocalFrameImpl::createFrameView");
ASSERT(frame()); // If frame() doesn't exist, we probably didn't init properly.
WebViewImpl* webView = viewImpl();
frame()->createView(webView->size(), webView->baseBackgroundColor(), webView->isTransparent());
frame()->view()->setInputEventsTransformForEmulation(m_inputEventsOffsetForEmulation, m_inputEventsScaleFactorForEmulation);
}
WebLocalFrameImpl* WebLocalFrameImpl::fromFrame(LocalFrame* frame)
{
if (!frame)
return 0;
return fromFrame(*frame);
}
WebLocalFrameImpl* WebLocalFrameImpl::fromFrame(LocalFrame& frame)
{
FrameLoaderClient* client = frame.loaderClient();
if (!client || !client->isFrameLoaderClientImpl())
return 0;
return toFrameLoaderClientImpl(client)->webFrame();
}
WebViewImpl* WebLocalFrameImpl::viewImpl() const
{
if (!frame())
return 0;
return WebViewImpl::fromPage(frame()->page());
}
void WebLocalFrameImpl::didFail(const ResourceError& error)
{
if (!client())
return;
client()->didFailLoad(this, error);
}
void WebLocalFrameImpl::setInputEventsTransformForEmulation(const IntSize& offset, float contentScaleFactor)
{
m_inputEventsOffsetForEmulation = offset;
m_inputEventsScaleFactorForEmulation = contentScaleFactor;
if (frame()->view())
frame()->view()->setInputEventsTransformForEmulation(m_inputEventsOffsetForEmulation, m_inputEventsScaleFactorForEmulation);
}
} // namespace blink