blob: 99decb8f1af548b9d189110571be482109f1fb25 [file] [log] [blame]
/*
* 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