diff options
Diffstat (limited to 'webkit/api/src')
23 files changed, 6433 insertions, 90 deletions
diff --git a/webkit/api/src/AutocompletePopupMenuClient.cpp b/webkit/api/src/AutocompletePopupMenuClient.cpp new file mode 100644 index 0000000..96cccf5 --- /dev/null +++ b/webkit/api/src/AutocompletePopupMenuClient.cpp @@ -0,0 +1,178 @@ +/* + * 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. + */ + +#include "config.h" +#include "AutocompletePopupMenuClient.h" + +#include "CSSValueKeywords.h" +#include "CSSStyleSelector.h" +#include "FrameView.h" +#include "HTMLInputElement.h" +#include "RenderTheme.h" +#include "WebVector.h" +#include "WebViewImpl.h" + +using namespace WebCore; + +namespace WebKit { + +AutocompletePopupMenuClient::AutocompletePopupMenuClient(WebViewImpl* webView) + : m_textField(0) + , m_selectedIndex(0) + , m_webView(webView) +{ +} + +AutocompletePopupMenuClient::~AutocompletePopupMenuClient() +{ +} + +void AutocompletePopupMenuClient::initialize( + HTMLInputElement* textField, + const WebVector<WebString>& suggestions, + int defaultSuggestionIndex) +{ + ASSERT(defaultSuggestionIndex < static_cast<int>(suggestions.size())); + m_textField = textField; + m_selectedIndex = defaultSuggestionIndex; + setSuggestions(suggestions); + + FontDescription fontDescription; + m_webView->theme()->systemFont(CSSValueWebkitControl, fontDescription); + // Use a smaller font size to match IE/Firefox. + // FIXME: http://crbug.com/7376 use the system size instead of a + // fixed font size value. + fontDescription.setComputedSize(12.0); + Font font(fontDescription, 0, 0); + font.update(textField->document()->styleSelector()->fontSelector()); + // The direction of text in popup menu is set the same as the direction of + // the input element: textField. + m_style.set(new PopupMenuStyle(Color::black, Color::white, font, true, + Length(WebCore::Fixed), + textField->renderer()->style()->direction())); +} + +void AutocompletePopupMenuClient::valueChanged(unsigned listIndex, bool fireEvents) +{ + m_textField->setValue(m_suggestions[listIndex]); + EditorClientImpl* editor = + static_cast<EditorClientImpl*>(m_webView->page()->editorClient()); + ASSERT(editor); + editor->onAutofillSuggestionAccepted( + static_cast<HTMLInputElement*>(m_textField.get())); +} + +String AutocompletePopupMenuClient::itemText(unsigned listIndex) const +{ + return m_suggestions[listIndex]; +} + +PopupMenuStyle AutocompletePopupMenuClient::itemStyle(unsigned listIndex) const +{ + return *m_style; +} + +PopupMenuStyle AutocompletePopupMenuClient::menuStyle() const +{ + return *m_style; +} + +int AutocompletePopupMenuClient::clientPaddingLeft() const +{ + // Bug http://crbug.com/7708 seems to indicate the style can be NULL. + RenderStyle* style = textFieldStyle(); + return style ? m_webView->theme()->popupInternalPaddingLeft(style) : 0; +} + +int AutocompletePopupMenuClient::clientPaddingRight() const +{ + // Bug http://crbug.com/7708 seems to indicate the style can be NULL. + RenderStyle* style = textFieldStyle(); + return style ? m_webView->theme()->popupInternalPaddingRight(style) : 0; +} + +void AutocompletePopupMenuClient::popupDidHide() +{ + m_webView->autoCompletePopupDidHide(); +} + +void AutocompletePopupMenuClient::setTextFromItem(unsigned listIndex) +{ + m_textField->setValue(m_suggestions[listIndex]); +} + +FontSelector* AutocompletePopupMenuClient::fontSelector() const +{ + return m_textField->document()->styleSelector()->fontSelector(); +} + +HostWindow* AutocompletePopupMenuClient::hostWindow() const +{ + return m_textField->document()->view()->hostWindow(); +} + +PassRefPtr<Scrollbar> AutocompletePopupMenuClient::createScrollbar( + ScrollbarClient* client, + ScrollbarOrientation orientation, + ScrollbarControlSize size) +{ + return Scrollbar::createNativeScrollbar(client, orientation, size); +} + +void AutocompletePopupMenuClient::setSuggestions(const WebVector<WebString>& suggestions) +{ + m_suggestions.clear(); + for (size_t i = 0; i < suggestions.size(); ++i) + m_suggestions.append(suggestions[i]); + // Try to preserve selection if possible. + if (m_selectedIndex >= static_cast<int>(suggestions.size())) + m_selectedIndex = -1; +} + +void AutocompletePopupMenuClient::removeItemAtIndex(int index) +{ + ASSERT(index >= 0 && index < static_cast<int>(m_suggestions.size())); + m_suggestions.remove(index); +} + +RenderStyle* AutocompletePopupMenuClient::textFieldStyle() const +{ + RenderStyle* style = m_textField->computedStyle(); + if (!style) { + // It seems we can only have an NULL style in a TextField if the + // node is dettached, in which case we the popup shoud not be + // showing. Please report this in http://crbug.com/7708 and + // include the page you were visiting. + ASSERT_NOT_REACHED(); + } + return style; +} + +} // namespace WebKit diff --git a/webkit/api/src/AutocompletePopupMenuClient.h b/webkit/api/src/AutocompletePopupMenuClient.h new file mode 100644 index 0000000..ad24e54 --- /dev/null +++ b/webkit/api/src/AutocompletePopupMenuClient.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#include "PopupMenuClient.h" + +namespace WebCore { +class HTMLInputElement; +class PopupMenuStyle; +class RenderStyle; +} + +namespace WebKit { +class WebString; +class WebViewImpl; +template <typename T> class WebVector; + +// AutocompletePopupMenuClient +class AutocompletePopupMenuClient : public WebCore::PopupMenuClient { +public: + AutocompletePopupMenuClient(WebViewImpl* webview); + ~AutocompletePopupMenuClient(); + + void initialize(WebCore::HTMLInputElement*, + const WebVector<WebString>& suggestions, + int defaultSuggestionIndex); + + WebCore::HTMLInputElement* textField() const { return m_textField.get(); } + + void setSuggestions(const WebVector<WebString>&); + void removeItemAtIndex(int index); + + // WebCore::PopupMenuClient methods: + virtual void valueChanged(unsigned listIndex, bool fireEvents = true); + virtual WebCore::String itemText(unsigned listIndex) const; + virtual WebCore::String itemToolTip(unsigned lastIndex) const { return WebCore::String(); } + virtual bool itemIsEnabled(unsigned listIndex) const { return true; } + virtual WebCore::PopupMenuStyle itemStyle(unsigned listIndex) const; + virtual WebCore::PopupMenuStyle menuStyle() const; + virtual int clientInsetLeft() const { return 0; } + virtual int clientInsetRight() const { return 0; } + virtual int clientPaddingLeft() const; + virtual int clientPaddingRight() const; + virtual int listSize() const { return m_suggestions.size(); } + virtual int selectedIndex() const { return m_selectedIndex; } + virtual void popupDidHide(); + virtual bool itemIsSeparator(unsigned listIndex) const { return false; } + virtual bool itemIsLabel(unsigned listIndex) const { return false; } + virtual bool itemIsSelected(unsigned listIndex) const { return false; } + virtual bool shouldPopOver() const { return false; } + virtual bool valueShouldChangeOnHotTrack() const { return false; } + virtual void setTextFromItem(unsigned listIndex); + virtual WebCore::FontSelector* fontSelector() const; + virtual WebCore::HostWindow* hostWindow() const; + virtual PassRefPtr<WebCore::Scrollbar> createScrollbar( + WebCore::ScrollbarClient* client, + WebCore::ScrollbarOrientation orientation, + WebCore::ScrollbarControlSize size); + +private: + WebCore::RenderStyle* textFieldStyle() const; + + RefPtr<WebCore::HTMLInputElement> m_textField; + Vector<WebCore::String> m_suggestions; + int m_selectedIndex; + WebViewImpl* m_webView; + OwnPtr<WebCore::PopupMenuStyle> m_style; +}; + +} // namespace WebKit diff --git a/webkit/api/src/BackForwardListClientImpl.cpp b/webkit/api/src/BackForwardListClientImpl.cpp index fc6defd..8feae32 100644 --- a/webkit/api/src/BackForwardListClientImpl.cpp +++ b/webkit/api/src/BackForwardListClientImpl.cpp @@ -33,9 +33,7 @@ #include "HistoryItem.h" #include "WebViewClient.h" - -// FIXME: Remove this once WebViewImpl moves out of glue/. -#include "webkit/glue/webview_impl.h" +#include "WebViewImpl.h" using namespace WebCore; @@ -52,13 +50,13 @@ BackForwardListClientImpl::~BackForwardListClientImpl() { } -void BackForwardListClientImpl::SetCurrentHistoryItem(HistoryItem* item) +void BackForwardListClientImpl::setCurrentHistoryItem(HistoryItem* item) { m_previousItem = m_currentItem; m_currentItem = item; } -HistoryItem* BackForwardListClientImpl::GetPreviousHistoryItem() const +HistoryItem* BackForwardListClientImpl::previousHistoryItem() const { return m_previousItem.get(); } @@ -70,7 +68,7 @@ void BackForwardListClientImpl::addItem(PassRefPtr<HistoryItem> item) // If WebCore adds a new HistoryItem, it means this is a new navigation (ie, // not a reload or back/forward). - m_webView->ObserveNewNavigation(); + m_webView->observeNewNavigation(); if (m_webView->client()) m_webView->client()->didAddHistoryItem(); diff --git a/webkit/api/src/BackForwardListClientImpl.h b/webkit/api/src/BackForwardListClientImpl.h index e498ea0..1d8beb0 100644 --- a/webkit/api/src/BackForwardListClientImpl.h +++ b/webkit/api/src/BackForwardListClientImpl.h @@ -33,9 +33,8 @@ #include "BackForwardList.h" -class WebViewImpl; - namespace WebKit { +class WebViewImpl; extern const char backForwardNavigationScheme[]; @@ -44,8 +43,8 @@ public: BackForwardListClientImpl(WebViewImpl* webview); ~BackForwardListClientImpl(); - void SetCurrentHistoryItem(WebCore::HistoryItem* item); - WebCore::HistoryItem* GetPreviousHistoryItem() const; + void setCurrentHistoryItem(WebCore::HistoryItem* item); + WebCore::HistoryItem* previousHistoryItem() const; private: // WebCore::BackForwardListClient methods: diff --git a/webkit/api/src/ChromeClientImpl.cpp b/webkit/api/src/ChromeClientImpl.cpp index a81c3aa..a28589c 100644 --- a/webkit/api/src/ChromeClientImpl.cpp +++ b/webkit/api/src/ChromeClientImpl.cpp @@ -56,24 +56,21 @@ #include "WebAccessibilityObject.h" #include "WebConsoleMessage.h" #include "WebCursorInfo.h" -#include "WebFileChooserCompletion.h" +#include "WebFileChooserCompletionImpl.h" #include "WebFrameClient.h" +#include "WebFrameImpl.h" #include "WebInputEvent.h" #include "WebKit.h" +#include "WebPopupMenuImpl.h" #include "WebPopupMenuInfo.h" #include "WebRect.h" #include "WebTextDirection.h" #include "WebURLRequest.h" #include "WebViewClient.h" -#include "WebFileChooserCompletionImpl.h" -#include "WebPopupMenuImpl.h" +#include "WebViewImpl.h" #include "WindowFeatures.h" #include "WrappedResourceRequest.h" -// FIXME: Remove these once they move out of glue/. -#include "webkit/glue/webframe_impl.h" -#include "webkit/glue/webview_impl.h" - using namespace WebCore; namespace WebKit { @@ -148,14 +145,14 @@ void ChromeClientImpl::focus() // If accessibility is enabled, we should notify assistive technology that // the active AccessibilityObject changed. - const Frame* frame = m_webView->GetFocusedWebCoreFrame(); + const Frame* frame = m_webView->focusedWebCoreFrame(); if (!frame) return; Document* doc = frame->document(); if (doc && doc->axObjectCache()->accessibilityEnabled()) { - Node* focusedNode = m_webView->GetFocusedNode(); + Node* focusedNode = m_webView->focusedWebCoreNode(); if (!focusedNode) { // Could not retrieve focused Node. @@ -202,7 +199,7 @@ Page* ChromeClientImpl::createWindow( return 0; WebViewImpl* newView = static_cast<WebViewImpl*>( - m_webView->client()->createView(WebFrameImpl::FromFrame(frame))); + m_webView->client()->createView(WebFrameImpl::fromFrame(frame))); if (!newView) return 0; @@ -210,13 +207,13 @@ Page* ChromeClientImpl::createWindow( // This corresponds to window.open(""), for example. if (!r.resourceRequest().isEmpty()) { WrappedResourceRequest request(r.resourceRequest()); - newView->main_frame()->loadRequest(request); + newView->mainFrame()->loadRequest(request); } return newView->page(); } -static inline bool CurrentEventShouldCauseBackgroundTab(const WebInputEvent* inputEvent) +static inline bool currentEventShouldCauseBackgroundTab(const WebInputEvent* inputEvent) { if (!inputEvent) return false; @@ -246,7 +243,7 @@ static inline bool CurrentEventShouldCauseBackgroundTab(const WebInputEvent* inp bool alt = mouseEvent->modifiers & WebMouseEvent::AltKey; bool meta = mouseEvent->modifiers & WebMouseEvent::MetaKey; - if (!WebViewImpl::NavigationPolicyFromMouseEvent(buttonNumber, ctrl, shift, alt, meta, &policy)) + if (!WebViewImpl::navigationPolicyFromMouseEvent(buttonNumber, ctrl, shift, alt, meta, &policy)) return false; return policy == WebNavigationPolicyNewBackgroundTab; @@ -270,7 +267,7 @@ void ChromeClientImpl::show() WebNavigationPolicy policy = WebNavigationPolicyNewForegroundTab; if (asPopup) policy = WebNavigationPolicyNewPopup; - if (CurrentEventShouldCauseBackgroundTab(WebViewImpl::current_input_event())) + if (currentEventShouldCauseBackgroundTab(WebViewImpl::currentInputEvent())) policy = WebNavigationPolicyNewBackgroundTab; m_webView->client()->show(policy); @@ -312,7 +309,7 @@ void ChromeClientImpl::setScrollbarsVisible(bool value) m_scrollbarsVisible = value; WebFrameImpl* web_frame = static_cast<WebFrameImpl*>(m_webView->mainFrame()); if (web_frame) - web_frame->SetAllowsScrolling(value); + web_frame->setAllowsScrolling(value); } bool ChromeClientImpl::scrollbarsVisible() @@ -359,7 +356,7 @@ bool ChromeClientImpl::runBeforeUnloadConfirmPanel(const String& message, Frame* { if (m_webView->client()) { return m_webView->client()->runModalBeforeUnloadDialog( - WebFrameImpl::FromFrame(frame), message); + WebFrameImpl::fromFrame(frame), message); } return false; } @@ -387,7 +384,7 @@ void ChromeClientImpl::runJavaScriptAlert(Frame* frame, const String& message) V8Proxy::processConsoleMessages(); #endif m_webView->client()->runModalAlertDialog( - WebFrameImpl::FromFrame(frame), message); + WebFrameImpl::fromFrame(frame), message); } } @@ -396,7 +393,7 @@ bool ChromeClientImpl::runJavaScriptConfirm(Frame* frame, const String& message) { if (m_webView->client()) { return m_webView->client()->runModalConfirmDialog( - WebFrameImpl::FromFrame(frame), message); + WebFrameImpl::fromFrame(frame), message); } return false; } @@ -410,7 +407,7 @@ bool ChromeClientImpl::runJavaScriptPrompt(Frame* frame, if (m_webView->client()) { WebString actualValue; bool ok = m_webView->client()->runModalPromptDialog( - WebFrameImpl::FromFrame(frame), + WebFrameImpl::fromFrame(frame), message, defaultValue, &actualValue); @@ -492,7 +489,7 @@ IntRect ChromeClientImpl::windowToScreen(const IntRect& rect) const { void ChromeClientImpl::contentsSizeChanged(Frame* frame, const IntSize& size) const { - WebFrameImpl* webframe = WebFrameImpl::FromFrame(frame); + WebFrameImpl* webframe = WebFrameImpl::fromFrame(frame); if (webframe->client()) webframe->client()->didChangeContentsSize(webframe, size); } @@ -527,7 +524,7 @@ void ChromeClientImpl::setToolTip(const String& tooltipText, TextDirection dir) void ChromeClientImpl::print(Frame* frame) { if (m_webView->client()) - m_webView->client()->printPage(WebFrameImpl::FromFrame(frame)); + m_webView->client()->printPage(WebFrameImpl::fromFrame(frame)); } void ChromeClientImpl::exceededDatabaseQuota(Frame* frame, const String& databaseName) @@ -613,7 +610,7 @@ void ChromeClientImpl::formStateDidChange(const Node* node) { // The current history item is not updated yet. That happens lazily when // WebFrame::currentHistoryItem is requested. - WebFrameImpl* webframe = WebFrameImpl::FromFrame(node->document()->frame()); + WebFrameImpl* webframe = WebFrameImpl::fromFrame(node->document()->frame()); if (webframe->client()) webframe->client()->didUpdateCurrentHistoryItem(webframe); } @@ -655,7 +652,7 @@ void ChromeClientImpl::getPopupMenuInfo(PopupContainer* popupContainer, #if ENABLE(NOTIFICATIONS) NotificationPresenter* ChromeClientImpl::notificationPresenter() const { - return m_webView->GetNotificationPresenter(); + return m_webView->notificationPresenterImpl(); } #endif diff --git a/webkit/api/src/ChromeClientImpl.h b/webkit/api/src/ChromeClientImpl.h index 427390a..9a1e443 100644 --- a/webkit/api/src/ChromeClientImpl.h +++ b/webkit/api/src/ChromeClientImpl.h @@ -33,8 +33,6 @@ #include "ChromeClientChromium.h" -class WebViewImpl; - namespace WebCore { class HTMLParserQuirks; class PopupContainer; @@ -43,6 +41,7 @@ struct WindowFeatures; } namespace WebKit { +class WebViewImpl; struct WebCursorInfo; struct WebPopupMenuInfo; diff --git a/webkit/api/src/ContextMenuClientImpl.cpp b/webkit/api/src/ContextMenuClientImpl.cpp index fd2a7a8..f32c72f 100644 --- a/webkit/api/src/ContextMenuClientImpl.cpp +++ b/webkit/api/src/ContextMenuClientImpl.cpp @@ -48,17 +48,14 @@ #include "Widget.h" #include "WebContextMenuData.h" -#include "WebFrame.h" +#include "WebDataSourceImpl.h" +#include "WebFrameImpl.h" #include "WebPoint.h" #include "WebString.h" #include "WebURL.h" #include "WebURLResponse.h" #include "WebViewClient.h" -#include "WebDataSourceImpl.h" -#undef LOG - -// FIXME: Temporary hack until WebViewImpl is in api/src. -#include "webkit/glue/webview_impl.h" +#include "WebViewImpl.h" using namespace WebCore; @@ -143,7 +140,7 @@ PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( // response to user input (Mouse event WM_RBUTTONDOWN, // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked // in response to the above input events before popping up the context menu. - if (!m_webView->context_menu_allowed()) + if (!m_webView->contextMenuAllowed()) return 0; HitTestResult r = defaultMenu->hitTestResult(); @@ -192,8 +189,8 @@ PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( data.frameEncoding = selectedFrame->loader()->encoding(); // Send the frame and page URLs in any case. - data.pageURL = urlFromFrame(m_webView->main_frame()->frame()); - if (selectedFrame != m_webView->main_frame()->frame()) + data.pageURL = urlFromFrame(m_webView->mainFrameImpl()->frame()); + if (selectedFrame != m_webView->mainFrameImpl()->frame()) data.frameURL = urlFromFrame(selectedFrame); if (r.isSelected()) @@ -202,7 +199,7 @@ PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( data.isEditable = false; if (r.isContentEditable()) { data.isEditable = true; - if (m_webView->GetFocusedWebCoreFrame()->editor()->isContinuousSpellCheckingEnabled()) { + if (m_webView->focusedWebCoreFrame()->editor()->isContinuousSpellCheckingEnabled()) { data.isSpellCheckingEnabled = true; data.misspelledWord = selectMisspelledWord(defaultMenu, selectedFrame); } @@ -216,22 +213,22 @@ PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( // Compute edit flags. data.editFlags = WebContextMenuData::CanDoNone; - if (m_webView->GetFocusedWebCoreFrame()->editor()->canUndo()) + if (m_webView->focusedWebCoreFrame()->editor()->canUndo()) data.editFlags |= WebContextMenuData::CanUndo; - if (m_webView->GetFocusedWebCoreFrame()->editor()->canRedo()) + if (m_webView->focusedWebCoreFrame()->editor()->canRedo()) data.editFlags |= WebContextMenuData::CanRedo; - if (m_webView->GetFocusedWebCoreFrame()->editor()->canCut()) + if (m_webView->focusedWebCoreFrame()->editor()->canCut()) data.editFlags |= WebContextMenuData::CanCut; - if (m_webView->GetFocusedWebCoreFrame()->editor()->canCopy()) + if (m_webView->focusedWebCoreFrame()->editor()->canCopy()) data.editFlags |= WebContextMenuData::CanCopy; - if (m_webView->GetFocusedWebCoreFrame()->editor()->canPaste()) + if (m_webView->focusedWebCoreFrame()->editor()->canPaste()) data.editFlags |= WebContextMenuData::CanPaste; - if (m_webView->GetFocusedWebCoreFrame()->editor()->canDelete()) + if (m_webView->focusedWebCoreFrame()->editor()->canDelete()) data.editFlags |= WebContextMenuData::CanDelete; // We can always select all... data.editFlags |= WebContextMenuData::CanSelectAll; - WebFrame* selected_web_frame = WebFrameImpl::FromFrame(selectedFrame); + WebFrame* selected_web_frame = WebFrameImpl::fromFrame(selectedFrame); if (m_webView->client()) m_webView->client()->showContextMenu(selected_web_frame, data); diff --git a/webkit/api/src/ContextMenuClientImpl.h b/webkit/api/src/ContextMenuClientImpl.h index 712891c..a19b247 100644 --- a/webkit/api/src/ContextMenuClientImpl.h +++ b/webkit/api/src/ContextMenuClientImpl.h @@ -33,9 +33,8 @@ #include "ContextMenuClient.h" -class WebViewImpl; - namespace WebKit { + class WebViewImpl; class ContextMenuClientImpl : public WebCore::ContextMenuClient { public: diff --git a/webkit/api/src/DragClientImpl.cpp b/webkit/api/src/DragClientImpl.cpp index 77c5ad8..5d8a9c3 100644 --- a/webkit/api/src/DragClientImpl.cpp +++ b/webkit/api/src/DragClientImpl.cpp @@ -36,9 +36,7 @@ #include "Frame.h" #include "WebDragData.h" #include "WebViewClient.h" - -// FIXME: Remove this once WebViewImpl moves out of glue/. -#include "webkit/glue/webview_impl.h" +#include "WebViewImpl.h" using namespace WebCore; @@ -85,7 +83,7 @@ void DragClientImpl::startDrag(DragImageRef dragImage, if (!clipboard->sourceOperation(dragOperationMask)) dragOperationMask = DragOperationEvery; - m_webView->StartDragging( + m_webView->startDragging( eventPos, dragData, static_cast<WebDragOperationsMask>(dragOperationMask)); } diff --git a/webkit/api/src/DragClientImpl.h b/webkit/api/src/DragClientImpl.h index 6eea773..7012370 100644 --- a/webkit/api/src/DragClientImpl.h +++ b/webkit/api/src/DragClientImpl.h @@ -41,9 +41,8 @@ class IntPoint; class KURL; } -class WebViewImpl; - namespace WebKit { +class WebViewImpl; class DragClientImpl : public WebCore::DragClient { public: diff --git a/webkit/api/src/EditorClientImpl.cpp b/webkit/api/src/EditorClientImpl.cpp index 058b892..d604d68 100644 --- a/webkit/api/src/EditorClientImpl.cpp +++ b/webkit/api/src/EditorClientImpl.cpp @@ -41,15 +41,16 @@ #include "PlatformString.h" #include "RenderObject.h" +#include "DOMUtilitiesPrivate.h" +#include "PasswordAutocompleteListener.h" #include "WebEditingAction.h" +#include "WebFrameImpl.h" #include "WebKit.h" #include "WebNode.h" #include "WebRange.h" #include "WebTextAffinity.h" #include "WebViewClient.h" -#include "DOMUtilitiesPrivate.h" -#include "PasswordAutocompleteListener.h" -#include "webkit/glue/webview_impl.h" +#include "WebViewImpl.h" using namespace WebCore; @@ -114,7 +115,7 @@ bool EditorClientImpl::shouldSpellcheckByDefault() { // Spellcheck should be enabled for all editable areas (such as textareas, // contentEditable regions, and designMode docs), except text inputs. - const Frame* frame = m_webView->GetFocusedWebCoreFrame(); + const Frame* frame = m_webView->focusedWebCoreFrame(); if (!frame) return false; const Editor* editor = frame->editor(); @@ -265,7 +266,7 @@ void EditorClientImpl::didBeginEditing() void EditorClientImpl::respondToChangedSelection() { if (m_webView->client()) { - Frame* frame = m_webView->GetFocusedWebCoreFrame(); + Frame* frame = m_webView->focusedWebCoreFrame(); if (frame) m_webView->client()->didChangeSelection(!frame->selection()->isRange()); } @@ -653,7 +654,7 @@ void EditorClientImpl::textFieldDidEndEditing(Element* element) m_autofillTimer.stop(); // Hide any showing popup. - m_webView->HideAutoCompletePopup(); + m_webView->hideAutoCompletePopup(); if (!m_webView->client()) return; // The page is getting closed, don't fill the password. @@ -663,8 +664,8 @@ void EditorClientImpl::textFieldDidEndEditing(Element* element) if (!inputElement) return; - WebFrameImpl* webframe = WebFrameImpl::FromFrame(inputElement->document()->frame()); - PasswordAutocompleteListener* listener = webframe->GetPasswordListener(inputElement); + WebFrameImpl* webframe = WebFrameImpl::fromFrame(inputElement->document()->frame()); + PasswordAutocompleteListener* listener = webframe->getPasswordListener(inputElement); if (!listener) return; @@ -744,15 +745,15 @@ void EditorClientImpl::doAutofill(Timer<EditorClientImpl>* timer) && inputElement->selectionEnd() == static_cast<int>(value.length()); if ((!args->autofillOnEmptyValue && value.isEmpty()) || !isCaretAtEnd) { - m_webView->HideAutoCompletePopup(); + m_webView->hideAutoCompletePopup(); return; } // First let's see if there is a password listener for that element. // We won't trigger form autofill in that case, as having both behavior on // a node would be confusing. - WebFrameImpl* webframe = WebFrameImpl::FromFrame(inputElement->document()->frame()); - PasswordAutocompleteListener* listener = webframe->GetPasswordListener(inputElement); + WebFrameImpl* webframe = WebFrameImpl::fromFrame(inputElement->document()->frame()); + PasswordAutocompleteListener* listener = webframe->getPasswordListener(inputElement); if (listener) { if (args->autofillFormOnly) return; @@ -780,8 +781,8 @@ void EditorClientImpl::cancelPendingAutofill() void EditorClientImpl::onAutofillSuggestionAccepted(HTMLInputElement* textField) { - WebFrameImpl* webframe = WebFrameImpl::FromFrame(textField->document()->frame()); - PasswordAutocompleteListener* listener = webframe->GetPasswordListener(textField); + WebFrameImpl* webframe = WebFrameImpl::fromFrame(textField->document()->frame()); + PasswordAutocompleteListener* listener = webframe->getPasswordListener(textField); // Password listeners need to autocomplete other fields that depend on the // input element with autofill suggestions. if (listener) diff --git a/webkit/api/src/EditorClientImpl.h b/webkit/api/src/EditorClientImpl.h index 55cd0b7..fd08b4d 100644 --- a/webkit/api/src/EditorClientImpl.h +++ b/webkit/api/src/EditorClientImpl.h @@ -39,9 +39,8 @@ namespace WebCore { class HTMLInputElement; } -class WebViewImpl; - namespace WebKit { +class WebViewImpl; class EditorClientImpl : public WebCore::EditorClient { public: diff --git a/webkit/api/src/FrameLoaderClientImpl.cpp b/webkit/api/src/FrameLoaderClientImpl.cpp new file mode 100644 index 0000000..3b7a906 --- /dev/null +++ b/webkit/api/src/FrameLoaderClientImpl.cpp @@ -0,0 +1,1405 @@ +/* + * 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. + */ + +#include "config.h" +#include "FrameLoaderClientImpl.h" + +#include "Chrome.h" +#include "CString.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "HTMLAppletElement.h" +#include "HTMLFormElement.h" // needed by FormState.h +#include "HTMLNames.h" +#include "FormState.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "HitTestResult.h" +#include "MIMETypeRegistry.h" +#include "MouseEvent.h" +#include "Page.h" +#include "PlatformString.h" +#include "PluginData.h" +#include "StringExtras.h" +#include "WebForm.h" +#include "WebFrameClient.h" +#include "WebFrameImpl.h" +#include "WebNode.h" +#include "WebPlugin.h" +#include "WebPluginParams.h" +#include "WebSecurityOrigin.h" +#include "WebURL.h" +#include "WebURLError.h" +#include "WebVector.h" +#include "WebViewClient.h" +#include "WebViewImpl.h" +#include "WebDataSourceImpl.h" +#include "WebPluginContainerImpl.h" +#include "WebPluginLoadObserver.h" +#include "WindowFeatures.h" +#include "WrappedResourceRequest.h" +#include "WrappedResourceResponse.h" + +// FIXME: remove these +#include "googleurl/src/gurl.h" +#include "net/base/mime_util.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webdevtoolsagent_impl.h" +#include "webkit/glue/webkit_glue.h" + +using namespace WebCore; + +namespace WebKit { + +// Domain for internal error codes. +static const char internalErrorDomain[] = "WebKit"; + +// An internal error code. Used to note a policy change error resulting from +// dispatchDecidePolicyForMIMEType not passing the PolicyUse option. +enum { + PolicyChangeError = -10000, +}; + +FrameLoaderClientImpl::FrameLoaderClientImpl(WebFrameImpl* frame) + : m_webFrame(frame) + , m_hasRepresentation(false) + , m_sentInitialResponseToPlugin(false) + , m_nextNavigationPolicy(WebNavigationPolicyIgnore) { +} + +FrameLoaderClientImpl::~FrameLoaderClientImpl() { +} + +void FrameLoaderClientImpl::frameLoaderDestroyed() +{ + // When the WebFrame was created, it had an extra reference given to it on + // behalf of the Frame. Since the WebFrame owns us, this extra ref also + // serves to keep us alive until the FrameLoader is done with us. The + // FrameLoader calls this method when it's going away. Therefore, we balance + // out that extra reference, which may cause 'this' to be deleted. + m_webFrame->closing(); + m_webFrame->deref(); +} + +void FrameLoaderClientImpl::windowObjectCleared() +{ + if (m_webFrame->client()) + m_webFrame->client()->didClearWindowObject(m_webFrame); + + WebViewImpl* webview = m_webFrame->viewImpl(); + if (webview) { + WebDevToolsAgentImpl* toolsAgent = webview->devToolsAgentImpl(); + if (toolsAgent) + toolsAgent->WindowObjectCleared(m_webFrame); + } +} + +void FrameLoaderClientImpl::documentElementAvailable() +{ + if (m_webFrame->client()) + m_webFrame->client()->didCreateDocumentElement(m_webFrame); +} + +void FrameLoaderClientImpl::didCreateScriptContextForFrame() +{ + if (m_webFrame->client()) + m_webFrame->client()->didCreateScriptContext(m_webFrame); +} + +void FrameLoaderClientImpl::didDestroyScriptContextForFrame() +{ + if (m_webFrame->client()) + m_webFrame->client()->didDestroyScriptContext(m_webFrame); +} + +void FrameLoaderClientImpl::didCreateIsolatedScriptContext() +{ + if (m_webFrame->client()) + m_webFrame->client()->didCreateIsolatedScriptContext(m_webFrame); +} + +void FrameLoaderClientImpl::didPerformFirstNavigation() const +{ +} + +void FrameLoaderClientImpl::registerForIconNotification(bool) +{ +} + +bool FrameLoaderClientImpl::hasWebView() const +{ + return m_webFrame->viewImpl() != 0; +} + +bool FrameLoaderClientImpl::hasFrameView() const +{ + // The Mac port has this notion of a WebFrameView, which seems to be + // some wrapper around an NSView. Since our equivalent is HWND, I guess + // we have a "frameview" whenever we have the toplevel HWND. + return m_webFrame->viewImpl() != 0; +} + +void FrameLoaderClientImpl::makeDocumentView() +{ + m_webFrame->createFrameView(); +} + +void FrameLoaderClientImpl::makeRepresentation(DocumentLoader*) +{ + m_hasRepresentation = true; +} + +void FrameLoaderClientImpl::forceLayout() +{ + // FIXME +} + +void FrameLoaderClientImpl::forceLayoutForNonHTML() +{ + // FIXME +} + +void FrameLoaderClientImpl::setCopiesOnScroll() +{ + // FIXME +} + +void FrameLoaderClientImpl::detachedFromParent2() +{ + // Nothing to do here. +} + +void FrameLoaderClientImpl::detachedFromParent3() +{ + // Close down the proxy. The purpose of this change is to make the + // call to ScriptController::clearWindowShell a no-op when called from + // Frame::pageDestroyed. Without this change, this call to clearWindowShell + // will cause a crash. If you remove/modify this, just ensure that you can + // go to a page and then navigate to a new page without getting any asserts + // or crashes. + m_webFrame->frame()->script()->proxy()->clearForClose(); +} + +// This function is responsible for associating the |identifier| with a given +// subresource load. The following functions that accept an |identifier| are +// called for each subresource, so they should not be dispatched to the +// WebFrame. +void FrameLoaderClientImpl::assignIdentifierToInitialRequest( + unsigned long identifier, DocumentLoader* loader, + const ResourceRequest& request) +{ + if (m_webFrame->client()) { + WrappedResourceRequest webreq(request); + m_webFrame->client()->assignIdentifierToRequest( + m_webFrame, identifier, webreq); + } +} + +// Determines whether the request being loaded by |loader| is a frame or a +// subresource. A subresource in this context is anything other than a frame -- +// this includes images and xmlhttp requests. It is important to note that a +// subresource is NOT limited to stuff loaded through the frame's subresource +// loader. Synchronous xmlhttp requests for example, do not go through the +// subresource loader, but we still label them as TargetIsSubResource. +// +// The important edge cases to consider when modifying this function are +// how synchronous resource loads are treated during load/unload threshold. +static ResourceRequest::TargetType determineTargetTypeFromLoader( + DocumentLoader* loader) +{ + if (loader == loader->frameLoader()->provisionalDocumentLoader()) { + if (loader->frameLoader()->isLoadingMainFrame()) + return ResourceRequest::TargetIsMainFrame; + else + return ResourceRequest::TargetIsSubFrame; + } + return ResourceRequest::TargetIsSubResource; +} + +void FrameLoaderClientImpl::dispatchWillSendRequest( + DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, + const ResourceResponse& redirectResponse) +{ + if (loader) { + // We want to distinguish between a request for a document to be loaded into + // the main frame, a sub-frame, or the sub-objects in that document. + request.setTargetType(determineTargetTypeFromLoader(loader)); + } + + // FrameLoader::loadEmptyDocumentSynchronously() creates an empty document + // with no URL. We don't like that, so we'll rename it to about:blank. + if (request.url().isEmpty()) + request.setURL(KURL(ParsedURLString, "about:blank")); + if (request.firstPartyForCookies().isEmpty()) + request.setFirstPartyForCookies(KURL(ParsedURLString, "about:blank")); + + // Give the WebFrameClient a crack at the request. + if (m_webFrame->client()) { + WrappedResourceRequest webreq(request); + WrappedResourceResponse webresp(redirectResponse); + m_webFrame->client()->willSendRequest( + m_webFrame, identifier, webreq, webresp); + } +} + +bool FrameLoaderClientImpl::shouldUseCredentialStorage( + DocumentLoader*, unsigned long identifier) +{ + // FIXME + // Intended to pass through to a method on the resource load delegate. + // If implemented, that method controls whether the browser should ask the + // networking layer for a stored default credential for the page (say from + // the Mac OS keychain). If the method returns false, the user should be + // presented with an authentication challenge whether or not the networking + // layer has a credential stored. + // This returns true for backward compatibility: the ability to override the + // system credential store is new. (Actually, not yet fully implemented in + // WebKit, as of this writing.) + return true; +} + +void FrameLoaderClientImpl::dispatchDidReceiveAuthenticationChallenge( + DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) +{ + // FIXME +} + +void FrameLoaderClientImpl::dispatchDidCancelAuthenticationChallenge( + DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) +{ + // FIXME +} + +void FrameLoaderClientImpl::dispatchDidReceiveResponse(DocumentLoader* loader, + unsigned long identifier, + const ResourceResponse& response) +{ + if (m_webFrame->client()) { + WrappedResourceResponse webresp(response); + m_webFrame->client()->didReceiveResponse(m_webFrame, identifier, webresp); + } +} + +void FrameLoaderClientImpl::dispatchDidReceiveContentLength( + DocumentLoader* loader, + unsigned long identifier, + int lengthReceived) +{ +} + +// Called when a particular resource load completes +void FrameLoaderClientImpl::dispatchDidFinishLoading(DocumentLoader* loader, + unsigned long identifier) +{ + if (m_webFrame->client()) + m_webFrame->client()->didFinishResourceLoad(m_webFrame, identifier); +} + +void FrameLoaderClientImpl::dispatchDidFailLoading(DocumentLoader* loader, + unsigned long identifier, + const ResourceError& error) +{ + if (m_webFrame->client()) + m_webFrame->client()->didFailResourceLoad(m_webFrame, identifier, error); +} + +void FrameLoaderClientImpl::dispatchDidFinishDocumentLoad() +{ + // A frame may be reused. This call ensures we don't hold on to our password + // listeners and their associated HTMLInputElements. + m_webFrame->clearPasswordListeners(); + + if (m_webFrame->client()) + m_webFrame->client()->didFinishDocumentLoad(m_webFrame); +} + +bool FrameLoaderClientImpl::dispatchDidLoadResourceFromMemoryCache( + DocumentLoader* loader, + const ResourceRequest& request, + const ResourceResponse& response, + int length) +{ + if (m_webFrame->client()) { + WrappedResourceRequest webreq(request); + WrappedResourceResponse webresp(response); + m_webFrame->client()->didLoadResourceFromMemoryCache( + m_webFrame, webreq, webresp); + } + return false; // Do not suppress remaining notifications +} + +void FrameLoaderClientImpl::dispatchDidLoadResourceByXMLHttpRequest( + unsigned long identifier, + const ScriptString& source) +{ +} + +void FrameLoaderClientImpl::dispatchDidHandleOnloadEvents() +{ + if (m_webFrame->client()) + m_webFrame->client()->didHandleOnloadEvents(m_webFrame); +} + +// Redirect Tracking +// ================= +// We want to keep track of the chain of redirects that occur during page +// loading. There are two types of redirects, server redirects which are HTTP +// response codes, and client redirects which are document.location= and meta +// refreshes. +// +// This outlines the callbacks that we get in different redirect situations, +// and how each call modifies the redirect chain. +// +// Normal page load +// ---------------- +// dispatchDidStartProvisionalLoad() -> adds URL to the redirect list +// dispatchDidCommitLoad() -> DISPATCHES & clears list +// +// Server redirect (success) +// ------------------------- +// dispatchDidStartProvisionalLoad() -> adds source URL +// dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL +// dispatchDidCommitLoad() -> DISPATCHES +// +// Client redirect (success) +// ------------------------- +// (on page) +// dispatchWillPerformClientRedirect() -> saves expected redirect +// dispatchDidStartProvisionalLoad() -> appends redirect source (since +// it matches the expected redirect) +// and the current page as the dest) +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidCommitLoad() -> DISPATCHES +// +// Client redirect (cancelled) +// (e.g meta-refresh trumped by manual doc.location change, or just cancelled +// because a link was clicked that requires the meta refresh to be rescheduled +// (the SOURCE URL may have changed). +// --------------------------- +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidStartProvisionalLoad() -> adds only URL to redirect list +// dispatchDidCommitLoad() -> DISPATCHES & clears list +// rescheduled ? dispatchWillPerformClientRedirect() -> saves expected redirect +// : nothing + +// Client redirect (failure) +// ------------------------- +// (on page) +// dispatchWillPerformClientRedirect() -> saves expected redirect +// dispatchDidStartProvisionalLoad() -> appends redirect source (since +// it matches the expected redirect) +// and the current page as the dest) +// dispatchDidCancelClientRedirect() +// dispatchDidFailProvisionalLoad() +// +// Load 1 -> Server redirect to 2 -> client redirect to 3 -> server redirect to 4 +// ------------------------------------------------------------------------------ +// dispatchDidStartProvisionalLoad() -> adds source URL 1 +// dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL 2 +// dispatchDidCommitLoad() -> DISPATCHES 1+2 +// -- begin client redirect and NEW DATA SOURCE +// dispatchWillPerformClientRedirect() -> saves expected redirect +// dispatchDidStartProvisionalLoad() -> appends URL 2 and URL 3 +// dispatchDidReceiveServerRedirectForProvisionalLoad() -> appends destination URL 4 +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidCommitLoad() -> DISPATCHES +// +// Interesting case with multiple location changes involving anchors. +// Load page 1 containing future client-redirect (back to 1, e.g meta refresh) > Click +// on a link back to the same page (i.e an anchor href) > +// client-redirect finally fires (with new source, set to 1#anchor) +// ----------------------------------------------------------------------------- +// dispatchWillPerformClientRedirect(non-zero 'interval' param) -> saves expected redirect +// -- click on anchor href +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidStartProvisionalLoad() -> adds 1#anchor source +// dispatchDidCommitLoad() -> DISPATCHES 1#anchor +// dispatchWillPerformClientRedirect() -> saves exp. source (1#anchor) +// -- redirect timer fires +// dispatchDidStartProvisionalLoad() -> appends 1#anchor (src) and 1 (dest) +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidCommitLoad() -> DISPATCHES 1#anchor + 1 +// +void FrameLoaderClientImpl::dispatchDidReceiveServerRedirectForProvisionalLoad() +{ + WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl(); + if (!ds) { + // Got a server redirect when there is no provisional DS! + ASSERT_NOT_REACHED(); + return; + } + + // The server redirect may have been blocked. + if (ds->request().isNull()) + return; + + // A provisional load should have started already, which should have put an + // entry in our redirect chain. + ASSERT(ds->hasRedirectChain()); + + // The URL of the destination is on the provisional data source. We also need + // to update the redirect chain to account for this addition (we do this + // before the callback so the callback can look at the redirect chain to see + // what happened). + ds->appendRedirect(ds->request().url()); + + if (m_webFrame->client()) + m_webFrame->client()->didReceiveServerRedirectForProvisionalLoad(m_webFrame); +} + +// Called on both success and failure of a client redirect. +void FrameLoaderClientImpl::dispatchDidCancelClientRedirect() +{ + // No longer expecting a client redirect. + if (m_webFrame->client()) { + m_expectedClientRedirectSrc = KURL(); + m_expectedClientRedirectDest = KURL(); + m_webFrame->client()->didCancelClientRedirect(m_webFrame); + } + + // No need to clear the redirect chain, since that data source has already + // been deleted by the time this function is called. +} + +void FrameLoaderClientImpl::dispatchWillPerformClientRedirect( + const KURL& url, + double interval, + double fireDate) +{ + // Tells dispatchDidStartProvisionalLoad that if it sees this item it is a + // redirect and the source item should be added as the start of the chain. + m_expectedClientRedirectSrc = m_webFrame->url(); + m_expectedClientRedirectDest = url; + + // FIXME: bug 1135512. Webkit does not properly notify us of cancelling + // http > file client redirects. Since the FrameLoader's policy is to never + // carry out such a navigation anyway, the best thing we can do for now to + // not get confused is ignore this notification. + if (m_expectedClientRedirectDest.isLocalFile() + && m_expectedClientRedirectSrc.protocolInHTTPFamily()) { + m_expectedClientRedirectSrc = KURL(); + m_expectedClientRedirectDest = KURL(); + return; + } + + if (m_webFrame->client()) { + m_webFrame->client()->willPerformClientRedirect( + m_webFrame, + m_expectedClientRedirectSrc, + m_expectedClientRedirectDest, + static_cast<unsigned int>(interval), + static_cast<unsigned int>(fireDate)); + } +} + +void FrameLoaderClientImpl::dispatchDidChangeLocationWithinPage() +{ + // Anchor fragment navigations are not normal loads, so we need to synthesize + // some events for our delegate. + WebViewImpl* webView = m_webFrame->viewImpl(); + if (webView->client()) + webView->client()->didStartLoading(); + + WebDataSourceImpl* ds = m_webFrame->dataSourceImpl(); + ASSERT(ds); // Should not be null when navigating to a reference fragment! + if (ds) { + KURL url = ds->request().url(); + KURL chainEnd; + if (ds->hasRedirectChain()) { + chainEnd = ds->endOfRedirectChain(); + ds->clearRedirectChain(); + } + + // Figure out if this location change is because of a JS-initiated + // client redirect (e.g onload/setTimeout document.location.href=). + // FIXME: (bugs 1085325, 1046841) We don't get proper redirect + // performed/cancelled notifications across anchor navigations, so the + // other redirect-tracking code in this class (see + // dispatch*ClientRedirect() and dispatchDidStartProvisionalLoad) is + // insufficient to catch and properly flag these transitions. Once a + // proper fix for this bug is identified and applied the following + // block may no longer be required. + bool wasClientRedirect = + (url == m_expectedClientRedirectDest && chainEnd == m_expectedClientRedirectSrc) + || !m_webFrame->isProcessingUserGesture(); + + if (wasClientRedirect) { + if (m_webFrame->client()) + m_webFrame->client()->didCompleteClientRedirect(m_webFrame, chainEnd); + ds->appendRedirect(chainEnd); + // Make sure we clear the expected redirect since we just effectively + // completed it. + m_expectedClientRedirectSrc = KURL(); + m_expectedClientRedirectDest = KURL(); + } + + // Regardless of how we got here, we are navigating to a URL so we need to + // add it to the redirect chain. + ds->appendRedirect(url); + } + + bool isNewNavigation; + webView->didCommitLoad(&isNewNavigation); + if (m_webFrame->client()) + m_webFrame->client()->didChangeLocationWithinPage(m_webFrame, isNewNavigation); + + if (webView->client()) + webView->client()->didStopLoading(); +} + +void FrameLoaderClientImpl::dispatchWillClose() +{ + if (m_webFrame->client()) + m_webFrame->client()->willClose(m_webFrame); +} + +void FrameLoaderClientImpl::dispatchDidReceiveIcon() +{ + // The icon database is disabled, so this should never be called. + ASSERT_NOT_REACHED(); +} + +void FrameLoaderClientImpl::dispatchDidStartProvisionalLoad() { + // In case a redirect occurs, we need this to be set so that the redirect + // handling code can tell where the redirect came from. Server redirects + // will occur on the provisional load, so we need to keep track of the most + // recent provisional load URL. + // See dispatchDidReceiveServerRedirectForProvisionalLoad. + WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl(); + if (!ds) { + ASSERT_NOT_REACHED(); + return; + } + KURL url = ds->request().url(); + + // Since the provisional load just started, we should have not gotten + // any redirects yet. + ASSERT(!ds->hasRedirectChain()); + + // If this load is what we expected from a client redirect, treat it as a + // redirect from that original page. The expected redirect urls will be + // cleared by DidCancelClientRedirect. + bool completingClientRedirect = false; + if (m_expectedClientRedirectSrc.isValid()) { + // m_expectedClientRedirectDest could be something like + // "javascript:history.go(-1)" thus we need to exclude url starts with + // "javascript:". See bug: 1080873 + ASSERT(m_expectedClientRedirectDest.protocolIs("javascript") + || m_expectedClientRedirectDest == url); + ds->appendRedirect(m_expectedClientRedirectSrc); + completingClientRedirect = true; + } + ds->appendRedirect(url); + + if (m_webFrame->client()) { + // Whatever information didCompleteClientRedirect contains should only + // be considered relevant until the next provisional load has started. + // So we first tell the client that the load started, and then tell it + // about the client redirect the load is responsible for completing. + m_webFrame->client()->didStartProvisionalLoad(m_webFrame); + if (completingClientRedirect) { + m_webFrame->client()->didCompleteClientRedirect( + m_webFrame, m_expectedClientRedirectSrc); + } + } +} + +void FrameLoaderClientImpl::dispatchDidReceiveTitle(const String& title) +{ + if (m_webFrame->client()) + m_webFrame->client()->didReceiveTitle(m_webFrame, title); +} + +void FrameLoaderClientImpl::dispatchDidCommitLoad() +{ + WebViewImpl* webview = m_webFrame->viewImpl(); + bool isNewNavigation; + webview->didCommitLoad(&isNewNavigation); + + if (m_webFrame->client()) + m_webFrame->client()->didCommitProvisionalLoad(m_webFrame, isNewNavigation); + + WebDevToolsAgentImpl* toolsAgent = webview->devToolsAgentImpl(); + if (toolsAgent) + toolsAgent->DidCommitLoadForFrame(webview, m_webFrame, isNewNavigation); +} + +void FrameLoaderClientImpl::dispatchDidFailProvisionalLoad( + const ResourceError& error) +{ + + // If a policy change occured, then we do not want to inform the plugin + // delegate. See http://b/907789 for details. FIXME: This means the + // plugin won't receive NPP_URLNotify, which seems like it could result in + // a memory leak in the plugin!! + if (error.domain() == internalErrorDomain + && error.errorCode() == PolicyChangeError) { + m_webFrame->didFail(cancelledError(error.failingURL()), true); + return; + } + + OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); + m_webFrame->didFail(error, true); + if (observer) + observer->didFailLoading(error); +} + +void FrameLoaderClientImpl::dispatchDidFailLoad(const ResourceError& error) +{ + OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); + m_webFrame->didFail(error, false); + if (observer) + observer->didFailLoading(error); + + // Don't clear the redirect chain, this will happen in the middle of client + // redirects, and we need the context. The chain will be cleared when the + // provisional load succeeds or fails, not the "real" one. +} + +void FrameLoaderClientImpl::dispatchDidFinishLoad() +{ + OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); + + if (m_webFrame->client()) + m_webFrame->client()->didFinishLoad(m_webFrame); + + if (observer) + observer->didFinishLoading(); + + // Don't clear the redirect chain, this will happen in the middle of client + // redirects, and we need the context. The chain will be cleared when the + // provisional load succeeds or fails, not the "real" one. +} + +void FrameLoaderClientImpl::dispatchDidFirstLayout() +{ +} + +void FrameLoaderClientImpl::dispatchDidFirstVisuallyNonEmptyLayout() +{ + // FIXME: called when webkit finished layout of a page that was visually + // non-empty. + // All resources have not necessarily finished loading. +} + +Frame* FrameLoaderClientImpl::dispatchCreatePage() +{ + struct WindowFeatures features; + Page* newPage = m_webFrame->frame()->page()->chrome()->createWindow( + m_webFrame->frame(), FrameLoadRequest(), features); + + // Make sure that we have a valid disposition. This should have been set in + // the preceeding call to dispatchDecidePolicyForNewWindowAction. + ASSERT(m_nextNavigationPolicy != WebNavigationPolicyIgnore); + WebNavigationPolicy policy = m_nextNavigationPolicy; + m_nextNavigationPolicy = WebNavigationPolicyIgnore; + + // createWindow can return null (e.g., popup blocker denies the window). + if (!newPage) + return 0; + + WebViewImpl::fromPage(newPage)->setInitialNavigationPolicy(policy); + return newPage->mainFrame(); +} + +void FrameLoaderClientImpl::dispatchShow() +{ + WebViewImpl* webView = m_webFrame->viewImpl(); + if (webView && webView->client()) + webView->client()->show(webView->initialNavigationPolicy()); +} + +static bool shouldTreatAsAttachment(const ResourceResponse& response) +{ + const String& contentDisposition = + response.httpHeaderField("Content-Disposition"); + if (contentDisposition.isEmpty()) + return false; + + // Some broken sites just send + // Content-Disposition: ; filename="file" + // screen those out here. + if (contentDisposition.startsWith(";")) + return false; + + if (contentDisposition.startsWith("inline", false)) + return false; + + // Some broken sites just send + // Content-Disposition: filename="file" + // without a disposition token... screen those out. + if (contentDisposition.startsWith("filename", false)) + return false; + + // Also in use is Content-Disposition: name="file" + if (contentDisposition.startsWith("name", false)) + return false; + + // We have a content-disposition of "attachment" or unknown. + // RFC 2183, section 2.8 says that an unknown disposition + // value should be treated as "attachment" + return true; +} + +void FrameLoaderClientImpl::dispatchDecidePolicyForMIMEType( + FramePolicyFunction function, + const String& mimeType, + const ResourceRequest&) +{ + const ResourceResponse& response = + m_webFrame->frame()->loader()->activeDocumentLoader()->response(); + + PolicyAction action; + + int statusCode = response.httpStatusCode(); + if (statusCode == 204 || statusCode == 205) { + // The server does not want us to replace the page contents. + action = PolicyIgnore; + } else if (shouldTreatAsAttachment(response)) { + // The server wants us to download instead of replacing the page contents. + // Downloading is handled by the embedder, but we still get the initial + // response so that we can ignore it and clean up properly. + action = PolicyIgnore; + } else if (!canShowMIMEType(mimeType)) { + // Make sure that we can actually handle this type internally. + action = PolicyIgnore; + } else { + // OK, we will render this page. + action = PolicyUse; + } + + // NOTE: PolicyChangeError will be generated when action is not PolicyUse. + (m_webFrame->frame()->loader()->policyChecker()->*function)(action); +} + +void FrameLoaderClientImpl::dispatchDecidePolicyForNewWindowAction( + FramePolicyFunction function, + const NavigationAction& action, + const ResourceRequest& request, + PassRefPtr<FormState> formState, + const String& frameName) +{ + WebNavigationPolicy navigationPolicy; + if (!actionSpecifiesNavigationPolicy(action, &navigationPolicy)) + navigationPolicy = WebNavigationPolicyNewForegroundTab; + + PolicyAction policyAction; + if (navigationPolicy == WebNavigationPolicyDownload) + policyAction = PolicyDownload; + else { + policyAction = PolicyUse; + + // Remember the disposition for when dispatchCreatePage is called. It is + // unfortunate that WebCore does not provide us with any context when + // creating or showing the new window that would allow us to avoid having + // to keep this state. + m_nextNavigationPolicy = navigationPolicy; + } + (m_webFrame->frame()->loader()->policyChecker()->*function)(policyAction); +} + +void FrameLoaderClientImpl::dispatchDecidePolicyForNavigationAction( + FramePolicyFunction function, + const NavigationAction& action, + const ResourceRequest& request, + PassRefPtr<FormState> formState) { + PolicyAction policyAction = PolicyIgnore; + + // It is valid for this function to be invoked in code paths where the + // the webview is closed. + // The null check here is to fix a crash that seems strange + // (see - https://bugs.webkit.org/show_bug.cgi?id=23554). + if (m_webFrame->client() && !request.url().isNull()) { + WebNavigationPolicy navigationPolicy = WebNavigationPolicyCurrentTab; + actionSpecifiesNavigationPolicy(action, &navigationPolicy); + + // Give the delegate a chance to change the navigation policy. + const WebDataSourceImpl* ds = m_webFrame->provisionalDataSourceImpl(); + if (ds) { + KURL url = ds->request().url(); + if (url.protocolIs(backForwardNavigationScheme)) { + handleBackForwardNavigation(url); + navigationPolicy = WebNavigationPolicyIgnore; + } else { + bool isRedirect = ds->hasRedirectChain(); + + WebNavigationType webnavType = + WebDataSourceImpl::toWebNavigationType(action.type()); + + RefPtr<Node> node; + for (const Event* event = action.event(); event; event = event->underlyingEvent()) { + if (event->isMouseEvent()) { + const MouseEvent* mouseEvent = + static_cast<const MouseEvent*>(event); + node = m_webFrame->frame()->eventHandler()->hitTestResultAtPoint( + mouseEvent->absoluteLocation(), false).innerNonSharedNode(); + break; + } + } + WebNode originatingNode(node); + + navigationPolicy = m_webFrame->client()->decidePolicyForNavigation( + m_webFrame, ds->request(), webnavType, originatingNode, + navigationPolicy, isRedirect); + } + } + + if (navigationPolicy == WebNavigationPolicyCurrentTab) + policyAction = PolicyUse; + else if (navigationPolicy == WebNavigationPolicyDownload) + policyAction = PolicyDownload; + else { + if (navigationPolicy != WebNavigationPolicyIgnore) { + WrappedResourceRequest webreq(request); + m_webFrame->client()->loadURLExternally(m_webFrame, webreq, navigationPolicy); + } + policyAction = PolicyIgnore; + } + } + + (m_webFrame->frame()->loader()->policyChecker()->*function)(policyAction); +} + +void FrameLoaderClientImpl::cancelPolicyCheck() +{ + // FIXME +} + +void FrameLoaderClientImpl::dispatchUnableToImplementPolicy(const ResourceError& error) +{ + m_webFrame->client()->unableToImplementPolicyWithError(m_webFrame, error); +} + +void FrameLoaderClientImpl::dispatchWillSubmitForm(FramePolicyFunction function, + PassRefPtr<FormState> formState) +{ + if (m_webFrame->client()) + m_webFrame->client()->willSubmitForm(m_webFrame, WebForm(formState->form())); + (m_webFrame->frame()->loader()->policyChecker()->*function)(PolicyUse); +} + +void FrameLoaderClientImpl::dispatchDidLoadMainResource(DocumentLoader*) +{ + // FIXME +} + +void FrameLoaderClientImpl::revertToProvisionalState(DocumentLoader*) +{ + m_hasRepresentation = true; +} + +void FrameLoaderClientImpl::setMainDocumentError(DocumentLoader*, + const ResourceError& error) +{ + if (m_pluginWidget.get()) { + if (m_sentInitialResponseToPlugin) { + m_pluginWidget->didFailLoading(error); + m_sentInitialResponseToPlugin = false; + } + m_pluginWidget = 0; + } +} + +void FrameLoaderClientImpl::postProgressStartedNotification() +{ + WebViewImpl* webview = m_webFrame->viewImpl(); + if (webview && webview->client()) + webview->client()->didStartLoading(); +} + +void FrameLoaderClientImpl::postProgressEstimateChangedNotification() +{ + // FIXME +} + +void FrameLoaderClientImpl::postProgressFinishedNotification() +{ + // FIXME: why might the webview be null? http://b/1234461 + WebViewImpl* webview = m_webFrame->viewImpl(); + if (webview && webview->client()) + webview->client()->didStopLoading(); +} + +void FrameLoaderClientImpl::setMainFrameDocumentReady(bool ready) +{ + // FIXME +} + +// Creates a new connection and begins downloading from that (contrast this +// with |download|). +void FrameLoaderClientImpl::startDownload(const ResourceRequest& request) +{ + if (m_webFrame->client()) { + WrappedResourceRequest webreq(request); + m_webFrame->client()->loadURLExternally( + m_webFrame, webreq, WebNavigationPolicyDownload); + } +} + +void FrameLoaderClientImpl::willChangeTitle(DocumentLoader*) +{ + // FIXME +} + +void FrameLoaderClientImpl::didChangeTitle(DocumentLoader*) +{ + // FIXME +} + +// Called whenever data is received. +void FrameLoaderClientImpl::committedLoad(DocumentLoader* loader, const char* data, int length) { + if (!m_pluginWidget.get()) { + if (m_webFrame->client()) { + bool preventDefault = false; + m_webFrame->client()->didReceiveDocumentData(m_webFrame, data, length, preventDefault); + if (!preventDefault) + m_webFrame->commitDocumentData(data, length); + } + } + + // If we are sending data to MediaDocument, we should stop here + // and cancel the request. + if (m_webFrame->frame()->document() + && m_webFrame->frame()->document()->isMediaDocument()) + loader->cancelMainResourceLoad(pluginWillHandleLoadError(loader->response())); + + // The plugin widget could have been created in the m_webFrame->DidReceiveData + // function. + if (m_pluginWidget.get()) { + if (!m_sentInitialResponseToPlugin) { + m_sentInitialResponseToPlugin = true; + m_pluginWidget->didReceiveResponse( + m_webFrame->frame()->loader()->activeDocumentLoader()->response()); + } + m_pluginWidget->didReceiveData(data, length); + } +} + +void FrameLoaderClientImpl::finishedLoading(DocumentLoader* dl) +{ + if (m_pluginWidget.get()) { + m_pluginWidget->didFinishLoading(); + m_pluginWidget = 0; + m_sentInitialResponseToPlugin = false; + } else { + // This is necessary to create an empty document. See bug 634004. + // However, we only want to do this if makeRepresentation has been called, to + // match the behavior on the Mac. + if (m_hasRepresentation) + dl->frameLoader()->setEncoding("", false); + } +} + +void FrameLoaderClientImpl::updateGlobalHistory() +{ +} + +void FrameLoaderClientImpl::updateGlobalHistoryRedirectLinks() +{ +} + +bool FrameLoaderClientImpl::shouldGoToHistoryItem(HistoryItem*) const +{ + // FIXME + return true; +} + +void FrameLoaderClientImpl::didDisplayInsecureContent() +{ + if (m_webFrame->client()) + m_webFrame->client()->didDisplayInsecureContent(m_webFrame); +} + +void FrameLoaderClientImpl::didRunInsecureContent(SecurityOrigin* origin) +{ + if (m_webFrame->client()) + m_webFrame->client()->didRunInsecureContent(m_webFrame, WebSecurityOrigin(origin)); +} + +ResourceError FrameLoaderClientImpl::blockedError(const ResourceRequest&) +{ + // FIXME + return ResourceError(); +} + +ResourceError FrameLoaderClientImpl::cancelledError(const ResourceRequest& request) +{ + if (!m_webFrame->client()) + return ResourceError(); + + return m_webFrame->client()->cancelledError( + m_webFrame, WrappedResourceRequest(request)); +} + +ResourceError FrameLoaderClientImpl::cannotShowURLError(const ResourceRequest& request) +{ + if (!m_webFrame->client()) + return ResourceError(); + + return m_webFrame->client()->cannotHandleRequestError( + m_webFrame, WrappedResourceRequest(request)); +} + +ResourceError FrameLoaderClientImpl::interruptForPolicyChangeError( + const ResourceRequest& request) +{ + return ResourceError(internalErrorDomain, PolicyChangeError, + request.url().string(), String()); +} + +ResourceError FrameLoaderClientImpl::cannotShowMIMETypeError(const ResourceResponse&) +{ + // FIXME + return ResourceError(); +} + +ResourceError FrameLoaderClientImpl::fileDoesNotExistError(const ResourceResponse&) +{ + // FIXME + return ResourceError(); +} + +ResourceError FrameLoaderClientImpl::pluginWillHandleLoadError(const ResourceResponse&) +{ + // FIXME + return ResourceError(); +} + +bool FrameLoaderClientImpl::shouldFallBack(const ResourceError& error) +{ + // This method is called when we fail to load the URL for an <object> tag + // that has fallback content (child elements) and is being loaded as a frame. + // The error parameter indicates the reason for the load failure. + // We should let the fallback content load only if this wasn't a cancelled + // request. + // Note: The mac version also has a case for "WebKitErrorPluginWillHandleLoad" + ResourceError c = cancelledError(ResourceRequest()); + return error.errorCode() != c.errorCode() || error.domain() != c.domain(); +} + +bool FrameLoaderClientImpl::canHandleRequest(const ResourceRequest& request) const +{ + return m_webFrame->client()->canHandleRequest( + m_webFrame, WrappedResourceRequest(request)); +} + +bool FrameLoaderClientImpl::canShowMIMEType(const String& mimeType) const +{ + // This method is called to determine if the media type can be shown + // "internally" (i.e. inside the browser) regardless of whether or not the + // browser or a plugin is doing the rendering. + + // mimeType strings are supposed to be ASCII, but if they are not for some + // reason, then it just means that the mime type will fail all of these "is + // supported" checks and go down the path of an unhandled mime type. + if (net::IsSupportedMimeType(mimeType.latin1().data())) + return true; + + // If Chrome is started with the --disable-plugins switch, pluginData is null. + PluginData* pluginData = m_webFrame->frame()->page()->pluginData(); + + // See if the type is handled by an installed plugin, if so, we can show it. + // FIXME: (http://b/1085524) This is the place to stick a preference to + // disable full page plugins (optionally for certain types!) + return !mimeType.isEmpty() && pluginData && pluginData->supportsMimeType(mimeType); +} + +bool FrameLoaderClientImpl::representationExistsForURLScheme(const String&) const +{ + // FIXME + return false; +} + +String FrameLoaderClientImpl::generatedMIMETypeForURLScheme(const String& scheme) const +{ + // This appears to generate MIME types for protocol handlers that are handled + // internally. The only place I can find in the WebKit code that uses this + // function is WebView::registerViewClass, where it is used as part of the + // process by which custom view classes for certain document representations + // are registered. + String mimeType("x-apple-web-kit/"); + mimeType.append(scheme.lower()); + return mimeType; +} + +void FrameLoaderClientImpl::frameLoadCompleted() +{ + // FIXME: the mac port also conditionally calls setDrawsBackground:YES on + // it's ScrollView here. + + // This comment from the Mac port: + // Note: Can be called multiple times. + // Even if already complete, we might have set a previous item on a frame that + // didn't do any data loading on the past transaction. Make sure to clear these out. + + // FIXME: setPreviousHistoryItem() no longer exists. http://crbug.com/8566 + // m_webFrame->frame()->loader()->setPreviousHistoryItem(0); +} + +void FrameLoaderClientImpl::saveViewStateToItem(HistoryItem*) +{ + // FIXME +} + +void FrameLoaderClientImpl::restoreViewState() +{ + // FIXME: probably scrolls to last position when you go back or forward +} + +void FrameLoaderClientImpl::provisionalLoadStarted() +{ + // FIXME: On mac, this does various caching stuff +} + +void FrameLoaderClientImpl::didFinishLoad() +{ + OwnPtr<WebPluginLoadObserver> observer = pluginLoadObserver(); + if (observer) + observer->didFinishLoading(); +} + +void FrameLoaderClientImpl::prepareForDataSourceReplacement() +{ + // FIXME +} + +PassRefPtr<DocumentLoader> FrameLoaderClientImpl::createDocumentLoader( + const ResourceRequest& request, + const SubstituteData& data) +{ + RefPtr<WebDataSourceImpl> ds = WebDataSourceImpl::create(request, data); + if (m_webFrame->client()) + m_webFrame->client()->didCreateDataSource(m_webFrame, ds.get()); + return ds.release(); +} + +void FrameLoaderClientImpl::setTitle(const String& title, const KURL& url) +{ + // FIXME: inform consumer of changes to the title. +} + +String FrameLoaderClientImpl::userAgent(const KURL& url) +{ + // FIXME: Convert this to a WebKitClient callback. + return webkit_glue::StdStringToString( + webkit_glue::GetUserAgent(webkit_glue::KURLToGURL(url))); +} + +void FrameLoaderClientImpl::savePlatformDataToCachedFrame(CachedFrame*) +{ + // The page cache should be disabled. + ASSERT_NOT_REACHED(); +} + +void FrameLoaderClientImpl::transitionToCommittedFromCachedFrame(CachedFrame*) +{ + ASSERT_NOT_REACHED(); +} + +// Called when the FrameLoader goes into a state in which a new page load +// will occur. +void FrameLoaderClientImpl::transitionToCommittedForNewPage() +{ + makeDocumentView(); +} + +bool FrameLoaderClientImpl::canCachePage() const +{ + // Since we manage the cache, always report this page as non-cacheable to + // FrameLoader. + return false; +} + +// Downloading is handled in the browser process, not WebKit. If we get to this +// point, our download detection code in the ResourceDispatcherHost is broken! +void FrameLoaderClientImpl::download(ResourceHandle* handle, + const ResourceRequest& request, + const ResourceRequest& initialRequest, + const ResourceResponse& response) +{ + ASSERT_NOT_REACHED(); +} + +PassRefPtr<Frame> FrameLoaderClientImpl::createFrame( + const KURL& url, + const String& name, + HTMLFrameOwnerElement* ownerElement, + const String& referrer, + bool allowsScrolling, + int marginWidth, + int marginHeight) +{ + FrameLoadRequest frameRequest(ResourceRequest(url, referrer), name); + return m_webFrame->createChildFrame(frameRequest, ownerElement); +} + +PassRefPtr<Widget> FrameLoaderClientImpl::createPlugin( + const IntSize& size, // FIXME: how do we use this? + HTMLPlugInElement* element, + const KURL& url, + const Vector<String>& paramNames, + const Vector<String>& paramValues, + const String& mimeType, + bool loadManually) +{ +#if !PLATFORM(WIN_OS) + // WebCore asks us to make a plugin even if we don't have a + // registered handler, with a comment saying it's so we can display + // the broken plugin icon. In Chromium, we normally register a + // fallback plugin handler that allows you to install a missing + // plugin. Since we don't yet have a default plugin handler, we + // need to return null here rather than going through all the + // plugin-creation IPCs only to discover we don't have a plugin + // registered, which causes a crash. + // FIXME: remove me once we have a default plugin. + if (objectContentType(url, mimeType) != ObjectContentNetscapePlugin) + return 0; +#endif + + if (!m_webFrame->client()) + return 0; + + WebPluginParams params; + params.url = url; + params.mimeType = mimeType; + params.attributeNames = paramNames; + params.attributeValues = paramValues; + params.loadManually = loadManually; + + WebPlugin* webPlugin = m_webFrame->client()->createPlugin(m_webFrame, params); + if (!webPlugin) + return 0; + + // The container takes ownership of the WebPlugin. + RefPtr<WebPluginContainerImpl> container = + WebPluginContainerImpl::create(element, webPlugin); + + if (!webPlugin->initialize(container.get())) + return 0; + + // The element might have been removed during plugin initialization! + if (!element->renderer()) + return 0; + + return container; +} + +// This method gets called when a plugin is put in place of html content +// (e.g., acrobat reader). +void FrameLoaderClientImpl::redirectDataToPlugin(Widget* pluginWidget) { + m_pluginWidget = static_cast<WebPluginContainerImpl*>(pluginWidget); + ASSERT(m_pluginWidget.get()); +} + +PassRefPtr<Widget> FrameLoaderClientImpl::createJavaAppletWidget( + const IntSize& size, + HTMLAppletElement* element, + const KURL& /* baseURL */, + const Vector<String>& paramNames, + const Vector<String>& paramValues) +{ + return createPlugin(size, element, KURL(), paramNames, paramValues, + "application/x-java-applet", false); +} + +ObjectContentType FrameLoaderClientImpl::objectContentType( + const KURL& url, + const String& explicitMimeType) +{ + // This code is based on Apple's implementation from + // WebCoreSupport/WebFrameBridge.mm. + + String mimeType = explicitMimeType; + if (mimeType.isEmpty()) { + // Try to guess the MIME type based off the extension. + String filename = url.lastPathComponent(); + int extensionPos = filename.reverseFind('.'); + if (extensionPos >= 0) + mimeType = MIMETypeRegistry::getMIMETypeForPath(url.path()); + + if (mimeType.isEmpty()) + return ObjectContentFrame; + } + + if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType)) + return ObjectContentImage; + + // If Chrome is started with the --disable-plugins switch, pluginData is 0. + PluginData* pluginData = m_webFrame->frame()->page()->pluginData(); + if (pluginData && pluginData->supportsMimeType(mimeType)) + return ObjectContentNetscapePlugin; + + if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType)) + return ObjectContentFrame; + + return ObjectContentNone; +} + +String FrameLoaderClientImpl::overrideMediaType() const +{ + // FIXME + return String(); +} + +bool FrameLoaderClientImpl::actionSpecifiesNavigationPolicy( + const NavigationAction& action, + WebNavigationPolicy* policy) +{ + if ((action.type() != NavigationTypeLinkClicked) || !action.event()->isMouseEvent()) + return false; + + const MouseEvent* event = static_cast<const MouseEvent*>(action.event()); + return WebViewImpl::navigationPolicyFromMouseEvent( + event->button(), event->ctrlKey(), event->shiftKey(), event->altKey(), + event->metaKey(), policy); +} + +void FrameLoaderClientImpl::handleBackForwardNavigation(const KURL& url) +{ + ASSERT(url.protocolIs(backForwardNavigationScheme)); + + bool ok; + int offset = url.lastPathComponent().toIntStrict(&ok); + if (!ok) + return; + + WebViewImpl* webview = m_webFrame->viewImpl(); + if (webview->client()) + webview->client()->navigateBackForwardSoon(offset); +} + +PassOwnPtr<WebPluginLoadObserver> FrameLoaderClientImpl::pluginLoadObserver() +{ + WebDataSourceImpl* ds = WebDataSourceImpl::fromDocumentLoader( + m_webFrame->frame()->loader()->activeDocumentLoader()); + return ds->releasePluginLoadObserver(); +} + +} // namespace WebKit diff --git a/webkit/api/src/FrameLoaderClientImpl.h b/webkit/api/src/FrameLoaderClientImpl.h new file mode 100644 index 0000000..a3e7f18 --- /dev/null +++ b/webkit/api/src/FrameLoaderClientImpl.h @@ -0,0 +1,230 @@ +/* + * 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. + */ + +#ifndef FrameLoaderClientImpl_h +#define FrameLoaderClientImpl_h + +#include "FrameLoaderClient.h" +#include "KURL.h" +// FIXME: remove this relative path once consumers from glue are removed. +#include "../public/WebNavigationPolicy.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/RefPtr.h> + +namespace WebKit { +class WebFrameImpl; +class WebPluginContainerImpl; +class WebPluginLoadObserver; + +class FrameLoaderClientImpl : public WebCore::FrameLoaderClient { +public: + FrameLoaderClientImpl(WebFrameImpl* webFrame); + ~FrameLoaderClientImpl(); + + WebFrameImpl* webFrame() const { return m_webFrame; } + + // WebCore::FrameLoaderClient ---------------------------------------------- + + virtual void frameLoaderDestroyed(); + + // Notifies the WebView delegate that the JS window object has been cleared, + // giving it a chance to bind native objects to the window before script + // parsing begins. + virtual void windowObjectCleared(); + virtual void documentElementAvailable(); + + // A frame's V8 context was created or destroyed. + virtual void didCreateScriptContextForFrame(); + virtual void didDestroyScriptContextForFrame(); + + // A context untied to a frame was created (through evaluateInNewContext). + // This context is not tied to the lifetime of its frame, and is destroyed + // in garbage collection. + virtual void didCreateIsolatedScriptContext(); + + virtual bool hasWebView() const; + virtual bool hasFrameView() const; + virtual void makeRepresentation(WebCore::DocumentLoader*); + virtual void forceLayout(); + virtual void forceLayoutForNonHTML(); + virtual void setCopiesOnScroll(); + virtual void detachedFromParent2(); + virtual void detachedFromParent3(); + virtual void assignIdentifierToInitialRequest(unsigned long identifier, WebCore::DocumentLoader*, const WebCore::ResourceRequest&); + virtual void dispatchWillSendRequest(WebCore::DocumentLoader*, unsigned long identifier, WebCore::ResourceRequest&, const WebCore::ResourceResponse& redirectResponse); + virtual bool shouldUseCredentialStorage(WebCore::DocumentLoader*, unsigned long identifier); + virtual void dispatchDidReceiveAuthenticationChallenge(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::AuthenticationChallenge&); + virtual void dispatchDidCancelAuthenticationChallenge(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::AuthenticationChallenge&); + virtual void dispatchDidReceiveResponse(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::ResourceResponse&); + virtual void dispatchDidReceiveContentLength(WebCore::DocumentLoader*, unsigned long identifier, int lengthReceived); + virtual void dispatchDidFinishLoading(WebCore::DocumentLoader*, unsigned long identifier); + virtual void dispatchDidFailLoading(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::ResourceError&); + virtual bool dispatchDidLoadResourceFromMemoryCache(WebCore::DocumentLoader*, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&, int length); + virtual void dispatchDidLoadResourceByXMLHttpRequest(unsigned long identifier, const WebCore::ScriptString&); + virtual void dispatchDidHandleOnloadEvents(); + virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(); + virtual void dispatchDidCancelClientRedirect(); + virtual void dispatchWillPerformClientRedirect(const WebCore::KURL&, double interval, double fireDate); + virtual void dispatchDidChangeLocationWithinPage(); + virtual void dispatchWillClose(); + virtual void dispatchDidReceiveIcon(); + virtual void dispatchDidStartProvisionalLoad(); + virtual void dispatchDidReceiveTitle(const WebCore::String& title); + virtual void dispatchDidCommitLoad(); + virtual void dispatchDidFailProvisionalLoad(const WebCore::ResourceError&); + virtual void dispatchDidFailLoad(const WebCore::ResourceError&); + virtual void dispatchDidFinishDocumentLoad(); + virtual void dispatchDidFinishLoad(); + virtual void dispatchDidFirstLayout(); + virtual void dispatchDidFirstVisuallyNonEmptyLayout(); + virtual WebCore::Frame* dispatchCreatePage(); + virtual void dispatchShow(); + virtual void dispatchDecidePolicyForMIMEType(WebCore::FramePolicyFunction function, const WebCore::String& mime_type, const WebCore::ResourceRequest&); + virtual void dispatchDecidePolicyForNewWindowAction(WebCore::FramePolicyFunction function, const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request, PassRefPtr<WebCore::FormState> form_state, const WebCore::String& frame_name); + virtual void dispatchDecidePolicyForNavigationAction(WebCore::FramePolicyFunction function, const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request, PassRefPtr<WebCore::FormState> form_state); + virtual void cancelPolicyCheck(); + virtual void dispatchUnableToImplementPolicy(const WebCore::ResourceError&); + virtual void dispatchWillSubmitForm(WebCore::FramePolicyFunction, PassRefPtr<WebCore::FormState>); + virtual void dispatchDidLoadMainResource(WebCore::DocumentLoader*); + virtual void revertToProvisionalState(WebCore::DocumentLoader*); + virtual void setMainDocumentError(WebCore::DocumentLoader*, const WebCore::ResourceError&); + virtual void willChangeEstimatedProgress() { } + virtual void didChangeEstimatedProgress() { } + virtual void postProgressStartedNotification(); + virtual void postProgressEstimateChangedNotification(); + virtual void postProgressFinishedNotification(); + virtual void setMainFrameDocumentReady(bool); + virtual void startDownload(const WebCore::ResourceRequest&); + virtual void willChangeTitle(WebCore::DocumentLoader*); + virtual void didChangeTitle(WebCore::DocumentLoader*); + virtual void committedLoad(WebCore::DocumentLoader*, const char*, int); + virtual void finishedLoading(WebCore::DocumentLoader*); + virtual void updateGlobalHistory(); + virtual void updateGlobalHistoryRedirectLinks(); + virtual bool shouldGoToHistoryItem(WebCore::HistoryItem*) const; + virtual void didDisplayInsecureContent(); + virtual void didRunInsecureContent(WebCore::SecurityOrigin*); + virtual WebCore::ResourceError blockedError(const WebCore::ResourceRequest&); + virtual WebCore::ResourceError cancelledError(const WebCore::ResourceRequest&); + virtual WebCore::ResourceError cannotShowURLError(const WebCore::ResourceRequest&); + virtual WebCore::ResourceError interruptForPolicyChangeError(const WebCore::ResourceRequest&); + virtual WebCore::ResourceError cannotShowMIMETypeError(const WebCore::ResourceResponse&); + virtual WebCore::ResourceError fileDoesNotExistError(const WebCore::ResourceResponse&); + virtual WebCore::ResourceError pluginWillHandleLoadError(const WebCore::ResourceResponse&); + virtual bool shouldFallBack(const WebCore::ResourceError&); + virtual bool canHandleRequest(const WebCore::ResourceRequest&) const; + virtual bool canShowMIMEType(const WebCore::String& MIMEType) const; + virtual bool representationExistsForURLScheme(const WebCore::String& URLScheme) const; + virtual WebCore::String generatedMIMETypeForURLScheme(const WebCore::String& URLScheme) const; + virtual void frameLoadCompleted(); + virtual void saveViewStateToItem(WebCore::HistoryItem*); + virtual void restoreViewState(); + virtual void provisionalLoadStarted(); + virtual void didFinishLoad(); + virtual void prepareForDataSourceReplacement(); + virtual PassRefPtr<WebCore::DocumentLoader> createDocumentLoader( + const WebCore::ResourceRequest&, const WebCore::SubstituteData&); + virtual void setTitle(const WebCore::String& title, const WebCore::KURL&); + virtual WebCore::String userAgent(const WebCore::KURL&); + virtual void savePlatformDataToCachedFrame(WebCore::CachedFrame*); + virtual void transitionToCommittedFromCachedFrame(WebCore::CachedFrame*); + virtual void transitionToCommittedForNewPage(); + virtual bool canCachePage() const; + virtual void download( + WebCore::ResourceHandle*, const WebCore::ResourceRequest&, + const WebCore::ResourceRequest& initialRequest, + const WebCore::ResourceResponse&); + virtual PassRefPtr<WebCore::Frame> createFrame( + const WebCore::KURL& url, const WebCore::String& name, + WebCore::HTMLFrameOwnerElement* ownerElement, + const WebCore::String& referrer, bool allowsScrolling, + int marginWidth, int marginHeight); + virtual PassRefPtr<WebCore::Widget> createPlugin( + const WebCore::IntSize&, WebCore::HTMLPlugInElement*, const WebCore::KURL&, + const Vector<WebCore::String>&, const Vector<WebCore::String>&, + const WebCore::String&, bool loadManually); + virtual void redirectDataToPlugin(WebCore::Widget* pluginWidget); + virtual PassRefPtr<WebCore::Widget> createJavaAppletWidget( + const WebCore::IntSize&, + WebCore::HTMLAppletElement*, + const WebCore::KURL& /* base_url */, + const Vector<WebCore::String>& paramNames, + const Vector<WebCore::String>& paramValues); + virtual WebCore::ObjectContentType objectContentType( + const WebCore::KURL& url, const WebCore::String& mimeType); + virtual WebCore::String overrideMediaType() const; + virtual void didPerformFirstNavigation() const; + virtual void registerForIconNotification(bool listen = true); + +private: + void makeDocumentView(); + + // Given a NavigationAction, determine the associated WebNavigationPolicy. + // For example, a middle click means "open in background tab". + static bool actionSpecifiesNavigationPolicy( + const WebCore::NavigationAction& action, WebNavigationPolicy* policy); + + // Called when a dummy back-forward navigation is intercepted. + void handleBackForwardNavigation(const WebCore::KURL&); + + PassOwnPtr<WebPluginLoadObserver> pluginLoadObserver(); + + // The WebFrame that owns this object and manages its lifetime. Therefore, + // the web frame object is guaranteed to exist. + WebFrameImpl* m_webFrame; + + // True if makeRepresentation was called. We don't actually have a concept + // of a "representation", but we need to know when we're expected to have one. + // See finishedLoading(). + bool m_hasRepresentation; + + // Used to help track client redirects. When a provisional load starts, it + // has no redirects in its chain. But in the case of client redirects, we want + // to add that initial load as a redirect. When we get a new provisional load + // and the dest URL matches that load, we know that it was the result of a + // previous client redirect and the source should be added as a redirect. + // Both should be empty if unused. + WebCore::KURL m_expectedClientRedirectSrc; + WebCore::KURL m_expectedClientRedirectDest; + + // Contains a pointer to the plugin widget. + RefPtr<WebPluginContainerImpl> m_pluginWidget; + + // Indicates if we need to send over the initial notification to the plugin + // which specifies that the plugin should be ready to accept data. + bool m_sentInitialResponseToPlugin; + + // The navigation policy to use for the next call to dispatchCreatePage. + WebNavigationPolicy m_nextNavigationPolicy; +}; + +} // namespace WebKit + +#endif diff --git a/webkit/api/src/InspectorClientImpl.cpp b/webkit/api/src/InspectorClientImpl.cpp index 2dd3b7c..9a1cd3f 100644 --- a/webkit/api/src/InspectorClientImpl.cpp +++ b/webkit/api/src/InspectorClientImpl.cpp @@ -40,11 +40,11 @@ #include "WebURL.h" #include "WebURLRequest.h" #include "WebViewClient.h" +#include "WebViewImpl.h" #include <wtf/Vector.h> // FIXME: Remove this once WebDevToolsAgentImpl and WebViewImpl move out of glue/. #include "webkit/glue/webdevtoolsagent_impl.h" -#include "webkit/glue/webview_impl.h" using namespace WebCore; @@ -75,7 +75,7 @@ Page* InspectorClientImpl::createPage() void InspectorClientImpl::showWindow() { - ASSERT(m_inspectedWebView->GetWebDevToolsAgentImpl()); + ASSERT(m_inspectedWebView->devToolsAgentImpl()); m_inspectedWebView->page()->inspectorController()->setWindowVisible(true); } @@ -87,7 +87,7 @@ void InspectorClientImpl::closeWindow() bool InspectorClientImpl::windowVisible() { - ASSERT(m_inspectedWebView->GetWebDevToolsAgentImpl()); + ASSERT(m_inspectedWebView->devToolsAgentImpl()); return false; } diff --git a/webkit/api/src/InspectorClientImpl.h b/webkit/api/src/InspectorClientImpl.h index aaeff93..62216b2 100644 --- a/webkit/api/src/InspectorClientImpl.h +++ b/webkit/api/src/InspectorClientImpl.h @@ -35,9 +35,8 @@ #include "InspectorController.h" #include <wtf/OwnPtr.h> -class WebViewImpl; - namespace WebKit { +class WebViewImpl; class InspectorClientImpl : public WebCore::InspectorClient { public: diff --git a/webkit/api/src/WebFrameImpl.cpp b/webkit/api/src/WebFrameImpl.cpp new file mode 100644 index 0000000..617cfd5 --- /dev/null +++ b/webkit/api/src/WebFrameImpl.cpp @@ -0,0 +1,1890 @@ +/* + * 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 +// | +// Page O------- Frame (m_mainFrame) O-------O FrameView +// || +// || +// FrameLoader O-------- WebFrame (via FrameLoaderClient) +// +// FrameLoader and Frame are formerly one object that was split apart because +// it got too big. They basically have the same lifetime, hence the double line. +// +// WebFrame is refcounted and has one ref on behalf of the FrameLoader/Frame. +// This is not a normal reference counted pointer because that would require +// changing WebKit code that we don't control. Instead, it is created with this +// ref initially and it is removed when the FrameLoader is getting destroyed. +// +// WebFrames are created in two places, first in WebViewImpl when the root +// frame is created, and second in WebFrame::CreateChildFrame when sub-frames +// are created. WebKit will hook up this object to the FrameLoader/Frame +// and the refcount will be correct. +// +// 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. +// +// Frame going away causes the FrameLoader to get deleted. In FrameLoader's +// destructor, it notifies its client with frameLoaderDestroyed. This calls +// WebFrame::Closing and then derefs the WebFrame and will cause it to be +// deleted (unless an external someone is also holding a reference). + +#include "config.h" +#include "WebFrameImpl.h" + +#include <algorithm> + +#include "Chrome.h" +#include "ChromiumBridge.h" +#include "ClipboardUtilitiesChromium.h" +#include "Console.h" +#include "Document.h" +#include "DocumentFragment.h" // Only needed for ReplaceSelectionCommand.h :( +#include "DocumentLoader.h" +#include "DocumentMarker.h" +#include "DOMWindow.h" +#include "DOMUtilitiesPrivate.h" +#include "Editor.h" +#include "EventHandler.h" +#include "FormState.h" +#include "FrameChromium.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLCollection.h" +#include "HTMLHeadElement.h" +#include "HTMLInputElement.h" +#include "HTMLFormElement.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLLinkElement.h" +#include "HTMLNames.h" +#include "HistoryItem.h" +#include "InspectorController.h" +#include "markup.h" +#include "Page.h" +#include "PasswordAutocompleteListener.h" +#include "PlatformContextSkia.h" +#include "PrintContext.h" +#include "RenderFrame.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "ReplaceSelectionCommand.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#include "ScriptController.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" +#include "ScrollbarTheme.h" +#include "ScrollTypes.h" +#include "SelectionController.h" +#include "Settings.h" +#include "SkiaUtils.h" +#include "SubstituteData.h" +#include "TextIterator.h" +#include "TextAffinity.h" +#include "XPathResult.h" +#include "WebConsoleMessage.h" +#include "WebDataSourceImpl.h" +#include "WebFindOptions.h" +#include "WebForm.h" +#include "WebFrameClient.h" +#include "WebHistoryItem.h" +#include "WebRange.h" +#include "WebRect.h" +#include "WebScriptSource.h" +#include "WebSecurityOrigin.h" +#include "WebSize.h" +#include "WebURLError.h" +#include "WebVector.h" +#include "WebViewImpl.h" +#include <wtf/CurrentTime.h> + +#if PLATFORM(DARWIN) +#include "LocalCurrentGraphicsContext.h" +#endif + +#if PLATFORM(LINUX) +#include <gdk/gdk.h> +#endif + +using namespace WebCore; + +namespace WebKit { + +// Key for a StatsCounter tracking how many WebFrames are active. +static const char* const webFrameActiveCount = "WebFrameActiveCount"; + +static const char* const osdType = "application/opensearchdescription+xml"; +static const char* const osdRel = "search"; + +// Backend for contentAsPlainText, this is a recursive function that gets +// the text for the current frame and all of its subframes. It will append +// the text of each frame in turn to the |output| up to |maxChars| length. +// +// The |frame| must be non-null. +static void frameContentAsPlainText(size_t maxChars, Frame* frame, + Vector<UChar>* output) +{ + Document* doc = frame->document(); + if (!doc) + return; + + if (!frame->view()) + return; + + // TextIterator iterates over the visual representation of the DOM. As such, + // it requires you to do a layout before using it (otherwise it'll crash). + if (frame->view()->needsLayout()) + frame->view()->layout(); + + // Select the document body. + RefPtr<Range> range(doc->createRange()); + ExceptionCode exception = 0; + range->selectNodeContents(doc->body(), exception); + + if (exception == 0) { + // The text iterator will walk nodes giving us text. This is similar to + // the plainText() function in 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()) { + const UChar* chars = it.characters(); + if (!chars) { + if (it.length() != 0) { + // It appears from crash reports that an iterator can get into a state + // where the character count is nonempty but the character pointer is + // null. advance()ing it will then just add that many to the null + // pointer which won't be caught in a null check but will crash. + // + // A null pointer and 0 length is common for some nodes. + // + // IF YOU CATCH THIS IN A DEBUGGER please let brettw know. We don't + // currently understand the conditions for this to occur. Ideally, the + // iterators would never get into the condition so we should fix them + // if we can. + ASSERT_NOT_REACHED(); + break; + } + + // Just got a null node, we can forge ahead! + continue; + } + size_t toAppend = + std::min(static_cast<size_t>(it.length()), maxChars - output->size()); + output->append(chars, toAppend); + if (output->size() >= maxChars) + return; // Filled up the buffer. + } + } + + // The separator between frames when the frames are converted to plain text. + const UChar frameSeparator[] = { '\n', '\n' }; + const size_t frameSeparatorLen = 2; + + // Recursively walk the children. + FrameTree* frameTree = frame->tree(); + for (Frame* curChild = frameTree->firstChild(); curChild; curChild = curChild->tree()->nextSibling()) { + // Make sure the frame separator won't fill up the buffer, and give up if + // it will. The danger is if the separator will make the buffer longer than + // maxChars. This will cause the computation above: + // maxChars - output->size() + // to be a negative number which will crash when the subframe is added. + if (output->size() >= maxChars - frameSeparatorLen) + return; + + output->append(frameSeparator, frameSeparatorLen); + frameContentAsPlainText(maxChars, curChild, output); + if (output->size() >= maxChars) + return; // Filled up the buffer. + } +} + +// Simple class to override some of PrintContext behavior. +class ChromePrintContext : public PrintContext, public Noncopyable { +public: + ChromePrintContext(Frame* frame) + : PrintContext(frame) + , m_printedPageWidth(0) + { + } + + void begin(float width) + { + ASSERT(!m_printedPageWidth); + m_printedPageWidth = width; + PrintContext::begin(m_printedPageWidth); + } + + float getPageShrink(int pageNumber) const + { + IntRect pageRect = m_pageRects[pageNumber]; + return m_printedPageWidth / pageRect.width(); + } + + // Spools the printed page, a subrect of m_frame. Skip the scale step. + // NativeTheme doesn't play well with scaling. Scaling is done browser side + // instead. Returns the scale to be applied. + float spoolPage(GraphicsContext& ctx, int pageNumber) + { + IntRect pageRect = m_pageRects[pageNumber]; + float scale = m_printedPageWidth / pageRect.width(); + + ctx.save(); + ctx.translate(static_cast<float>(-pageRect.x()), + static_cast<float>(-pageRect.y())); + ctx.clip(pageRect); + m_frame->view()->paintContents(&ctx, pageRect); + ctx.restore(); + return scale; + } + +private: + // Set when printing. + float m_printedPageWidth; +}; + +static WebDataSource* DataSourceForDocLoader(DocumentLoader* loader) +{ + return loader ? WebDataSourceImpl::fromDocumentLoader(loader) : 0; +} + + +// WebFrame ------------------------------------------------------------------- + +class WebFrameImpl::DeferredScopeStringMatches { +public: + DeferredScopeStringMatches(WebFrameImpl* webFrame, + int identifier, + const WebString& searchText, + const WebFindOptions& options, + bool reset) + : m_timer(this, &DeferredScopeStringMatches::doTimeout) + , m_webFrame(webFrame) + , m_identifier(identifier) + , m_searchText(searchText) + , m_options(options) + , m_reset(reset) + { + m_timer.startOneShot(0.0); + } + +private: + void doTimeout(Timer<DeferredScopeStringMatches>*) + { + m_webFrame->callScopeStringMatches( + this, m_identifier, m_searchText, m_options, m_reset); + } + + Timer<DeferredScopeStringMatches> m_timer; + RefPtr<WebFrameImpl> m_webFrame; + int m_identifier; + WebString m_searchText; + WebFindOptions m_options; + bool m_reset; +}; + + +// WebFrame ------------------------------------------------------------------- + +WebFrame* WebFrame::frameForEnteredContext() +{ + Frame* frame = + ScriptController::retrieveFrameForEnteredContext(); + return WebFrameImpl::fromFrame(frame); +} + +WebFrame* WebFrame::frameForCurrentContext() +{ + Frame* frame = + ScriptController::retrieveFrameForCurrentContext(); + return WebFrameImpl::fromFrame(frame); +} + +WebString WebFrameImpl::name() const +{ + return m_frame->tree()->name(); +} + +WebURL WebFrameImpl::url() const +{ + const WebDataSource* ds = dataSource(); + if (!ds) + return WebURL(); + return ds->request().url(); +} + +WebURL WebFrameImpl::favIconURL() const +{ + FrameLoader* frameLoader = m_frame->loader(); + // The URL to the favicon may be in the header. As such, only + // ask the loader for the favicon if it's finished loading. + if (frameLoader->state() == FrameStateComplete) { + const KURL& url = frameLoader->iconURL(); + if (!url.isEmpty()) + return url; + } + return WebURL(); +} + +WebURL WebFrameImpl::openSearchDescriptionURL() const +{ + FrameLoader* frameLoader = m_frame->loader(); + if (frameLoader->state() == FrameStateComplete + && m_frame->document() && m_frame->document()->head() + && !m_frame->tree()->parent()) { + HTMLHeadElement* head = m_frame->document()->head(); + if (head) { + RefPtr<HTMLCollection> children = head->children(); + for (Node* child = children->firstItem(); child != 0; child = children->nextItem()) { + HTMLLinkElement* linkElement = toHTMLLinkElement(child); + if (linkElement + && linkElement->type() == osdType + && linkElement->rel() == osdRel + && !linkElement->href().isEmpty()) + return linkElement->href(); + } + } + } + return WebURL(); +} + +WebSize WebFrameImpl::scrollOffset() const +{ + FrameView* view = frameView(); + if (view) + return view->scrollOffset(); + + return WebSize(); +} + +WebSize WebFrameImpl::contentsSize() const +{ + return frame()->view()->contentsSize(); +} + +int WebFrameImpl::contentsPreferredWidth() const +{ + if ((m_frame->document() != 0) && (m_frame->document()->renderView() != 0)) + return m_frame->document()->renderView()->minPrefWidth(); + + return 0; +} + +bool WebFrameImpl::hasVisibleContent() const +{ + return frame()->view()->visibleWidth() > 0 && frame()->view()->visibleHeight() > 0; +} + +WebView* WebFrameImpl::view() const +{ + return viewImpl(); +} + +WebFrame* WebFrameImpl::opener() const +{ + Frame* opener = 0; + if (m_frame) + opener = m_frame->loader()->opener(); + return fromFrame(opener); +} + +WebFrame* WebFrameImpl::parent() const +{ + Frame *parent = 0; + if (m_frame) + parent = m_frame->tree()->parent(); + return fromFrame(parent); +} + +WebFrame* WebFrameImpl::top() const +{ + if (m_frame) + return fromFrame(m_frame->tree()->top()); + + return 0; +} + +WebFrame* WebFrameImpl::firstChild() const +{ + return fromFrame(frame()->tree()->firstChild()); +} + +WebFrame* WebFrameImpl::lastChild() const +{ + return fromFrame(frame()->tree()->lastChild()); +} + +WebFrame* WebFrameImpl::nextSibling() const +{ + return fromFrame(frame()->tree()->nextSibling()); +} + +WebFrame* WebFrameImpl::previousSibling() const +{ + return fromFrame(frame()->tree()->previousSibling()); +} + +WebFrame* WebFrameImpl::traverseNext(bool wrap) const +{ + return fromFrame(frame()->tree()->traverseNextWithWrap(wrap)); +} + +WebFrame* WebFrameImpl::traversePrevious(bool wrap) const +{ + return fromFrame(frame()->tree()->traversePreviousWithWrap(wrap)); +} + +WebFrame* WebFrameImpl::findChildByName(const WebString& name) const +{ + return fromFrame(frame()->tree()->child(name)); +} + +WebFrame* WebFrameImpl::findChildByExpression(const WebString& xpath) const +{ + if (xpath.isEmpty()) + return 0; + + Document* document = m_frame->document(); + + ExceptionCode ec = 0; + PassRefPtr<XPathResult> xpathResult = + document->evaluate(xpath, + document, + 0, // namespace + XPathResult::ORDERED_NODE_ITERATOR_TYPE, + 0, // XPathResult object + ec); + if (!xpathResult.get()) + return 0; + + Node* node = xpathResult->iterateNext(ec); + + if (!node || !node->isFrameOwnerElement()) + return 0; + HTMLFrameOwnerElement* frameElement = + static_cast<HTMLFrameOwnerElement*>(node); + return fromFrame(frameElement->contentFrame()); +} + +void WebFrameImpl::forms(WebVector<WebForm>& results) const +{ + if (!m_frame) + return; + + RefPtr<HTMLCollection> forms = m_frame->document()->forms(); + size_t formCount = forms->length(); + + WebVector<WebForm> temp(formCount); + for (size_t i = 0; i < formCount; ++i) { + Node* node = forms->item(i); + // Strange but true, sometimes item can be 0. + if (node) + temp[i] = static_cast<HTMLFormElement*>(node); + } + results.swap(temp); +} + +WebSecurityOrigin WebFrameImpl::securityOrigin() const +{ + if (!m_frame || !m_frame->document()) + return WebSecurityOrigin(); + + return WebSecurityOrigin(m_frame->document()->securityOrigin()); +} + +void WebFrameImpl::grantUniversalAccess() +{ + ASSERT(m_frame && m_frame->document()); + if (m_frame && m_frame->document()) + m_frame->document()->securityOrigin()->grantUniversalAccess(); +} + +NPObject* WebFrameImpl::windowObject() const +{ + if (!m_frame) + return 0; + + return m_frame->script()->windowScriptNPObject(); +} + +void WebFrameImpl::bindToWindowObject(const WebString& name, NPObject* object) +{ + ASSERT(m_frame); + if (!m_frame || !m_frame->script()->isEnabled()) + return; + + String key = name; +#if USE(V8) + m_frame->script()->bindToWindowObject(m_frame, key, object); +#else + notImplemented(); +#endif +} + +void WebFrameImpl::executeScript(const WebScriptSource& source) +{ + m_frame->script()->executeScript( + ScriptSourceCode(source.code, source.url, source.startLine)); +} + +void WebFrameImpl::executeScriptInNewContext( + const WebScriptSource* sourcesIn, unsigned numSources, int extensionGroup) +{ + Vector<ScriptSourceCode> sources; + + for (unsigned i = 0; i < numSources; ++i) { + sources.append(ScriptSourceCode( + sourcesIn[i].code, sourcesIn[i].url, sourcesIn[i].startLine)); + } + + m_frame->script()->evaluateInNewContext(sources, extensionGroup); +} + +void WebFrameImpl::executeScriptInIsolatedWorld( + int worldId, const WebScriptSource* sourcesIn, unsigned numSources, + int extensionGroup) +{ + Vector<ScriptSourceCode> sources; + + for (unsigned i = 0; i < numSources; ++i) { + sources.append(ScriptSourceCode( + sourcesIn[i].code, sourcesIn[i].url, sourcesIn[i].startLine)); + } + + m_frame->script()->evaluateInIsolatedWorld(worldId, sources, extensionGroup); +} + +void WebFrameImpl::addMessageToConsole(const WebConsoleMessage& message) +{ + ASSERT(frame()); + + MessageLevel webCoreMessageLevel; + switch (message.level) { + case WebConsoleMessage::LevelTip: + webCoreMessageLevel = TipMessageLevel; + 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()->domWindow()->console()->addMessage( + OtherMessageSource, LogMessageType, webCoreMessageLevel, message.text, + 1, String()); +} + +void WebFrameImpl::collectGarbage() +{ + if (!m_frame) + return; + if (!m_frame->settings()->isJavaScriptEnabled()) + return; + // FIXME: Move this to the ScriptController and make it JS neutral. +#if USE(V8) + m_frame->script()->collectGarbage(); +#else + notImplemented(); +#endif +} + +#if USE(V8) +// Returns the V8 context for this frame, or an empty handle if there is none. +v8::Local<v8::Context> WebFrameImpl::mainWorldScriptContext() const +{ + if (!m_frame) + return v8::Local<v8::Context>(); + + return V8Proxy::mainWorldContext(m_frame); +} +#endif + +bool WebFrameImpl::insertStyleText( + const WebString& css, const WebString& id) { + Document* document = frame()->document(); + if (!document) + return false; + Element* documentElement = document->documentElement(); + if (!documentElement) + return false; + + ExceptionCode err = 0; + + if (!id.isEmpty()) { + Element* oldElement = document->getElementById(id); + if (oldElement) { + Node* parent = oldElement->parent(); + if (!parent) + return false; + parent->removeChild(oldElement, err); + } + } + + RefPtr<Element> stylesheet = document->createElement( + HTMLNames::styleTag, false); + if (!id.isEmpty()) + stylesheet->setAttribute(HTMLNames::idAttr, id); + stylesheet->setTextContent(css, err); + ASSERT(!err); + Node* first = documentElement->firstChild(); + bool success = documentElement->insertBefore(stylesheet, first, err); + ASSERT(success); + return success; +} + +void WebFrameImpl::reload() +{ + m_frame->loader()->history()->saveDocumentAndScrollState(); + + stopLoading(); // Make sure existing activity stops. + m_frame->loader()->reload(); +} + +void WebFrameImpl::loadRequest(const WebURLRequest& request) +{ + ASSERT(!request.isNull()); + const ResourceRequest& resourceRequest = request.toResourceRequest(); + + if (resourceRequest.url().protocolIs("javascript")) { + loadJavaScriptURL(resourceRequest.url()); + return; + } + + stopLoading(); // Make sure existing activity stops. + m_frame->loader()->load(resourceRequest, false); +} + +void WebFrameImpl::loadHistoryItem(const WebHistoryItem& item) +{ + RefPtr<HistoryItem> historyItem = PassRefPtr<HistoryItem>(item); + ASSERT(historyItem.get()); + + stopLoading(); // Make sure existing activity stops. + + // If there is no currentItem, which happens when we are navigating in + // session history after a crash, we need to manufacture one otherwise WebKit + // hoarks. This is probably the wrong thing to do, but it seems to work. + RefPtr<HistoryItem> currentItem = m_frame->loader()->history()->currentItem(); + if (!currentItem) { + currentItem = HistoryItem::create(); + currentItem->setLastVisitWasFailure(true); + m_frame->loader()->history()->setCurrentItem(currentItem.get()); + viewImpl()->setCurrentHistoryItem(currentItem.get()); + } + + m_frame->loader()->history()->goToItem( + historyItem.get(), FrameLoadTypeIndexedBackForward); +} + +void WebFrameImpl::loadData(const WebData& data, + const WebString& mimeType, + const WebString& textEncoding, + const WebURL& baseURL, + const WebURL& unreachableURL, + bool replace) +{ + SubstituteData substData(data, mimeType, textEncoding, unreachableURL); + ASSERT(substData.isValid()); + + stopLoading(); // Make sure existing activity stops. + m_frame->loader()->load(ResourceRequest(baseURL), substData, false); + if (replace) { + // Do this to force WebKit to treat the load as replacing the currently + // loaded page. + m_frame->loader()->setReplacing(); + } +} + +void WebFrameImpl::loadHTMLString(const WebData& data, + const WebURL& baseURL, + const WebURL& unreachableURL, + bool replace) +{ + loadData(data, + WebString::fromUTF8("text/html"), + WebString::fromUTF8("UTF-8"), + baseURL, + unreachableURL, + replace); +} + +bool WebFrameImpl::isLoading() const +{ + if (!m_frame) + return false; + return m_frame->loader()->isLoading(); +} + +void WebFrameImpl::stopLoading() +{ + if (!m_frame) + return; + + // FIXME: Figure out what we should really do here. It seems like a bug + // that FrameLoader::stopLoading doesn't call stopAllLoaders. + m_frame->loader()->stopAllLoaders(); + m_frame->loader()->stopLoading(UnloadEventPolicyNone); +} + +WebDataSource* WebFrameImpl::provisionalDataSource() const +{ + FrameLoader* frameLoader = m_frame->loader(); + + // We regard the policy document loader as still provisional. + DocumentLoader* docLoader = frameLoader->provisionalDocumentLoader(); + if (!docLoader) + docLoader = frameLoader->policyDocumentLoader(); + + return DataSourceForDocLoader(docLoader); +} + +WebDataSource* WebFrameImpl::dataSource() const +{ + return DataSourceForDocLoader(m_frame->loader()->documentLoader()); +} + +WebHistoryItem WebFrameImpl::previousHistoryItem() const +{ + // We use the previous item here because documentState (filled-out forms) + // only get saved to history when it becomes the previous item. The caller + // is expected to query the history item after a navigation occurs, after + // the desired history item has become the previous entry. + return WebHistoryItem(viewImpl()->previousHistoryItem()); +} + +WebHistoryItem WebFrameImpl::currentHistoryItem() const +{ + m_frame->loader()->history()->saveDocumentAndScrollState(); + + return WebHistoryItem(m_frame->page()->backForwardList()->currentItem()); +} + +void WebFrameImpl::enableViewSourceMode(bool enable) +{ + if (m_frame) + m_frame->setInViewSourceMode(enable); +} + +bool WebFrameImpl::isViewSourceModeEnabled() const +{ + if (m_frame) + return m_frame->inViewSourceMode(); + + return false; +} + +void WebFrameImpl::setReferrerForRequest( + WebURLRequest& request, const WebURL& referrerURL) { + String referrer; + if (referrerURL.isEmpty()) + referrer = m_frame->loader()->outgoingReferrer(); + else + referrer = referrerURL.spec().utf16(); + if (SecurityOrigin::shouldHideReferrer(request.url(), referrer)) + return; + request.setHTTPHeaderField(WebString::fromUTF8("Referer"), referrer); +} + +void WebFrameImpl::dispatchWillSendRequest(WebURLRequest& request) +{ + ResourceResponse response; + m_frame->loader()->client()->dispatchWillSendRequest( + 0, 0, request.toMutableResourceRequest(), response); +} + +void WebFrameImpl::commitDocumentData(const char* data, size_t dataLen) +{ + DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); + + // Set the text encoding. This calls begin() for us. It is safe to call + // this multiple times (Mac does: page/mac/WebCoreFrameBridge.mm). + bool userChosen = true; + String encoding = documentLoader->overrideEncoding(); + if (encoding.isNull()) { + userChosen = false; + encoding = documentLoader->response().textEncodingName(); + } + m_frame->loader()->setEncoding(encoding, userChosen); + + // NOTE: mac only does this if there is a document + m_frame->loader()->addData(data, dataLen); +} + +unsigned WebFrameImpl::unloadListenerCount() const +{ + return frame()->domWindow()->pendingUnloadEventListeners(); +} + +bool WebFrameImpl::isProcessingUserGesture() const +{ + return frame()->loader()->isProcessingUserGesture(); +} + +bool WebFrameImpl::willSuppressOpenerInNewFrame() const +{ + return frame()->loader()->suppressOpenerInNewFrame(); +} + +void WebFrameImpl::replaceSelection(const WebString& text) +{ + RefPtr<DocumentFragment> fragment = createFragmentFromText( + frame()->selection()->toNormalizedRange().get(), text); + applyCommand(ReplaceSelectionCommand::create( + frame()->document(), fragment.get(), false, true, true)); +} + +void WebFrameImpl::insertText(const WebString& text) +{ + frame()->editor()->insertText(text, 0); +} + +void WebFrameImpl::setMarkedText( + const WebString& text, unsigned location, unsigned length) +{ + Editor* editor = frame()->editor(); + + editor->confirmComposition(text); + + Vector<CompositionUnderline> decorations; + editor->setComposition(text, decorations, location, length); +} + +void WebFrameImpl::unmarkText() +{ + frame()->editor()->confirmCompositionWithoutDisturbingSelection(); +} + +bool WebFrameImpl::hasMarkedText() const +{ + return frame()->editor()->hasComposition(); +} + +WebRange WebFrameImpl::markedRange() const +{ + return frame()->editor()->compositionRange(); +} + +bool WebFrameImpl::executeCommand(const WebString& name) +{ + 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); + + bool rv = true; + + // Specially handling commands that Editor::execCommand does not directly + // support. + if (command == "DeleteToEndOfParagraph") { + Editor* editor = frame()->editor(); + if (!editor->deleteWithDirection(SelectionController::FORWARD, + ParagraphBoundary, + true, + false)) { + editor->deleteWithDirection(SelectionController::FORWARD, + CharacterGranularity, + true, + false); + } + } else if (command == "Indent") + frame()->editor()->indent(); + else if (command == "Outdent") + frame()->editor()->outdent(); + else if (command == "DeleteBackward") + rv = frame()->editor()->command(AtomicString("BackwardDelete")).execute(); + else if (command == "DeleteForward") + rv = frame()->editor()->command(AtomicString("ForwardDelete")).execute(); + else if (command == "AdvanceToNextMisspelling") { + // False must be passed here, or the currently selected word will never be + // skipped. + frame()->editor()->advanceToNextMisspelling(false); + } else if (command == "ToggleSpellPanel") + frame()->editor()->showSpellingGuessPanel(); + else + rv = frame()->editor()->command(command).execute(); + return rv; +} + +bool WebFrameImpl::executeCommand(const WebString& name, const WebString& value) +{ + ASSERT(frame()); + String webName = name; + + // moveToBeginningOfDocument and moveToEndfDocument are only handled by WebKit + // for editable nodes. + if (!frame()->editor()->canEdit() && webName == "moveToBeginningOfDocument") + return viewImpl()->propagateScroll(ScrollUp, ScrollByDocument); + + if (!frame()->editor()->canEdit() && webName == "moveToEndOfDocument") + return viewImpl()->propagateScroll(ScrollDown, ScrollByDocument); + + return frame()->editor()->command(webName).execute(value); +} + +bool WebFrameImpl::isCommandEnabled(const WebString& name) const +{ + ASSERT(frame()); + return frame()->editor()->command(name).isEnabled(); +} + +void WebFrameImpl::enableContinuousSpellChecking(bool enable) +{ + if (enable == isContinuousSpellCheckingEnabled()) + return; + frame()->editor()->toggleContinuousSpellChecking(); +} + +bool WebFrameImpl::isContinuousSpellCheckingEnabled() const +{ + return frame()->editor()->isContinuousSpellCheckingEnabled(); +} + +bool WebFrameImpl::hasSelection() const +{ + // frame()->selection()->isNone() never returns true. + return (frame()->selection()->start() != frame()->selection()->end()); +} + +WebRange WebFrameImpl::selectionRange() const +{ + return frame()->selection()->toNormalizedRange(); +} + +WebString WebFrameImpl::selectionAsText() const +{ + RefPtr<Range> range = frame()->selection()->toNormalizedRange(); + if (!range.get()) + return WebString(); + + String text = range->text(); +#if PLATFORM(WIN_OS) + replaceNewlinesWithWindowsStyleNewlines(text); +#endif + replaceNBSPWithSpace(text); + return text; +} + +WebString WebFrameImpl::selectionAsMarkup() const +{ + RefPtr<Range> range = frame()->selection()->toNormalizedRange(); + if (!range.get()) + return WebString(); + + return createMarkup(range.get(), 0); +} + +int WebFrameImpl::printBegin(const WebSize& pageSize) +{ + ASSERT(!frame()->document()->isFrameSet()); + + m_printContext.set(new ChromePrintContext(frame())); + FloatRect rect(0, 0, static_cast<float>(pageSize.width), + static_cast<float>(pageSize.height)); + m_printContext->begin(rect.width()); + float pageHeight; + // We ignore the overlays calculation for now since they are generated in the + // browser. pageHeight is actually an output parameter. + m_printContext->computePageRects(rect, 0, 0, 1.0, pageHeight); + return m_printContext->pageCount(); +} + +float WebFrameImpl::getPrintPageShrink(int page) +{ + // Ensure correct state. + if (!m_printContext.get() || page < 0) { + ASSERT_NOT_REACHED(); + return 0; + } + + return m_printContext->getPageShrink(page); +} + +float WebFrameImpl::printPage(int page, WebCanvas* canvas) +{ + // Ensure correct state. + if (!m_printContext.get() || page < 0 || !frame() || !frame()->document()) { + ASSERT_NOT_REACHED(); + return 0; + } + +#if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD) + PlatformContextSkia context(canvas); + GraphicsContext spool(&context); +#elif PLATFORM(DARWIN) + GraphicsContext spool(canvas); + LocalCurrentGraphicsContext localContext(&spool); +#endif + + return m_printContext->spoolPage(spool, page); +} + +void WebFrameImpl::printEnd() +{ + ASSERT(m_printContext.get()); + if (m_printContext.get()) + m_printContext->end(); + m_printContext.clear(); +} + +bool WebFrameImpl::find(int identifier, + const WebString& searchText, + const WebFindOptions& options, + bool wrapWithinFrame, + WebRect* selectionRect) +{ + WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); + + if (!options.findNext) + frame()->page()->unmarkAllTextMatches(); + else + setMarkerActive(m_activeMatch.get(), false); // Active match is changing. + + // Starts the search from the current selection. + bool startInSelection = true; + + // If the user has selected something since the last Find operation we want + // to start from there. Otherwise, we start searching from where the last Find + // operation left off (either a Find or a FindNext operation). + VisibleSelection selection(frame()->selection()->selection()); + if (selection.isNone() && m_activeMatch) { + selection = VisibleSelection(m_activeMatch.get()); + frame()->selection()->setSelection(selection); + } + + ASSERT(frame() && frame()->view()); + bool found = frame()->findString( + searchText, options.forward, options.matchCase, wrapWithinFrame, + startInSelection); + if (found) { + // Store which frame was active. This will come in handy later when we + // change the active match ordinal below. + WebFrameImpl* oldActiveFrame = mainFrameImpl->m_activeMatchFrame; + // Set this frame as the active frame (the one with the active highlight). + mainFrameImpl->m_activeMatchFrame = this; + + // We found something, so we can now query the selection for its position. + VisibleSelection newSelection(frame()->selection()->selection()); + IntRect currSelectionRect; + + // If we thought we found something, but it couldn't be selected (perhaps + // because it was marked -webkit-user-select: none), we can't set it to + // be active but we still continue searching. This matches Safari's + // behavior, including some oddities when selectable and un-selectable text + // are mixed on a page: see https://bugs.webkit.org/show_bug.cgi?id=19127. + if (newSelection.isNone() || (newSelection.start() == newSelection.end())) + m_activeMatch = 0; + else { + m_activeMatch = newSelection.toNormalizedRange(); + currSelectionRect = m_activeMatch->boundingBox(); + setMarkerActive(m_activeMatch.get(), true); // Active. + // WebKit draws the highlighting for all matches. + executeCommand(WebString::fromUTF8("Unselect")); + } + + if (!options.findNext) { + // This is a Find operation, so we set the flag to ask the scoping effort + // to find the active rect for us so we can update the ordinal (n of m). + m_locatingActiveRect = true; + } else { + if (oldActiveFrame != this) { + // If the active frame has changed it means that we have a multi-frame + // page and we just switch to searching in a new frame. Then we just + // want to reset the index. + if (options.forward) + m_activeMatchIndex = 0; + else + m_activeMatchIndex = m_lastMatchCount - 1; + } else { + // We are still the active frame, so increment (or decrement) the + // |m_activeMatchIndex|, wrapping if needed (on single frame pages). + options.forward ? ++m_activeMatchIndex : --m_activeMatchIndex; + if (m_activeMatchIndex + 1 > m_lastMatchCount) + m_activeMatchIndex = 0; + if (m_activeMatchIndex + 1 == 0) + m_activeMatchIndex = m_lastMatchCount - 1; + } + if (selectionRect) { + WebRect rect = frame()->view()->convertToContainingWindow(currSelectionRect); + rect.x -= frameView()->scrollOffset().width(); + rect.y -= frameView()->scrollOffset().height(); + *selectionRect = rect; + + reportFindInPageSelection(rect, m_activeMatchIndex + 1, identifier); + } + } + } else { + // Nothing was found in this frame. + m_activeMatch = 0; + + // Erase all previous tickmarks and highlighting. + invalidateArea(InvalidateAll); + } + + return found; +} + +void WebFrameImpl::stopFinding(bool clearSelection) +{ + if (!clearSelection) + setFindEndstateFocusAndSelection(); + cancelPendingScopingEffort(); + + // Remove all markers for matches found and turn off the highlighting. + if (!parent()) + frame()->document()->removeMarkers(DocumentMarker::TextMatch); + frame()->setMarkedTextMatchesAreHighlighted(false); + + // Let the frame know that we don't want tickmarks or highlighting anymore. + invalidateArea(InvalidateAll); +} + +void WebFrameImpl::scopeStringMatches(int identifier, + const WebString& searchText, + const WebFindOptions& options, + bool reset) +{ + if (!shouldScopeMatches(searchText)) + return; + + WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); + + if (reset) { + // This is a brand new search, so we need to reset everything. + // Scoping is just about to begin. + m_scopingComplete = false; + // Clear highlighting for this frame. + if (frame()->markedTextMatchesAreHighlighted()) + frame()->page()->unmarkAllTextMatches(); + // Clear the counters from last operation. + m_lastMatchCount = 0; + m_nextInvalidateAfter = 0; + + m_resumeScopingFromRange = 0; + + mainFrameImpl->m_framesScopingCount++; + + // Now, defer scoping until later to allow find operation to finish quickly. + scopeStringMatchesSoon( + identifier, + searchText, + options, + false); // false=we just reset, so don't do it again. + return; + } + + RefPtr<Range> searchRange(rangeOfContents(frame()->document())); + + ExceptionCode ec = 0, ec2 = 0; + if (m_resumeScopingFromRange.get()) { + // This is a continuation of a scoping operation that timed out and didn't + // complete last time around, so we should start from where we left off. + searchRange->setStart(m_resumeScopingFromRange->startContainer(), + m_resumeScopingFromRange->startOffset(ec2) + 1, + ec); + if (ec != 0 || ec2 != 0) { + if (ec2 != 0) // A non-zero |ec| happens when navigating during search. + ASSERT_NOT_REACHED(); + return; + } + } + + // This timeout controls how long we scope before releasing control. This + // value does not prevent us from running for longer than this, but it is + // periodically checked to see if we have exceeded our allocated time. + const double maxScopingDuration = 0.1; // seconds + + int matchCount = 0; + bool timedOut = false; + double startTime = currentTime(); + do { + // Find next occurrence of the search string. + // FIXME: (http://b/1088245) This WebKit operation may run for longer + // than the timeout value, and is not interruptible as it is currently + // written. We may need to rewrite it with interruptibility in mind, or + // find an alternative. + RefPtr<Range> resultRange(findPlainText(searchRange.get(), + searchText, + true, + options.matchCase)); + if (resultRange->collapsed(ec)) { + if (!resultRange->startContainer()->isInShadowTree()) + break; + + searchRange = rangeOfContents(frame()->document()); + searchRange->setStartAfter( + resultRange->startContainer()->shadowAncestorNode(), ec); + continue; + } + + // A non-collapsed result range can in some funky whitespace cases still not + // advance the range's start position (4509328). Break to avoid infinite + // loop. (This function is based on the implementation of + // Frame::markAllMatchesForText, which is where this safeguard comes from). + VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM); + if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM)) + break; + + // Only treat the result as a match if it is visible + if (frame()->editor()->insideVisibleArea(resultRange.get())) { + ++matchCount; + + setStart(searchRange.get(), newStart); + Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); + if (searchRange->collapsed(ec) && shadowTreeRoot) + searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); + + // Catch a special case where Find found something but doesn't know what + // the bounding box for it is. In this case we set the first match we find + // as the active rect. + IntRect resultBounds = resultRange->boundingBox(); + IntRect activeSelectionRect; + if (m_locatingActiveRect) { + activeSelectionRect = m_activeMatch.get() ? + m_activeMatch->boundingBox() : resultBounds; + } + + // If the Find function found a match it will have stored where the + // match was found in m_activeSelectionRect on the current frame. If we + // find this rect during scoping it means we have found the active + // tickmark. + bool foundActiveMatch = false; + if (m_locatingActiveRect && (activeSelectionRect == resultBounds)) { + // We have found the active tickmark frame. + mainFrameImpl->m_activeMatchFrame = this; + foundActiveMatch = true; + // We also know which tickmark is active now. + m_activeMatchIndex = matchCount - 1; + // To stop looking for the active tickmark, we set this flag. + m_locatingActiveRect = false; + + // Notify browser of new location for the selected rectangle. + resultBounds.move(-frameView()->scrollOffset().width(), + -frameView()->scrollOffset().height()); + reportFindInPageSelection( + frame()->view()->convertToContainingWindow(resultBounds), + m_activeMatchIndex + 1, + identifier); + } + + addMarker(resultRange.get(), foundActiveMatch); + } + + m_resumeScopingFromRange = resultRange; + timedOut = (currentTime() - startTime) >= maxScopingDuration; + } while (!timedOut); + + // Remember what we search for last time, so we can skip searching if more + // letters are added to the search string (and last outcome was 0). + m_lastSearchString = searchText; + + if (matchCount > 0) { + frame()->setMarkedTextMatchesAreHighlighted(true); + + m_lastMatchCount += matchCount; + + // Let the mainframe know how much we found during this pass. + mainFrameImpl->increaseMatchCount(matchCount, identifier); + } + + if (timedOut) { + // If we found anything during this pass, we should redraw. However, we + // don't want to spam too much if the page is extremely long, so if we + // reach a certain point we start throttling the redraw requests. + if (matchCount > 0) + invalidateIfNecessary(); + + // Scoping effort ran out of time, lets ask for another time-slice. + scopeStringMatchesSoon( + identifier, + searchText, + options, + false); // don't reset. + return; // Done for now, resume work later. + } + + // This frame has no further scoping left, so it is done. Other frames might, + // of course, continue to scope matches. + m_scopingComplete = true; + mainFrameImpl->m_framesScopingCount--; + + // If this is the last frame to finish scoping we need to trigger the final + // update to be sent. + if (mainFrameImpl->m_framesScopingCount == 0) + mainFrameImpl->increaseMatchCount(0, identifier); + + // This frame is done, so show any scrollbar tickmarks we haven't drawn yet. + invalidateArea(InvalidateScrollbar); +} + +void WebFrameImpl::cancelPendingScopingEffort() +{ + deleteAllValues(m_deferredScopingWork); + m_deferredScopingWork.clear(); + + m_activeMatchIndex = -1; +} + +void WebFrameImpl::increaseMatchCount(int count, int identifier) +{ + // This function should only be called on the mainframe. + ASSERT(!parent()); + + m_totalMatchCount += count; + + // Update the UI with the latest findings. + if (client()) { + client()->reportFindInPageMatchCount(identifier, m_totalMatchCount, + m_framesScopingCount == 0); + } +} + +void WebFrameImpl::reportFindInPageSelection(const WebRect& selectionRect, + int activeMatchOrdinal, + int identifier) +{ + // Update the UI with the latest selection rect. + if (client()) { + client()->reportFindInPageSelection( + identifier, ordinalOfFirstMatchForFrame(this) + activeMatchOrdinal, + selectionRect); + } +} + +void WebFrameImpl::resetMatchCount() +{ + m_totalMatchCount = 0; + m_framesScopingCount = 0; +} + +WebURL WebFrameImpl::completeURL(const WebString& url) const +{ + if (!m_frame || !m_frame->document()) + return WebURL(); + + return m_frame->document()->completeURL(url); +} + +WebString WebFrameImpl::contentAsText(size_t maxChars) const +{ + if (!m_frame) + return WebString(); + + Vector<UChar> text; + frameContentAsPlainText(maxChars, m_frame, &text); + return String::adopt(text); +} + +WebString WebFrameImpl::contentAsMarkup() const +{ + return createFullMarkup(m_frame->document()); +} + +// WebFrameImpl public --------------------------------------------------------- + +int WebFrameImpl::m_liveObjectCount = 0; + +PassRefPtr<WebFrameImpl> WebFrameImpl::create(WebFrameClient* client) +{ + return adoptRef(new WebFrameImpl(ClientHandle::create(client))); +} + +WebFrameImpl::WebFrameImpl(PassRefPtr<ClientHandle> clientHandle) + : m_frameLoaderClient(this) + , m_clientHandle(clientHandle) + , m_activeMatchFrame(0) + , m_activeMatchIndex(-1) + , m_locatingActiveRect(false) + , m_resumeScopingFromRange(0) + , m_lastMatchCount(-1) + , m_totalMatchCount(-1) + , m_framesScopingCount(-1) + , m_scopingComplete(false) + , m_nextInvalidateAfter(0) +{ + ChromiumBridge::incrementStatsCounter(webFrameActiveCount); + m_liveObjectCount++; +} + +WebFrameImpl::~WebFrameImpl() +{ + ChromiumBridge::decrementStatsCounter(webFrameActiveCount); + m_liveObjectCount--; + + cancelPendingScopingEffort(); + clearPasswordListeners(); +} + +void WebFrameImpl::initializeAsMainFrame(WebViewImpl* webViewImpl) +{ + RefPtr<Frame> frame = Frame::create(webViewImpl->page(), 0, &m_frameLoaderClient); + m_frame = frame.get(); + + // Add reference on behalf of FrameLoader. See comments in + // WebFrameLoaderClient::frameLoaderDestroyed for more info. + ref(); + + // We must call init() after m_frame is assigned because it is referenced + // during init(). + m_frame->init(); +} + +PassRefPtr<Frame> WebFrameImpl::createChildFrame( + const FrameLoadRequest& request, HTMLFrameOwnerElement* ownerElement) +{ + RefPtr<WebFrameImpl> webframe(adoptRef(new WebFrameImpl(m_clientHandle))); + + // Add an extra ref on behalf of the Frame/FrameLoader, which references the + // WebFrame via the FrameLoaderClient interface. See the comment at the top + // of this file for more info. + webframe->ref(); + + RefPtr<Frame> childFrame = Frame::create( + m_frame->page(), ownerElement, &webframe->m_frameLoaderClient); + webframe->m_frame = childFrame.get(); + + childFrame->tree()->setName(request.frameName()); + + m_frame->tree()->appendChild(childFrame); + + // Frame::init() can trigger onload event in the parent frame, + // which may detach this frame and trigger a null-pointer access + // in FrameTree::removeChild. Move init() after appendChild call + // so that webframe->mFrame is in the tree before triggering + // onload event handler. + // Because the event handler may set webframe->mFrame to null, + // it is necessary to check the value after calling init() and + // return without loading URL. + // (b:791612) + childFrame->init(); // create an empty document + if (!childFrame->tree()->parent()) + return 0; + + m_frame->loader()->loadURLIntoChildFrame( + request.resourceRequest().url(), + request.resourceRequest().httpReferrer(), + childFrame.get()); + + // A synchronous navigation (about:blank) would have already processed + // onload, so it is possible for the frame to have already been destroyed by + // script in the page. + if (!childFrame->tree()->parent()) + return 0; + + return childFrame.release(); +} + +void WebFrameImpl::layout() +{ + // layout this frame + FrameView* view = m_frame->view(); + if (view) + view->layoutIfNeededRecursive(); +} + +void WebFrameImpl::paint(WebCanvas* canvas, const WebRect& rect) +{ + if (rect.isEmpty()) + return; + IntRect dirtyRect(rect); +#if WEBKIT_USING_CG + GraphicsContext gc(canvas); + LocalCurrentGraphicsContext localContext(&gc); +#elif WEBKIT_USING_SKIA + PlatformContextSkia context(canvas); + + // PlatformGraphicsContext is actually a pointer to PlatformContextSkia + GraphicsContext gc(reinterpret_cast<PlatformGraphicsContext*>(&context)); +#else + notImplemented(); +#endif + gc.save(); + if (m_frame->document() && frameView()) { + gc.clip(dirtyRect); + frameView()->paint(&gc, dirtyRect); + m_frame->page()->inspectorController()->drawNodeHighlight(gc); + } else + gc.fillRect(dirtyRect, Color::white); + gc.restore(); +} + +void WebFrameImpl::createFrameView() +{ + ASSERT(m_frame); // If m_frame doesn't exist, we probably didn't init properly. + + Page* page = m_frame->page(); + ASSERT(page); + + ASSERT(page->mainFrame() != 0); + + bool isMainFrame = m_frame == page->mainFrame(); + if (isMainFrame && m_frame->view()) + m_frame->view()->setParentVisible(false); + + m_frame->setView(0); + + WebViewImpl* webView = viewImpl(); + + RefPtr<FrameView> view; + if (isMainFrame) + view = FrameView::create(m_frame, webView->size()); + else + view = FrameView::create(m_frame); + + m_frame->setView(view); + + if (webView->isTransparent()) + view->setTransparent(true); + + // FIXME: The Mac code has a comment about this possibly being unnecessary. + // See installInFrame in WebCoreFrameBridge.mm + if (m_frame->ownerRenderer()) + m_frame->ownerRenderer()->setWidget(view.get()); + + if (HTMLFrameOwnerElement* owner = m_frame->ownerElement()) + view->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff); + + if (isMainFrame) + view->setParentVisible(true); +} + +WebFrameImpl* WebFrameImpl::fromFrame(Frame* frame) +{ + if (!frame) + return 0; + + return static_cast<FrameLoaderClientImpl*>(frame->loader()->client())->webFrame(); +} + +WebViewImpl* WebFrameImpl::viewImpl() const +{ + if (!m_frame) + return 0; + + return WebViewImpl::fromPage(m_frame->page()); +} + +WebDataSourceImpl* WebFrameImpl::dataSourceImpl() const +{ + return static_cast<WebDataSourceImpl*>(dataSource()); +} + +WebDataSourceImpl* WebFrameImpl::provisionalDataSourceImpl() const +{ + return static_cast<WebDataSourceImpl*>(provisionalDataSource()); +} + +void WebFrameImpl::setFindEndstateFocusAndSelection() +{ + WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); + + if (this == mainFrameImpl->activeMatchFrame() && m_activeMatch.get()) { + // If the user has set the selection since the match was found, we + // don't focus anything. + VisibleSelection selection(frame()->selection()->selection()); + if (!selection.isNone()) + return; + + // Try to find the first focusable node up the chain, which will, for + // example, focus links if we have found text within the link. + Node* node = m_activeMatch->firstNode(); + while (node && !node->isFocusable() && node != frame()->document()) + node = node->parent(); + + if (node && node != frame()->document()) { + // Found a focusable parent node. Set focus to it. + frame()->document()->setFocusedNode(node); + } else { + // Iterate over all the nodes in the range until we find a focusable node. + // This, for example, sets focus to the first link if you search for + // text and text that is within one or more links. + node = m_activeMatch->firstNode(); + while (node && node != m_activeMatch->pastLastNode()) { + if (node->isFocusable()) { + frame()->document()->setFocusedNode(node); + break; + } + node = node->traverseNextNode(); + } + } + } +} + +void WebFrameImpl::didFail(const ResourceError& error, bool wasProvisional) +{ + if (!client()) + return; + WebURLError webError = error; + if (wasProvisional) + client()->didFailProvisionalLoad(this, webError); + else + client()->didFailLoad(this, webError); +} + +void WebFrameImpl::setAllowsScrolling(bool flag) +{ + m_frame->view()->setCanHaveScrollbars(flag); +} + +void WebFrameImpl::registerPasswordListener( + PassRefPtr<HTMLInputElement> inputElement, + PasswordAutocompleteListener* listener) +{ + RefPtr<HTMLInputElement> element = inputElement; + ASSERT(m_passwordListeners.find(element) == m_passwordListeners.end()); + m_passwordListeners.set(element, listener); +} + +PasswordAutocompleteListener* WebFrameImpl::getPasswordListener( + HTMLInputElement* inputElement) +{ + return m_passwordListeners.get(RefPtr<HTMLInputElement>(inputElement)); +} + +// WebFrameImpl private -------------------------------------------------------- + +void WebFrameImpl::closing() +{ + m_frame = 0; +} + +void WebFrameImpl::invalidateArea(AreaToInvalidate area) +{ + ASSERT(frame() && frame()->view()); + FrameView* view = frame()->view(); + + if ((area & InvalidateAll) == InvalidateAll) + view->invalidateRect(view->frameRect()); + else { + if ((area & InvalidateContentArea) == InvalidateContentArea) { + IntRect contentArea( + view->x(), view->y(), view->visibleWidth(), view->visibleHeight()); + view->invalidateRect(contentArea); + } + + if ((area & InvalidateScrollbar) == InvalidateScrollbar) { + // Invalidate the vertical scroll bar region for the view. + IntRect scrollBarVert( + view->x() + view->visibleWidth(), view->y(), + ScrollbarTheme::nativeTheme()->scrollbarThickness(), + view->visibleHeight()); + view->invalidateRect(scrollBarVert); + } + } +} + +void WebFrameImpl::addMarker(Range* range, bool activeMatch) +{ + // Use a TextIterator to visit the potentially multiple nodes the range + // covers. + TextIterator markedText(range); + for (; !markedText.atEnd(); markedText.advance()) { + RefPtr<Range> textPiece = markedText.range(); + int exception = 0; + + DocumentMarker marker = { + DocumentMarker::TextMatch, + textPiece->startOffset(exception), + textPiece->endOffset(exception), + "", + activeMatch + }; + + if (marker.endOffset > marker.startOffset) { + // Find the node to add a marker to and add it. + Node* node = textPiece->startContainer(exception); + frame()->document()->addMarker(node, marker); + + // Rendered rects for markers in WebKit are not populated until each time + // the markers are painted. However, we need it to happen sooner, because + // the whole purpose of tickmarks on the scrollbar is to show where + // matches off-screen are (that haven't been painted yet). + Vector<DocumentMarker> markers = frame()->document()->markersForNode(node); + frame()->document()->setRenderedRectForMarker( + textPiece->startContainer(exception), + markers[markers.size() - 1], + range->boundingBox()); + } + } +} + +void WebFrameImpl::setMarkerActive(Range* range, bool active) +{ + if (!range) + return; + + frame()->document()->setMarkersActive(range, active); +} + +int WebFrameImpl::ordinalOfFirstMatchForFrame(WebFrameImpl* frame) const +{ + int ordinal = 0; + WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl(); + // Iterate from the main frame up to (but not including) |frame| and + // add up the number of matches found so far. + for (WebFrameImpl* it = mainFrameImpl; + it != frame; + it = static_cast<WebFrameImpl*>(it->traverseNext(true))) { + if (it->m_lastMatchCount > 0) + ordinal += it->m_lastMatchCount; + } + return ordinal; +} + +bool WebFrameImpl::shouldScopeMatches(const String& searchText) +{ + // Don't scope if we can't find a frame or if the frame is not visible. + // The user may have closed the tab/application, so abort. + if (!frame() || !hasVisibleContent()) + return false; + + ASSERT(frame()->document() && frame()->view()); + + // If the frame completed the scoping operation and found 0 matches the last + // time it was searched, then we don't have to search it again if the user is + // just adding to the search string or sending the same search string again. + if (m_scopingComplete && !m_lastSearchString.isEmpty() && m_lastMatchCount == 0) { + // Check to see if the search string prefixes match. + String previousSearchPrefix = + searchText.substring(0, m_lastSearchString.length()); + + if (previousSearchPrefix == m_lastSearchString) + return false; // Don't search this frame, it will be fruitless. + } + + return true; +} + +void WebFrameImpl::scopeStringMatchesSoon(int identifier, const WebString& searchText, + const WebFindOptions& options, bool reset) +{ + m_deferredScopingWork.append(new DeferredScopeStringMatches( + this, identifier, searchText, options, reset)); +} + +void WebFrameImpl::callScopeStringMatches(DeferredScopeStringMatches* caller, + int identifier, const WebString& searchText, + const WebFindOptions& options, bool reset) +{ + m_deferredScopingWork.remove(m_deferredScopingWork.find(caller)); + + scopeStringMatches(identifier, searchText, options, reset); + + // This needs to happen last since searchText is passed by reference. + delete caller; +} + +void WebFrameImpl::invalidateIfNecessary() +{ + if (m_lastMatchCount > m_nextInvalidateAfter) { + // FIXME: (http://b/1088165) Optimize the drawing of the tickmarks and + // remove this. This calculation sets a milestone for when next to + // invalidate the scrollbar and the content area. We do this so that we + // don't spend too much time drawing the scrollbar over and over again. + // Basically, up until the first 500 matches there is no throttle. + // After the first 500 matches, we set set the milestone further and + // further out (750, 1125, 1688, 2K, 3K). + static const int startSlowingDownAfter = 500; + static const int slowdown = 750; + int i = (m_lastMatchCount / startSlowingDownAfter); + m_nextInvalidateAfter += i * slowdown; + + invalidateArea(InvalidateScrollbar); + } +} + +void WebFrameImpl::clearPasswordListeners() +{ + deleteAllValues(m_passwordListeners); + m_passwordListeners.clear(); +} + +void WebFrameImpl::loadJavaScriptURL(const KURL& url) +{ + // This is copied from FrameLoader::executeIfJavaScriptURL. Unfortunately, + // we cannot just use that method since it is private, and it also doesn't + // quite behave as we require it to for bookmarklets. The key difference is + // that we need to suppress loading the string result from evaluating the JS + // URL if executing the JS URL resulted in a location change. We also allow + // a JS URL to be loaded even if scripts on the page are otherwise disabled. + + if (!m_frame->document() || !m_frame->page()) + return; + + String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:"))); + ScriptValue result = m_frame->script()->executeScript(script, true); + + String scriptResult; + if (!result.getString(scriptResult)) + return; + + SecurityOrigin* securityOrigin = m_frame->document()->securityOrigin(); + + if (!m_frame->redirectScheduler()->locationChangePending()) { + m_frame->loader()->stopAllLoaders(); + m_frame->loader()->begin(m_frame->loader()->url(), true, securityOrigin); + m_frame->loader()->write(scriptResult); + m_frame->loader()->end(); + } +} + +} // namespace WebKit diff --git a/webkit/api/src/WebFrameImpl.h b/webkit/api/src/WebFrameImpl.h new file mode 100644 index 0000000..8a3772c --- /dev/null +++ b/webkit/api/src/WebFrameImpl.h @@ -0,0 +1,376 @@ +/* + * 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. + */ + +#ifndef WebFrameImpl_h +#define WebFrameImpl_h + +#include "Frame.h" +#include "FrameLoaderClientImpl.h" +#include "PlatformString.h" +// FIXME: remove this relative path once consumers from glue are removed. +#include "../public/WebFrame.h" +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { +class HistoryItem; +class KURL; +class Node; +class Range; +class SubstituteData; +struct WindowFeatures; +} + +namespace WebKit { +class ChromePrintContext; +class PasswordAutocompleteListener; +class WebDataSourceImpl; +class WebFrameClient; +class WebView; +class WebViewImpl; + +// Implementation of WebFrame, note that this is a reference counted object. +class WebFrameImpl : public WebFrame, public RefCounted<WebFrameImpl> { +public: + // WebFrame methods: + virtual WebString name() const; + virtual WebURL url() const; + virtual WebURL favIconURL() const; + virtual WebURL openSearchDescriptionURL() const; + virtual WebSize scrollOffset() const; + virtual WebSize contentsSize() const; + virtual int contentsPreferredWidth() const; + virtual bool hasVisibleContent() const; + virtual WebView* view() const; + virtual WebFrame* opener() const; + virtual WebFrame* parent() const; + virtual WebFrame* top() const; + virtual WebFrame* firstChild() const; + virtual WebFrame* lastChild() const; + virtual WebFrame* nextSibling() const; + virtual WebFrame* previousSibling() const; + virtual WebFrame* traverseNext(bool wrap) const; + virtual WebFrame* traversePrevious(bool wrap) const; + virtual WebFrame* findChildByName(const WebString&) const; + virtual WebFrame* findChildByExpression(const WebString&) const; + virtual void forms(WebVector<WebForm>&) const; + virtual WebSecurityOrigin securityOrigin() const; + virtual void grantUniversalAccess(); + virtual NPObject* windowObject() const; + virtual void bindToWindowObject(const WebString& name, NPObject*); + virtual void executeScript(const WebScriptSource&); + virtual void executeScriptInNewContext( + const WebScriptSource* sources, unsigned numSources, int extensionGroup); + virtual void executeScriptInIsolatedWorld( + int worldId, const WebScriptSource* sources, unsigned numSources, + int extensionGroup); + virtual void addMessageToConsole(const WebConsoleMessage&); + virtual void collectGarbage(); +#if WEBKIT_USING_V8 + virtual v8::Local<v8::Context> mainWorldScriptContext() const; +#endif + virtual bool insertStyleText(const WebString& css, const WebString& id); + virtual void reload(); + virtual void loadRequest(const WebURLRequest&); + virtual void loadHistoryItem(const WebHistoryItem&); + virtual void loadData( + const WebData&, const WebString& mimeType, const WebString& textEncoding, + const WebURL& baseURL, const WebURL& unreachableURL, bool replace); + virtual void loadHTMLString( + const WebData& html, const WebURL& baseURL, const WebURL& unreachableURL, + bool replace); + virtual bool isLoading() const; + virtual void stopLoading(); + virtual WebDataSource* provisionalDataSource() const; + virtual WebDataSource* dataSource() const; + virtual WebHistoryItem previousHistoryItem() const; + virtual WebHistoryItem currentHistoryItem() const; + virtual void enableViewSourceMode(bool enable); + virtual bool isViewSourceModeEnabled() const; + virtual void setReferrerForRequest(WebURLRequest&, const WebURL& referrer); + virtual void dispatchWillSendRequest(WebURLRequest&); + virtual void commitDocumentData(const char* data, size_t length); + virtual unsigned unloadListenerCount() const; + virtual bool isProcessingUserGesture() const; + virtual bool willSuppressOpenerInNewFrame() const; + virtual void replaceSelection(const WebString&); + virtual void insertText(const WebString&); + virtual void setMarkedText(const WebString&, unsigned location, unsigned length); + virtual void unmarkText(); + virtual bool hasMarkedText() const; + virtual WebRange markedRange() const; + virtual bool executeCommand(const WebString&); + virtual bool executeCommand(const WebString&, const WebString& value); + virtual bool isCommandEnabled(const WebString&) const; + virtual void enableContinuousSpellChecking(bool); + virtual bool isContinuousSpellCheckingEnabled() const; + virtual bool hasSelection() const; + virtual WebRange selectionRange() const; + virtual WebString selectionAsText() const; + virtual WebString selectionAsMarkup() const; + virtual int printBegin(const WebSize& pageSize); + virtual float printPage(int pageToPrint, WebCanvas*); + virtual float getPrintPageShrink(int page); + virtual void printEnd(); + virtual bool find( + int identifier, const WebString& searchText, const WebFindOptions&, + bool wrapWithinFrame, WebRect* selectionRect); + virtual void stopFinding(bool clearSelection); + virtual void scopeStringMatches( + int identifier, const WebString& searchText, const WebFindOptions&, + bool reset); + virtual void cancelPendingScopingEffort(); + virtual void increaseMatchCount(int count, int identifier); + virtual void resetMatchCount(); + virtual WebURL completeURL(const WebString& url) const; + virtual WebString contentAsText(size_t maxChars) const; + virtual WebString contentAsMarkup() const; + + static PassRefPtr<WebFrameImpl> create(WebFrameClient* client); + ~WebFrameImpl(); + + static int liveObjectCount() { + return m_liveObjectCount; + } + + // Called by the WebViewImpl to initialize its main frame: + void initializeAsMainFrame(WebViewImpl*); + + PassRefPtr<WebCore::Frame> createChildFrame( + const WebCore::FrameLoadRequest&, WebCore::HTMLFrameOwnerElement*); + + void layout(); + void paint(WebCanvas*, const WebRect&); + void createFrameView(); + + static WebFrameImpl* fromFrame(WebCore::Frame* frame); + + WebViewImpl* viewImpl() const; + + WebCore::Frame* frame() const { return m_frame; } + WebCore::FrameView* frameView() const { return m_frame ? m_frame->view() : 0; } + + // Getters for the impls corresponding to Get(Provisional)DataSource. They + // may return NULL if there is no corresponding data source. + WebDataSourceImpl* dataSourceImpl() const; + WebDataSourceImpl* provisionalDataSourceImpl() const; + + // Returns which frame has an active match. This function should only be + // called on the main frame, as it is the only frame keeping track. Returned + // value can be NULL if no frame has an active match. + const WebFrameImpl* activeMatchFrame() const { return m_activeMatchFrame; } + + // When a Find operation ends, we want to set the selection to what was active + // and set focus to the first focusable node we find (starting with the first + // node in the matched range and going up the inheritance chain). If we find + // nothing to focus we focus the first focusable node in the range. This + // allows us to set focus to a link (when we find text inside a link), which + // allows us to navigate by pressing Enter after closing the Find box. + void setFindEndstateFocusAndSelection(); + + void didFail(const WebCore::ResourceError&, bool wasProvisional); + + // Sets whether the WebFrameImpl allows its document to be scrolled. + // If the parameter is true, allow the document to be scrolled. + // Otherwise, disallow scrolling. + void setAllowsScrolling(bool); + + // Registers a listener for the specified user name input element. The + // listener will receive notifications for blur and when autocomplete should + // be triggered. + // The WebFrameImpl becomes the owner of the passed listener. + void registerPasswordListener( + PassRefPtr<WebCore::HTMLInputElement>, + PasswordAutocompleteListener*); + + // Returns the password autocomplete listener associated with the passed + // user name input element, or NULL if none available. + // Note that the returned listener is owner by the WebFrameImpl and should not + // be kept around as it is deleted when the page goes away. + PasswordAutocompleteListener* getPasswordListener(WebCore::HTMLInputElement*); + + WebFrameClient* client() const { return m_clientHandle->client(); } + void dropClient() { m_clientHandle->dropClient(); } + +private: + class DeferredScopeStringMatches; + friend class DeferredScopeStringMatches; + friend class FrameLoaderClientImpl; + + // A weak reference to the WebFrameClient. Each WebFrame in the hierarchy + // owns a reference to a ClientHandle. When the main frame is destroyed, it + // clears the WebFrameClient. + class ClientHandle : public RefCounted<ClientHandle> { + public: + static PassRefPtr<ClientHandle> create(WebFrameClient* client) + { + return adoptRef(new ClientHandle(client)); + } + WebFrameClient* client() { return m_client; } + void dropClient() { m_client = NULL; } + private: + ClientHandle(WebFrameClient* client) : m_client(client) {} + WebFrameClient* m_client; + }; + + // A bit mask specifying area of the frame to invalidate. + enum AreaToInvalidate { + InvalidateNothing, + InvalidateContentArea, + InvalidateScrollbar, // Vertical scrollbar only. + InvalidateAll // Both content area and the scrollbar. + }; + + WebFrameImpl(PassRefPtr<ClientHandle>); + + // Informs the WebFrame that the Frame is being closed, called by the + // WebFrameLoaderClient + void closing(); + + // Notifies the delegate about a new selection rect. + void reportFindInPageSelection( + const WebRect& selectionRect, int activeMatchOrdinal, int identifier); + + // Invalidates a certain area within the frame. + void invalidateArea(AreaToInvalidate); + + // Add a WebKit TextMatch-highlight marker to nodes in a range. + void addMarker(WebCore::Range*, bool activeMatch); + + // Sets the markers within a range as active or inactive. + void setMarkerActive(WebCore::Range*, bool active); + + // Returns the ordinal of the first match in the frame specified. This + // function enumerates the frames, starting with the main frame and up to (but + // not including) the frame passed in as a parameter and counts how many + // matches have been found. + int ordinalOfFirstMatchForFrame(WebFrameImpl*) const; + + // Determines whether the scoping effort is required for a particular frame. + // It is not necessary if the frame is invisible, for example, or if this + // is a repeat search that already returned nothing last time the same prefix + // was searched. + bool shouldScopeMatches(const WebCore::String& searchText); + + // Queue up a deferred call to scopeStringMatches. + void scopeStringMatchesSoon( + int identifier, const WebString& searchText, const WebFindOptions&, + bool reset); + + // Called by a DeferredScopeStringMatches instance. + void callScopeStringMatches( + DeferredScopeStringMatches*, int identifier, const WebString& searchText, + const WebFindOptions&, bool reset); + + // Determines whether to invalidate the content area and scrollbar. + void invalidateIfNecessary(); + + // Clears the map of password listeners. + void clearPasswordListeners(); + + void loadJavaScriptURL(const WebCore::KURL&); + + // Used to check for leaks of this object. + static int m_liveObjectCount; + + FrameLoaderClientImpl m_frameLoaderClient; + + RefPtr<ClientHandle> m_clientHandle; + + // This is a weak pointer to our corresponding WebCore frame. A reference to + // ourselves is held while frame_ is valid. See our Closing method. + WebCore::Frame* m_frame; + + // A way for the main frame to keep track of which frame has an active + // match. Should be NULL for all other frames. + WebFrameImpl* m_activeMatchFrame; + + // The range of the active match for the current frame. + RefPtr<WebCore::Range> m_activeMatch; + + // The index of the active match. + int m_activeMatchIndex; + + // This flag is used by the scoping effort to determine if we need to figure + // out which rectangle is the active match. Once we find the active + // rectangle we clear this flag. + bool m_locatingActiveRect; + + // The scoping effort can time out and we need to keep track of where we + // ended our last search so we can continue from where we left of. + RefPtr<WebCore::Range> m_resumeScopingFromRange; + + // Keeps track of the last string this frame searched for. This is used for + // short-circuiting searches in the following scenarios: When a frame has + // been searched and returned 0 results, we don't need to search that frame + // again if the user is just adding to the search (making it more specific). + WebCore::String m_lastSearchString; + + // Keeps track of how many matches this frame has found so far, so that we + // don't loose count between scoping efforts, and is also used (in conjunction + // with m_lastSearchString and m_scopingComplete) to figure out if we need to + // search the frame again. + int m_lastMatchCount; + + // This variable keeps a cumulative total of matches found so far for ALL the + // frames on the page, and is only incremented by calling IncreaseMatchCount + // (on the main frame only). It should be -1 for all other frames. + size_t m_totalMatchCount; + + // This variable keeps a cumulative total of how many frames are currently + // scoping, and is incremented/decremented on the main frame only. + // It should be -1 for all other frames. + int m_framesScopingCount; + + // Keeps track of whether the scoping effort was completed (the user may + // interrupt it before it completes by submitting a new search). + bool m_scopingComplete; + + // Keeps track of when the scoping effort should next invalidate the scrollbar + // and the frame area. + int m_nextInvalidateAfter; + + // A list of all of the pending calls to scopeStringMatches. + Vector<DeferredScopeStringMatches*> m_deferredScopingWork; + + // Valid between calls to BeginPrint() and EndPrint(). Containts the print + // information. Is used by PrintPage(). + OwnPtr<ChromePrintContext> m_printContext; + + // The input fields that are interested in edit events and their associated + // listeners. + typedef HashMap<RefPtr<WebCore::HTMLInputElement>, + PasswordAutocompleteListener*> PasswordListenerMap; + PasswordListenerMap m_passwordListeners; +}; + +} // namespace WebKit + +#endif diff --git a/webkit/api/src/WebStorageEventDispatcherImpl.cpp b/webkit/api/src/WebStorageEventDispatcherImpl.cpp index a6da503..2e6df47 100644 --- a/webkit/api/src/WebStorageEventDispatcherImpl.cpp +++ b/webkit/api/src/WebStorageEventDispatcherImpl.cpp @@ -35,10 +35,10 @@ #include "SecurityOrigin.h" -extern const char* pageGroupName; - namespace WebKit { +extern const char* pageGroupName; + WebStorageEventDispatcher* WebStorageEventDispatcher::create() { return new WebStorageEventDispatcherImpl(); diff --git a/webkit/api/src/WebViewImpl.cpp b/webkit/api/src/WebViewImpl.cpp new file mode 100644 index 0000000..3aa2279 --- /dev/null +++ b/webkit/api/src/WebViewImpl.cpp @@ -0,0 +1,1767 @@ +/* + * 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. + */ + +#include "config.h" +#include "WebViewImpl.h" + +#include "AutocompletePopupMenuClient.h" +#include "AXObjectCache.h" +#include "CSSStyleSelector.h" +#include "CSSValueKeywords.h" +#include "Cursor.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "DOMUtilitiesPrivate.h" +#include "DragController.h" +#include "DragData.h" +#include "Editor.h" +#include "EventHandler.h" +#include "FocusController.h" +#include "FontDescription.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLInputElement.h" +#include "HTMLMediaElement.h" +#include "HitTestResult.h" +#include "Image.h" +#include "InspectorController.h" +#include "IntRect.h" +#include "KeyboardCodes.h" +#include "KeyboardEvent.h" +#include "MIMETypeRegistry.h" +#include "NodeRenderStyle.h" +#include "Page.h" +#include "PageGroup.h" +#include "Pasteboard.h" +#include "PlatformContextSkia.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#include "PluginInfoStore.h" +#include "PopupMenuChromium.h" +#include "PopupMenuClient.h" +#include "RenderView.h" +#include "ResourceHandle.h" +#include "SecurityOrigin.h" +#include "SelectionController.h" +#include "Settings.h" +#include "TypingCommand.h" +#include "WebAccessibilityObject.h" +#include "WebDragData.h" +#include "WebFrameImpl.h" +#include "WebInputEvent.h" +#include "WebInputEventConversion.h" +#include "WebMediaPlayerAction.h" +#include "WebNode.h" +#include "WebPoint.h" +#include "WebPopupMenuImpl.h" +#include "WebRect.h" +#include "WebSettingsImpl.h" +#include "WebString.h" +#include "WebVector.h" +#include "WebViewClient.h" + +#if PLATFORM(WIN_OS) +#include "KeyboardCodesWin.h" +#include "RenderThemeChromiumWin.h" +#else +#include "KeyboardCodesPosix.h" +#include "RenderTheme.h" +#endif + +// FIXME +#include "webkit/glue/webdevtoolsagent_impl.h" + +// Get rid of WTF's pow define so we can use std::pow. +#undef pow +#include <cmath> // for std::pow + +using namespace WebCore; + +namespace WebKit { + +// Change the text zoom level by kTextSizeMultiplierRatio each time the user +// zooms text in or out (ie., change by 20%). The min and max values limit +// text zoom to half and 3x the original text size. These three values match +// those in Apple's port in WebKit/WebKit/WebView/WebView.mm +static const double textSizeMultiplierRatio = 1.2; +static const double minTextSizeMultiplier = 0.5; +static const double maxTextSizeMultiplier = 3.0; + +// The group name identifies a namespace of pages. Page group is used on OSX +// for some programs that use HTML views to display things that don't seem like +// web pages to the user (so shouldn't have visited link coloring). We only use +// one page group. +const char* pageGroupName = "default"; + +// Ensure that the WebDragOperation enum values stay in sync with the original +// DragOperation constants. +#define COMPILE_ASSERT_MATCHING_ENUM(coreName) \ + COMPILE_ASSERT(int(coreName) == int(Web##coreName), dummy##coreName) +COMPILE_ASSERT_MATCHING_ENUM(DragOperationNone); +COMPILE_ASSERT_MATCHING_ENUM(DragOperationCopy); +COMPILE_ASSERT_MATCHING_ENUM(DragOperationLink); +COMPILE_ASSERT_MATCHING_ENUM(DragOperationGeneric); +COMPILE_ASSERT_MATCHING_ENUM(DragOperationPrivate); +COMPILE_ASSERT_MATCHING_ENUM(DragOperationMove); +COMPILE_ASSERT_MATCHING_ENUM(DragOperationDelete); +COMPILE_ASSERT_MATCHING_ENUM(DragOperationEvery); + +// Note that focusOnShow is false so that the autocomplete popup is shown not +// activated. We need the page to still have focus so the user can keep typing +// while the popup is showing. +static const PopupContainerSettings autocompletePopupSettings = { + false, // focusOnShow + false, // setTextOnIndexChange + false, // acceptOnAbandon + true, // loopSelectionNavigation + true, // restrictWidthOfListBox. Same as other browser (Fx, IE, and safari) + // For autocomplete, we use the direction of the input field as the direction + // of the popup items. The main reason is to keep the display of items in + // drop-down the same as the items in the input field. + PopupContainerSettings::DOMElementDirection, +}; + +// WebView ---------------------------------------------------------------- + +WebView* WebView::create(WebViewClient* client) +{ + return new WebViewImpl(client); +} + +void WebView::updateVisitedLinkState(unsigned long long linkHash) +{ + Page::visitedStateChanged(PageGroup::pageGroup(pageGroupName), linkHash); +} + +void WebView::resetVisitedLinkState() +{ + Page::allVisitedStateChanged(PageGroup::pageGroup(pageGroupName)); +} + +void WebViewImpl::initializeMainFrame(WebFrameClient* frameClient) { + // NOTE: The WebFrameImpl takes a reference to itself within InitMainFrame + // and releases that reference once the corresponding Frame is destroyed. + RefPtr<WebFrameImpl> frame = WebFrameImpl::create(frameClient); + + frame->initializeAsMainFrame(this); + + if (m_client) { + WebDevToolsAgentClient* toolsClient = m_client->devToolsAgentClient(); + if (toolsClient) + m_devToolsAgent.set(new WebDevToolsAgentImpl(this, toolsClient)); + } + + // Restrict the access to the local file system + // (see WebView.mm WebView::_commonInitializationWithFrameName). + SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForLocalOnly); +} + +WebViewImpl::WebViewImpl(WebViewClient* client) + : m_client(client) + , m_backForwardListClientImpl(this) + , m_chromeClientImpl(this) + , m_contextMenuClientImpl(this) + , m_dragClientImpl(this) + , m_editorClientImpl(this) + , m_inspectorClientImpl(this) + , m_observedNewNavigation(false) +#ifndef NDEBUG + , m_newNavigationLoader(0) +#endif + , m_zoomLevel(0) + , m_contextMenuAllowed(false) + , m_doingDragAndDrop(false) + , m_ignoreInputEvents(false) + , m_suppressNextKeypressEvent(false) + , m_initialNavigationPolicy(WebNavigationPolicyIgnore) + , m_imeAcceptEvents(true) + , m_dragTargetDispatch(false) + , m_dragIdentity(0) + , m_dropEffect(DropEffectDefault) + , m_operationsAllowed(WebDragOperationNone) + , m_dragOperation(WebDragOperationNone) + , m_autocompletePopupShowing(false) + , m_isTransparent(false) + , m_tabsToLinks(false) +{ + // WebKit/win/WebView.cpp does the same thing, except they call the + // KJS specific wrapper around this method. We need to have threading + // initialized because CollatorICU requires it. + WTF::initializeThreading(); + + // set to impossible point so we always get the first mouse pos + m_lastMousePosition = WebPoint(-1, -1); + + // the page will take ownership of the various clients + m_page.set(new Page(&m_chromeClientImpl, + &m_contextMenuClientImpl, + &m_editorClientImpl, + &m_dragClientImpl, + &m_inspectorClientImpl, + 0)); + + m_page->backForwardList()->setClient(&m_backForwardListClientImpl); + m_page->setGroupName(pageGroupName); +} + +WebViewImpl::~WebViewImpl() +{ + ASSERT(!m_page); +} + +RenderTheme* WebViewImpl::theme() const +{ + return m_page.get() ? m_page->theme() : RenderTheme::defaultTheme().get(); +} + +WebFrameImpl* WebViewImpl::mainFrameImpl() +{ + return m_page.get() ? WebFrameImpl::fromFrame(m_page->mainFrame()) : 0; +} + +bool WebViewImpl::tabKeyCyclesThroughElements() const +{ + ASSERT(m_page.get()); + return m_page->tabKeyCyclesThroughElements(); +} + +void WebViewImpl::setTabKeyCyclesThroughElements(bool value) +{ + if (m_page) + m_page->setTabKeyCyclesThroughElements(value); +} + +void WebViewImpl::mouseMove(const WebMouseEvent& event) +{ + if (!mainFrameImpl() || !mainFrameImpl()->frameView()) + return; + + m_lastMousePosition = WebPoint(event.x, event.y); + + // We call mouseMoved here instead of handleMouseMovedEvent because we need + // our ChromeClientImpl to receive changes to the mouse position and + // tooltip text, and mouseMoved handles all of that. + mainFrameImpl()->frame()->eventHandler()->mouseMoved( + PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); +} + +void WebViewImpl::mouseLeave(const WebMouseEvent& event) +{ + // This event gets sent as the main frame is closing. In that case, just + // ignore it. + if (!mainFrameImpl() || !mainFrameImpl()->frameView()) + return; + + m_client->setMouseOverURL(WebURL()); + + mainFrameImpl()->frame()->eventHandler()->handleMouseMoveEvent( + PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); +} + +void WebViewImpl::mouseDown(const WebMouseEvent& event) +{ + if (!mainFrameImpl() || !mainFrameImpl()->frameView()) + return; + + m_lastMouseDownPoint = WebPoint(event.x, event.y); + + // If a text field that has focus is clicked again, we should display the + // autocomplete popup. + RefPtr<Node> clickedNode; + if (event.button == WebMouseEvent::ButtonLeft) { + RefPtr<Node> focusedNode = focusedWebCoreNode(); + if (focusedNode.get() && toHTMLInputElement(focusedNode.get())) { + IntPoint point(event.x, event.y); + point = m_page->mainFrame()->view()->windowToContents(point); + HitTestResult result(point); + result = m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(point, false); + if (result.innerNonSharedNode() == focusedNode) { + // Already focused text field was clicked, let's remember this. If + // focus has not changed after the mouse event is processed, we'll + // trigger the autocomplete. + clickedNode = focusedNode; + } + } + } + + mainFrameImpl()->frame()->eventHandler()->handleMousePressEvent( + PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); + + if (clickedNode.get() && clickedNode == focusedWebCoreNode()) { + // Focus has not changed, show the autocomplete popup. + static_cast<EditorClientImpl*>(m_page->editorClient())-> + showFormAutofillForNode(clickedNode.get()); + } + + // Dispatch the contextmenu event regardless of if the click was swallowed. + // On Windows, we handle it on mouse up, not down. +#if PLATFORM(DARWIN) + if (event.button == WebMouseEvent::ButtonRight + || (event.button == WebMouseEvent::ButtonLeft + && event.modifiers & WebMouseEvent::ControlKey)) + mouseContextMenu(event); +#elif PLATFORM(LINUX) + if (event.button == WebMouseEvent::ButtonRight) + mouseContextMenu(event); +#endif +} + +void WebViewImpl::mouseContextMenu(const WebMouseEvent& event) +{ + if (!mainFrameImpl() || !mainFrameImpl()->frameView()) + return; + + m_page->contextMenuController()->clearContextMenu(); + + PlatformMouseEventBuilder pme(mainFrameImpl()->frameView(), event); + + // Find the right target frame. See issue 1186900. + HitTestResult result = hitTestResultForWindowPos(pme.pos()); + Frame* targetFrame; + if (result.innerNonSharedNode()) + targetFrame = result.innerNonSharedNode()->document()->frame(); + else + targetFrame = m_page->focusController()->focusedOrMainFrame(); + +#if PLATFORM(WIN_OS) + targetFrame->view()->setCursor(pointerCursor()); +#endif + + m_contextMenuAllowed = true; + targetFrame->eventHandler()->sendContextMenuEvent(pme); + m_contextMenuAllowed = false; + // Actually showing the context menu is handled by the ContextMenuClient + // implementation... +} + +void WebViewImpl::mouseUp(const WebMouseEvent& event) +{ + if (!mainFrameImpl() || !mainFrameImpl()->frameView()) + return; + +#if PLATFORM(LINUX) + // If the event was a middle click, attempt to copy text into the focused + // frame. We execute this before we let the page have a go at the event + // because the page may change what is focused during in its event handler. + // + // This code is in the mouse up handler. There is some debate about putting + // this here, as opposed to the mouse down handler. + // xterm: pastes on up. + // GTK: pastes on down. + // Firefox: pastes on up. + // Midori: couldn't paste at all with 0.1.2 + // + // There is something of a webcompat angle to this well, as highlighted by + // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on + // down then the text is pasted just before the onclick handler runs and + // clears the text box. So it's important this happens after the + // handleMouseReleaseEvent() earlier in this function + if (event.button == WebMouseEvent::ButtonMiddle) { + Frame* focused = focusedWebCoreFrame(); + IntPoint clickPoint(m_lastMouseDownPoint.x, m_lastMouseDownPoint.y); + clickPoint = m_page->mainFrame()->view()->windowToContents(clickPoint); + HitTestResult hitTestResult = + focused->eventHandler()->hitTestResultAtPoint(clickPoint, false, false, + ShouldHitTestScrollbars); + // We don't want to send a paste when middle clicking a scroll bar or a + // link (which will navigate later in the code). + if (!hitTestResult.scrollbar() && !hitTestResult.isLiveLink() && focused) { + Editor* editor = focused->editor(); + Pasteboard* pasteboard = Pasteboard::generalPasteboard(); + bool oldSelectionMode = pasteboard->isSelectionMode(); + pasteboard->setSelectionMode(true); + editor->command(AtomicString("Paste")).execute(); + pasteboard->setSelectionMode(oldSelectionMode); + } + } +#endif + + mouseCaptureLost(); + mainFrameImpl()->frame()->eventHandler()->handleMouseReleaseEvent( + PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); + +#if PLATFORM(WIN_OS) + // Dispatch the contextmenu event regardless of if the click was swallowed. + // On Mac/Linux, we handle it on mouse down, not up. + if (event.button == WebMouseEvent::ButtonRight) + mouseContextMenu(event); +#endif +} + +void WebViewImpl::mouseWheel(const WebMouseWheelEvent& event) +{ + PlatformWheelEventBuilder platformEvent(mainFrameImpl()->frameView(), event); + mainFrameImpl()->frame()->eventHandler()->handleWheelEvent(platformEvent); +} + +bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) +{ + ASSERT((event.type == WebInputEvent::RawKeyDown) + || (event.type == WebInputEvent::KeyDown) + || (event.type == WebInputEvent::KeyUp)); + + // Please refer to the comments explaining the m_suppressNextKeypressEvent + // member. + // The m_suppressNextKeypressEvent is set if the KeyDown is handled by + // Webkit. A keyDown event is typically associated with a keyPress(char) + // event and a keyUp event. We reset this flag here as this is a new keyDown + // event. + m_suppressNextKeypressEvent = false; + + // Give autocomplete a chance to consume the key events it is interested in. + if (autocompleteHandleKeyEvent(event)) + return true; + + Frame* frame = focusedWebCoreFrame(); + if (!frame) + return false; + + EventHandler* handler = frame->eventHandler(); + if (!handler) + return keyEventDefault(event); + +#if PLATFORM(WIN_OS) || PLATFORM(LINUX) + if (((event.modifiers == 0) && (event.windowsKeyCode == VKEY_APPS)) + || ((event.modifiers == WebInputEvent::ShiftKey) && (event.windowsKeyCode == VKEY_F10))) { + sendContextMenuEvent(event); + return true; + } +#endif + + // It's not clear if we should continue after detecting a capslock keypress. + // I'll err on the side of continuing, which is the pre-existing behaviour. + if (event.windowsKeyCode == VKEY_CAPITAL) + handler->capsLockStateMayHaveChanged(); + + PlatformKeyboardEventBuilder evt(event); + + if (handler->keyEvent(evt)) { + if (WebInputEvent::RawKeyDown == event.type && !evt.isSystemKey()) + m_suppressNextKeypressEvent = true; + return true; + } + + return keyEventDefault(event); +} + +bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) +{ + if (!m_autocompletePopupShowing + // Home and End should be left to the text field to process. + || event.windowsKeyCode == VKEY_HOME + || event.windowsKeyCode == VKEY_END) + return false; + + // Pressing delete triggers the removal of the selected suggestion from the DB. + if (event.windowsKeyCode == VKEY_DELETE + && m_autocompletePopup->selectedIndex() != -1) { + Node* node = focusedWebCoreNode(); + if (!node || (node->nodeType() != Node::ELEMENT_NODE)) { + ASSERT_NOT_REACHED(); + return false; + } + Element* element = static_cast<Element*>(node); + if (!element->hasLocalName(HTMLNames::inputTag)) { + ASSERT_NOT_REACHED(); + return false; + } + + int selectedIndex = m_autocompletePopup->selectedIndex(); + HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element); + WebString name = inputElement->name(); + WebString value = m_autocompletePopupClient->itemText(selectedIndex); + m_client->removeAutofillSuggestions(name, value); + // Update the entries in the currently showing popup to reflect the + // deletion. + m_autocompletePopupClient->removeItemAtIndex(selectedIndex); + refreshAutofillPopup(); + return false; + } + + if (!m_autocompletePopup->isInterestedInEventForKey(event.windowsKeyCode)) + return false; + + if (m_autocompletePopup->handleKeyEvent(PlatformKeyboardEventBuilder(event))) { + // We need to ignore the next Char event after this otherwise pressing + // enter when selecting an item in the menu will go to the page. + if (WebInputEvent::RawKeyDown == event.type) + m_suppressNextKeypressEvent = true; + return true; + } + + return false; +} + +bool WebViewImpl::charEvent(const WebKeyboardEvent& event) +{ + ASSERT(event.type == WebInputEvent::Char); + + // Please refer to the comments explaining the m_suppressNextKeypressEvent + // member. The m_suppressNextKeypressEvent is set if the KeyDown is + // handled by Webkit. A keyDown event is typically associated with a + // keyPress(char) event and a keyUp event. We reset this flag here as it + // only applies to the current keyPress event. + if (m_suppressNextKeypressEvent) { + m_suppressNextKeypressEvent = false; + return true; + } + + Frame* frame = focusedWebCoreFrame(); + if (!frame) + return false; + + EventHandler* handler = frame->eventHandler(); + if (!handler) + return keyEventDefault(event); + + PlatformKeyboardEventBuilder evt(event); + if (!evt.isCharacterKey()) + return true; + + // Safari 3.1 does not pass off windows system key messages (WM_SYSCHAR) to + // the eventHandler::keyEvent. We mimic this behavior on all platforms since + // for now we are converting other platform's key events to windows key + // events. + if (evt.isSystemKey()) + return handler->handleAccessKey(evt); + + if (!handler->keyEvent(evt)) + return keyEventDefault(event); + + return true; +} + +// The WebViewImpl::SendContextMenuEvent function is based on the Webkit +// function +// bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in +// webkit\webkit\win\WebView.cpp. The only significant change in this +// function is the code to convert from a Keyboard event to the Right +// Mouse button up event. +// +// This function is an ugly copy/paste and should be cleaned up when the +// WebKitWin version is cleaned: https://bugs.webkit.org/show_bug.cgi?id=20438 +#if PLATFORM(WIN_OS) || PLATFORM(LINUX) +// FIXME: implement on Mac +bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event) +{ + static const int kContextMenuMargin = 1; + Frame* mainFrameImpl = page()->mainFrame(); + FrameView* view = mainFrameImpl->view(); + if (!view) + return false; + + IntPoint coords(-1, -1); +#if PLATFORM(WIN_OS) + int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT); +#else + int rightAligned = 0; +#endif + IntPoint location; + + // The context menu event was generated from the keyboard, so show the + // context menu by the current selection. + Position start = mainFrameImpl->selection()->selection().start(); + Position end = mainFrameImpl->selection()->selection().end(); + + if (!start.node() || !end.node()) { + location = IntPoint( + rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin, + kContextMenuMargin); + } else { + RenderObject* renderer = start.node()->renderer(); + if (!renderer) + return false; + + RefPtr<Range> selection = mainFrameImpl->selection()->toNormalizedRange(); + IntRect firstRect = mainFrameImpl->firstRectForRange(selection.get()); + + int x = rightAligned ? firstRect.right() : firstRect.x(); + location = IntPoint(x, firstRect.bottom()); + } + + location = view->contentsToWindow(location); + // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in + // the selected element. Ideally we'd have the position of a context menu + // event be separate from its target node. + coords = location + IntSize(0, -1); + + // The contextMenuController() holds onto the last context menu that was + // popped up on the page until a new one is created. We need to clear + // this menu before propagating the event through the DOM so that we can + // detect if we create a new menu for this event, since we won't create + // a new menu if the DOM swallows the event and the defaultEventHandler does + // not run. + page()->contextMenuController()->clearContextMenu(); + + Frame* focusedFrame = page()->focusController()->focusedOrMainFrame(); + focusedFrame->view()->setCursor(pointerCursor()); + WebMouseEvent mouseEvent; + mouseEvent.button = WebMouseEvent::ButtonRight; + mouseEvent.x = coords.x(); + mouseEvent.y = coords.y(); + mouseEvent.type = WebInputEvent::MouseUp; + + PlatformMouseEventBuilder platformEvent(view, mouseEvent); + + m_contextMenuAllowed = true; + bool handled = focusedFrame->eventHandler()->sendContextMenuEvent(platformEvent); + m_contextMenuAllowed = false; + return handled; +} +#endif + +bool WebViewImpl::keyEventDefault(const WebKeyboardEvent& event) +{ + Frame* frame = focusedWebCoreFrame(); + if (!frame) + return false; + + switch (event.type) { + case WebInputEvent::Char: + if (event.windowsKeyCode == VKEY_SPACE) { + int keyCode = ((event.modifiers & WebInputEvent::ShiftKey) ? VKEY_PRIOR : VKEY_NEXT); + return scrollViewWithKeyboard(keyCode, event.modifiers); + } + break; + case WebInputEvent::RawKeyDown: + if (event.modifiers == WebInputEvent::ControlKey) { + switch (event.windowsKeyCode) { + case 'A': + focusedFrame()->executeCommand(WebString::fromUTF8("SelectAll")); + return true; + case VKEY_INSERT: + case 'C': + focusedFrame()->executeCommand(WebString::fromUTF8("Copy")); + return true; + // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl + // key combinations which affect scrolling. Safari is buggy in the + // sense that it scrolls the page for all Ctrl+scrolling key + // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc. + case VKEY_HOME: + case VKEY_END: + break; + default: + return false; + } + } + if (!event.isSystemKey && !(event.modifiers & WebInputEvent::ShiftKey)) + return scrollViewWithKeyboard(event.windowsKeyCode, event.modifiers); + break; + default: + break; + } + return false; +} + +bool WebViewImpl::scrollViewWithKeyboard(int keyCode, int modifiers) +{ + ScrollDirection scrollDirection; + ScrollGranularity scrollGranularity; + + switch (keyCode) { + case VKEY_LEFT: + scrollDirection = ScrollLeft; + scrollGranularity = ScrollByLine; + break; + case VKEY_RIGHT: + scrollDirection = ScrollRight; + scrollGranularity = ScrollByLine; + break; + case VKEY_UP: + scrollDirection = ScrollUp; + scrollGranularity = ScrollByLine; + break; + case VKEY_DOWN: + scrollDirection = ScrollDown; + scrollGranularity = ScrollByLine; + break; + case VKEY_HOME: + scrollDirection = ScrollUp; + scrollGranularity = ScrollByDocument; + break; + case VKEY_END: + scrollDirection = ScrollDown; + scrollGranularity = ScrollByDocument; + break; + case VKEY_PRIOR: // page up + scrollDirection = ScrollUp; + scrollGranularity = ScrollByPage; + break; + case VKEY_NEXT: // page down + scrollDirection = ScrollDown; + scrollGranularity = ScrollByPage; + break; + default: + return false; + } + + return propagateScroll(scrollDirection, scrollGranularity); +} + +bool WebViewImpl::propagateScroll(ScrollDirection scrollDirection, + ScrollGranularity scrollGranularity) +{ + Frame* frame = focusedWebCoreFrame(); + if (!frame) + return false; + + bool scrollHandled = + frame->eventHandler()->scrollOverflow(scrollDirection, + scrollGranularity); + Frame* currentFrame = frame; + while (!scrollHandled && currentFrame) { + scrollHandled = currentFrame->view()->scroll(scrollDirection, + scrollGranularity); + currentFrame = currentFrame->tree()->parent(); + } + return scrollHandled; +} + +Frame* WebViewImpl::focusedWebCoreFrame() +{ + return m_page.get() ? m_page->focusController()->focusedOrMainFrame() : 0; +} + +WebViewImpl* WebViewImpl::fromPage(Page* page) +{ + if (!page) + return 0; + + return static_cast<ChromeClientImpl*>(page->chrome()->client())->webview(); +} + +// WebWidget ------------------------------------------------------------------ + +void WebViewImpl::close() +{ + RefPtr<WebFrameImpl> mainFrameImpl; + + if (m_page.get()) { + // Initiate shutdown for the entire frameset. This will cause a lot of + // notifications to be sent. + if (m_page->mainFrame()) { + mainFrameImpl = WebFrameImpl::fromFrame(m_page->mainFrame()); + m_page->mainFrame()->loader()->frameDetached(); + } + m_page.clear(); + } + + // Should happen after m_page.reset(). + if (m_devToolsAgent.get()) + m_devToolsAgent.clear(); + + // We drop the client after the page has been destroyed to support the + // WebFrameClient::didDestroyScriptContext method. + if (mainFrameImpl) + mainFrameImpl->dropClient(); + + // Reset the delegate to prevent notifications being sent as we're being + // deleted. + m_client = 0; + + deref(); // Balances ref() acquired in WebView::create +} + +void WebViewImpl::resize(const WebSize& newSize) +{ + if (m_size == newSize) + return; + m_size = newSize; + + if (mainFrameImpl()->frameView()) { + mainFrameImpl()->frameView()->resize(m_size.width, m_size.height); + mainFrameImpl()->frame()->eventHandler()->sendResizeEvent(); + } + + if (m_client) { + WebRect damagedRect(0, 0, m_size.width, m_size.height); + m_client->didInvalidateRect(damagedRect); + } +} + +void WebViewImpl::layout() +{ + WebFrameImpl* webframe = mainFrameImpl(); + if (webframe) { + // In order for our child HWNDs (NativeWindowWidgets) to update properly, + // they need to be told that we are updating the screen. The problem is + // that the native widgets need to recalculate their clip region and not + // overlap any of our non-native widgets. To force the resizing, call + // setFrameRect(). This will be a quick operation for most frames, but + // the NativeWindowWidgets will update a proper clipping region. + FrameView* view = webframe->frameView(); + if (view) + view->setFrameRect(view->frameRect()); + + // setFrameRect may have the side-effect of causing existing page + // layout to be invalidated, so layout needs to be called last. + + webframe->layout(); + } +} + +void WebViewImpl::paint(WebCanvas* canvas, const WebRect& rect) +{ + WebFrameImpl* webframe = mainFrameImpl(); + if (webframe) + webframe->paint(canvas, rect); +} + +// FIXME: m_currentInputEvent should be removed once ChromeClient::show() can +// get the current-event information from WebCore. +const WebInputEvent* WebViewImpl::m_currentInputEvent = 0; + +bool WebViewImpl::handleInputEvent(const WebInputEvent& inputEvent) +{ + // If we've started a drag and drop operation, ignore input events until + // we're done. + if (m_doingDragAndDrop) + return true; + + if (m_ignoreInputEvents) + return true; + + // FIXME: Remove m_currentInputEvent. + // This only exists to allow ChromeClient::show() to know which mouse button + // triggered a window.open event. + // Safari must perform a similar hack, ours is in our WebKit glue layer + // theirs is in the application. This should go when WebCore can be fixed + // to pass more event information to ChromeClient::show() + m_currentInputEvent = &inputEvent; + + bool handled = true; + + // FIXME: WebKit seems to always return false on mouse events processing + // methods. For now we'll assume it has processed them (as we are only + // interested in whether keyboard events are processed). + switch (inputEvent.type) { + case WebInputEvent::MouseMove: + mouseMove(*static_cast<const WebMouseEvent*>(&inputEvent)); + break; + + case WebInputEvent::MouseLeave: + mouseLeave(*static_cast<const WebMouseEvent*>(&inputEvent)); + break; + + case WebInputEvent::MouseWheel: + mouseWheel(*static_cast<const WebMouseWheelEvent*>(&inputEvent)); + break; + + case WebInputEvent::MouseDown: + mouseDown(*static_cast<const WebMouseEvent*>(&inputEvent)); + break; + + case WebInputEvent::MouseUp: + mouseUp(*static_cast<const WebMouseEvent*>(&inputEvent)); + break; + + case WebInputEvent::RawKeyDown: + case WebInputEvent::KeyDown: + case WebInputEvent::KeyUp: + handled = keyEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent)); + break; + + case WebInputEvent::Char: + handled = charEvent(*static_cast<const WebKeyboardEvent*>(&inputEvent)); + break; + + default: + handled = false; + } + + m_currentInputEvent = 0; + + return handled; +} + +void WebViewImpl::mouseCaptureLost() +{ +} + +void WebViewImpl::setFocus(bool enable) +{ + m_page->focusController()->setFocused(enable); + if (enable) { + // Note that we don't call setActive() when disabled as this cause extra + // focus/blur events to be dispatched. + m_page->focusController()->setActive(true); + m_imeAcceptEvents = true; + } else { + hideAutoCompletePopup(); + + // Clear focus on the currently focused frame if any. + if (!m_page.get()) + return; + + Frame* frame = m_page->mainFrame(); + if (!frame) + return; + + RefPtr<Frame> focusedFrame = m_page->focusController()->focusedFrame(); + if (focusedFrame.get()) { + // Finish an ongoing composition to delete the composition node. + Editor* editor = focusedFrame->editor(); + if (editor && editor->hasComposition()) + editor->confirmComposition(); + m_imeAcceptEvents = false; + } + } +} + +bool WebViewImpl::handleCompositionEvent(WebCompositionCommand command, + int cursorPosition, + int targetStart, + int targetEnd, + const WebString& imeString) +{ + Frame* focused = focusedWebCoreFrame(); + if (!focused || !m_imeAcceptEvents) + return false; + Editor* editor = focused->editor(); + if (!editor) + return false; + if (!editor->canEdit()) { + // The input focus has been moved to another WebWidget object. + // We should use this |editor| object only to complete the ongoing + // composition. + if (!editor->hasComposition()) + return false; + } + + // We should verify the parent node of this IME composition node are + // editable because JavaScript may delete a parent node of the composition + // node. In this case, WebKit crashes while deleting texts from the parent + // node, which doesn't exist any longer. + PassRefPtr<Range> range = editor->compositionRange(); + if (range) { + const Node* node = range->startPosition().node(); + if (!node || !node->isContentEditable()) + return false; + } + + if (command == WebCompositionCommandDiscard) { + // A browser process sent an IPC message which does not contain a valid + // string, which means an ongoing composition has been canceled. + // If the ongoing composition has been canceled, replace the ongoing + // composition string with an empty string and complete it. + String emptyString; + Vector<CompositionUnderline> emptyUnderlines; + editor->setComposition(emptyString, emptyUnderlines, 0, 0); + } else { + // A browser process sent an IPC message which contains a string to be + // displayed in this Editor object. + // To display the given string, set the given string to the + // m_compositionNode member of this Editor object and display it. + if (targetStart < 0) + targetStart = 0; + if (targetEnd < 0) + targetEnd = static_cast<int>(imeString.length()); + String compositionString(imeString); + // Create custom underlines. + // To emphasize the selection, the selected region uses a solid black + // for its underline while other regions uses a pale gray for theirs. + Vector<CompositionUnderline> underlines(3); + underlines[0].startOffset = 0; + underlines[0].endOffset = targetStart; + underlines[0].thick = true; + underlines[0].color.setRGB(0xd3, 0xd3, 0xd3); + underlines[1].startOffset = targetStart; + underlines[1].endOffset = targetEnd; + underlines[1].thick = true; + underlines[1].color.setRGB(0x00, 0x00, 0x00); + underlines[2].startOffset = targetEnd; + underlines[2].endOffset = static_cast<int>(imeString.length()); + underlines[2].thick = true; + underlines[2].color.setRGB(0xd3, 0xd3, 0xd3); + // When we use custom underlines, WebKit ("InlineTextBox.cpp" Line 282) + // prevents from writing a text in between 'selectionStart' and + // 'selectionEnd' somehow. + // Therefore, we use the 'cursorPosition' for these arguments so that + // there are not any characters in the above region. + editor->setComposition(compositionString, underlines, + cursorPosition, cursorPosition); + // The given string is a result string, which means the ongoing + // composition has been completed. I have to call the + // Editor::confirmCompletion() and complete this composition. + if (command == WebCompositionCommandConfirm) + editor->confirmComposition(); + } + + return editor->hasComposition(); +} + +bool WebViewImpl::queryCompositionStatus(bool* enableIME, WebRect* caretRect) +{ + // Store whether the selected node needs IME and the caret rectangle. + // This process consists of the following four steps: + // 1. Retrieve the selection controller of the focused frame; + // 2. Retrieve the caret rectangle from the controller; + // 3. Convert the rectangle, which is relative to the parent view, to the + // one relative to the client window, and; + // 4. Store the converted rectangle. + const Frame* focused = focusedWebCoreFrame(); + if (!focused) + return false; + + const Editor* editor = focused->editor(); + if (!editor || !editor->canEdit()) + return false; + + SelectionController* controller = focused->selection(); + if (!controller) + return false; + + const Node* node = controller->start().node(); + if (!node) + return false; + + *enableIME = node->shouldUseInputMethod() && !controller->isInPasswordField(); + const FrameView* view = node->document()->view(); + if (!view) + return false; + + *caretRect = view->contentsToWindow(controller->absoluteCaretBounds()); + return true; +} + +void WebViewImpl::setTextDirection(WebTextDirection direction) +{ + // The Editor::setBaseWritingDirection() function checks if we can change + // the text direction of the selected node and updates its DOM "dir" + // attribute and its CSS "direction" property. + // So, we just call the function as Safari does. + const Frame* focused = focusedWebCoreFrame(); + if (!focused) + return; + + Editor* editor = focused->editor(); + if (!editor || !editor->canEdit()) + return; + + switch (direction) { + case WebTextDirectionDefault: + editor->setBaseWritingDirection(NaturalWritingDirection); + break; + + case WebTextDirectionLeftToRight: + editor->setBaseWritingDirection(LeftToRightWritingDirection); + break; + + case WebTextDirectionRightToLeft: + editor->setBaseWritingDirection(RightToLeftWritingDirection); + break; + + default: + notImplemented(); + break; + } +} + +// WebView -------------------------------------------------------------------- + +WebSettings* WebViewImpl::settings() +{ + if (!m_webSettings.get()) + m_webSettings.set(new WebSettingsImpl(m_page->settings())); + ASSERT(m_webSettings.get()); + return m_webSettings.get(); +} + +WebString WebViewImpl::pageEncoding() const +{ + if (!m_page.get()) + return WebString(); + + return m_page->mainFrame()->loader()->encoding(); +} + +void WebViewImpl::setPageEncoding(const WebString& encodingName) +{ + if (!m_page.get()) + return; + + // Only change override encoding, don't change default encoding. + // Note that the new encoding must be 0 if it isn't supposed to be set. + String newEncodingName; + if (!encodingName.isEmpty()) + newEncodingName = encodingName; + m_page->mainFrame()->loader()->reloadWithOverrideEncoding(newEncodingName); +} + +bool WebViewImpl::dispatchBeforeUnloadEvent() +{ + // FIXME: This should really cause a recursive depth-first walk of all + // frames in the tree, calling each frame's onbeforeunload. At the moment, + // we're consistent with Safari 3.1, not IE/FF. + Frame* frame = m_page->focusController()->focusedOrMainFrame(); + if (!frame) + return true; + + return frame->shouldClose(); +} + +void WebViewImpl::dispatchUnloadEvent() +{ + // Run unload handlers. + m_page->mainFrame()->loader()->closeURL(); +} + +WebFrame* WebViewImpl::mainFrame() +{ + return mainFrameImpl(); +} + +WebFrame* WebViewImpl::findFrameByName( + const WebString& name, WebFrame* relativeToFrame) +{ + if (!relativeToFrame) + relativeToFrame = mainFrame(); + Frame* frame = static_cast<WebFrameImpl*>(relativeToFrame)->frame(); + frame = frame->tree()->find(name); + return WebFrameImpl::fromFrame(frame); +} + +WebFrame* WebViewImpl::focusedFrame() +{ + return WebFrameImpl::fromFrame(focusedWebCoreFrame()); +} + +void WebViewImpl::setFocusedFrame(WebFrame* frame) +{ + if (!frame) { + // Clears the focused frame if any. + Frame* frame = focusedWebCoreFrame(); + if (frame) + frame->selection()->setFocused(false); + return; + } + WebFrameImpl* frameImpl = static_cast<WebFrameImpl*>(frame); + Frame* webcoreFrame = frameImpl->frame(); + webcoreFrame->page()->focusController()->setFocusedFrame(webcoreFrame); +} + +void WebViewImpl::setInitialFocus(bool reverse) +{ + if (!m_page.get()) + return; + + // Since we don't have a keyboard event, we'll create one. + WebKeyboardEvent keyboardEvent; + keyboardEvent.type = WebInputEvent::RawKeyDown; + if (reverse) + keyboardEvent.modifiers = WebInputEvent::ShiftKey; + + // VK_TAB which is only defined on Windows. + keyboardEvent.windowsKeyCode = 0x09; + PlatformKeyboardEventBuilder platformEvent(keyboardEvent); + RefPtr<KeyboardEvent> webkitEvent = KeyboardEvent::create(platformEvent, 0); + page()->focusController()->setInitialFocus( + reverse ? FocusDirectionBackward : FocusDirectionForward, + webkitEvent.get()); +} + +void WebViewImpl::clearFocusedNode() +{ + if (!m_page.get()) + return; + + RefPtr<Frame> frame = m_page->mainFrame(); + if (!frame.get()) + return; + + RefPtr<Document> document = frame->document(); + if (!document.get()) + return; + + RefPtr<Node> oldFocusedNode = document->focusedNode(); + + // Clear the focused node. + document->setFocusedNode(0); + + if (!oldFocusedNode.get()) + return; + + // If a text field has focus, we need to make sure the selection controller + // knows to remove selection from it. Otherwise, the text field is still + // processing keyboard events even though focus has been moved to the page and + // keystrokes get eaten as a result. + if (oldFocusedNode->hasTagName(HTMLNames::textareaTag) + || (oldFocusedNode->hasTagName(HTMLNames::inputTag) + && static_cast<HTMLInputElement*>(oldFocusedNode.get())->isTextField())) { + // Clear the selection. + SelectionController* selection = frame->selection(); + selection->clear(); + } +} + +void WebViewImpl::zoomIn(bool textOnly) +{ + Frame* frame = mainFrameImpl()->frame(); + double multiplier = std::min(std::pow(textSizeMultiplierRatio, m_zoomLevel + 1), + maxTextSizeMultiplier); + float zoomFactor = static_cast<float>(multiplier); + if (zoomFactor != frame->zoomFactor()) { + ++m_zoomLevel; + frame->setZoomFactor(zoomFactor, textOnly); + } +} + +void WebViewImpl::zoomOut(bool textOnly) +{ + Frame* frame = mainFrameImpl()->frame(); + double multiplier = std::max(std::pow(textSizeMultiplierRatio, m_zoomLevel - 1), + minTextSizeMultiplier); + float zoomFactor = static_cast<float>(multiplier); + if (zoomFactor != frame->zoomFactor()) { + --m_zoomLevel; + frame->setZoomFactor(zoomFactor, textOnly); + } +} + +void WebViewImpl::zoomDefault() +{ + // We don't change the zoom mode (text only vs. full page) here. We just want + // to reset whatever is already set. + m_zoomLevel = 0; + mainFrameImpl()->frame()->setZoomFactor( + 1.0f, mainFrameImpl()->frame()->isZoomFactorTextOnly()); +} + +void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action, + const WebPoint& location) +{ + HitTestResult result = + hitTestResultForWindowPos(location); + RefPtr<Node> node = result.innerNonSharedNode(); + if (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag)) + return; + + RefPtr<HTMLMediaElement> mediaElement = + static_pointer_cast<HTMLMediaElement>(node); + switch (action.type) { + case WebMediaPlayerAction::Play: + if (action.enable) + mediaElement->play(); + else + mediaElement->pause(); + break; + case WebMediaPlayerAction::Mute: + mediaElement->setMuted(action.enable); + break; + case WebMediaPlayerAction::Loop: + mediaElement->setLoop(action.enable); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +void WebViewImpl::copyImageAt(const WebPoint& point) +{ + if (!m_page.get()) + return; + + HitTestResult result = hitTestResultForWindowPos(point); + + if (result.absoluteImageURL().isEmpty()) { + // There isn't actually an image at these coordinates. Might be because + // the window scrolled while the context menu was open or because the page + // changed itself between when we thought there was an image here and when + // we actually tried to retreive the image. + // + // FIXME: implement a cache of the most recent HitTestResult to avoid having + // to do two hit tests. + return; + } + + m_page->mainFrame()->editor()->copyImage(result); +} + +void WebViewImpl::dragSourceEndedAt( + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperation operation) +{ + PlatformMouseEvent pme(clientPoint, + screenPoint, + LeftButton, MouseEventMoved, 0, false, false, false, + false, 0); + m_page->mainFrame()->eventHandler()->dragSourceEndedAt(pme, + static_cast<DragOperation>(operation)); +} + +void WebViewImpl::dragSourceMovedTo( + const WebPoint& clientPoint, + const WebPoint& screenPoint) +{ + PlatformMouseEvent pme(clientPoint, + screenPoint, + LeftButton, MouseEventMoved, 0, false, false, false, + false, 0); + m_page->mainFrame()->eventHandler()->dragSourceMovedTo(pme); +} + +void WebViewImpl::dragSourceSystemDragEnded() +{ + // It's possible for us to get this callback while not doing a drag if + // it's from a previous page that got unloaded. + if (m_doingDragAndDrop) { + m_page->dragController()->dragEnded(); + m_doingDragAndDrop = false; + } +} + +WebDragOperation WebViewImpl::dragTargetDragEnter( + const WebDragData& webDragData, int identity, + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperationsMask operationsAllowed) +{ + ASSERT(!m_currentDragData.get()); + + m_currentDragData = webDragData; + m_dragIdentity = identity; + m_operationsAllowed = operationsAllowed; + + DragData dragData( + m_currentDragData.get(), + clientPoint, + screenPoint, + static_cast<DragOperation>(operationsAllowed)); + + m_dropEffect = DropEffectDefault; + m_dragTargetDispatch = true; + DragOperation effect = m_page->dragController()->dragEntered(&dragData); + // Mask the operation against the drag source's allowed operations. + if ((effect & dragData.draggingSourceOperationMask()) != effect) + effect = DragOperationNone; + m_dragTargetDispatch = false; + + if (m_dropEffect != DropEffectDefault) { + m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy + : WebDragOperationNone; + } else + m_dragOperation = static_cast<WebDragOperation>(effect); + return m_dragOperation; +} + +WebDragOperation WebViewImpl::dragTargetDragOver( + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperationsMask operationsAllowed) +{ + ASSERT(m_currentDragData.get()); + + m_operationsAllowed = operationsAllowed; + DragData dragData( + m_currentDragData.get(), + clientPoint, + screenPoint, + static_cast<DragOperation>(operationsAllowed)); + + m_dropEffect = DropEffectDefault; + m_dragTargetDispatch = true; + DragOperation effect = m_page->dragController()->dragUpdated(&dragData); + // Mask the operation against the drag source's allowed operations. + if ((effect & dragData.draggingSourceOperationMask()) != effect) + effect = DragOperationNone; + m_dragTargetDispatch = false; + + if (m_dropEffect != DropEffectDefault) { + m_dragOperation = (m_dropEffect != DropEffectNone) ? WebDragOperationCopy + : WebDragOperationNone; + } else + m_dragOperation = static_cast<WebDragOperation>(effect); + return m_dragOperation; +} + +void WebViewImpl::dragTargetDragLeave() +{ + ASSERT(m_currentDragData.get()); + + DragData dragData( + m_currentDragData.get(), + IntPoint(), + IntPoint(), + static_cast<DragOperation>(m_operationsAllowed)); + + m_dragTargetDispatch = true; + m_page->dragController()->dragExited(&dragData); + m_dragTargetDispatch = false; + + m_currentDragData = 0; + m_dropEffect = DropEffectDefault; + m_dragOperation = WebDragOperationNone; + m_dragIdentity = 0; +} + +void WebViewImpl::dragTargetDrop(const WebPoint& clientPoint, + const WebPoint& screenPoint) +{ + ASSERT(m_currentDragData.get()); + + // If this webview transitions from the "drop accepting" state to the "not + // accepting" state, then our IPC message reply indicating that may be in- + // flight, or else delayed by javascript processing in this webview. If a + // drop happens before our IPC reply has reached the browser process, then + // the browser forwards the drop to this webview. So only allow a drop to + // proceed if our webview m_dragOperation state is not DragOperationNone. + + if (m_dragOperation == WebDragOperationNone) { // IPC RACE CONDITION: do not allow this drop. + dragTargetDragLeave(); + return; + } + + DragData dragData( + m_currentDragData.get(), + clientPoint, + screenPoint, + static_cast<DragOperation>(m_operationsAllowed)); + + m_dragTargetDispatch = true; + m_page->dragController()->performDrag(&dragData); + m_dragTargetDispatch = false; + + m_currentDragData = 0; + m_dropEffect = DropEffectDefault; + m_dragOperation = WebDragOperationNone; + m_dragIdentity = 0; +} + +int WebViewImpl::dragIdentity() +{ + if (m_dragTargetDispatch) + return m_dragIdentity; + return 0; +} + +void WebViewImpl::inspectElementAt(const WebPoint& point) +{ + if (!m_page.get()) + return; + + if (point.x == -1 || point.y == -1) + m_page->inspectorController()->inspect(0); + else { + HitTestResult result = hitTestResultForWindowPos(point); + + if (!result.innerNonSharedNode()) + return; + + m_page->inspectorController()->inspect(result.innerNonSharedNode()); + } +} + +WebString WebViewImpl::inspectorSettings() const +{ + return m_inspectorSettings; +} + +void WebViewImpl::setInspectorSettings(const WebString& settings) +{ + m_inspectorSettings = settings; +} + +WebDevToolsAgent* WebViewImpl::devToolsAgent() +{ + return m_devToolsAgent.get(); +} + +WebAccessibilityObject WebViewImpl::accessibilityObject() +{ + if (!mainFrameImpl()) + return WebAccessibilityObject(); + + Document* document = mainFrameImpl()->frame()->document(); + return WebAccessibilityObject( + document->axObjectCache()->getOrCreate(document->renderer())); +} + +void WebViewImpl::applyAutofillSuggestions( + const WebNode& node, + const WebVector<WebString>& suggestions, + int defaultSuggestionIndex) +{ + if (!m_page.get() || suggestions.isEmpty()) { + hideAutoCompletePopup(); + return; + } + + ASSERT(defaultSuggestionIndex < static_cast<int>(suggestions.size())); + + if (RefPtr<Frame> focused = m_page->focusController()->focusedFrame()) { + RefPtr<Document> document = focused->document(); + if (!document.get()) { + hideAutoCompletePopup(); + return; + } + + RefPtr<Node> focusedNode = document->focusedNode(); + // If the node for which we queried the autofill suggestions is not the + // focused node, then we have nothing to do. FIXME: also check the + // carret is at the end and that the text has not changed. + if (!focusedNode.get() || focusedNode != PassRefPtr<Node>(node)) { + hideAutoCompletePopup(); + return; + } + + if (!focusedNode->hasTagName(HTMLNames::inputTag)) { + ASSERT_NOT_REACHED(); + return; + } + + HTMLInputElement* inputElem = + static_cast<HTMLInputElement*>(focusedNode.get()); + + // The first time the autocomplete is shown we'll create the client and the + // popup. + if (!m_autocompletePopupClient.get()) + m_autocompletePopupClient.set(new AutocompletePopupMenuClient(this)); + m_autocompletePopupClient->initialize(inputElem, + suggestions, + defaultSuggestionIndex); + if (!m_autocompletePopup.get()) { + m_autocompletePopup = + PopupContainer::create(m_autocompletePopupClient.get(), + autocompletePopupSettings); + } + + if (m_autocompletePopupShowing) { + m_autocompletePopupClient->setSuggestions(suggestions); + refreshAutofillPopup(); + } else { + m_autocompletePopup->show(focusedNode->getRect(), + focusedNode->ownerDocument()->view(), 0); + m_autocompletePopupShowing = true; + } + } +} + +void WebViewImpl::hideAutofillPopup() +{ + hideAutoCompletePopup(); +} + +// WebView -------------------------------------------------------------------- + +bool WebViewImpl::setDropEffect(bool accept) { + if (m_dragTargetDispatch) { + m_dropEffect = accept ? DropEffectCopy : DropEffectNone; + return true; + } + return false; +} + +WebDevToolsAgentImpl* WebViewImpl::devToolsAgentImpl() +{ + return m_devToolsAgent.get(); +} + +void WebViewImpl::setIsTransparent(bool isTransparent) +{ + // Set any existing frames to be transparent. + Frame* frame = m_page->mainFrame(); + while (frame) { + frame->view()->setTransparent(isTransparent); + frame = frame->tree()->traverseNext(); + } + + // Future frames check this to know whether to be transparent. + m_isTransparent = isTransparent; +} + +bool WebViewImpl::isTransparent() const +{ + return m_isTransparent; +} + +void WebViewImpl::setIsActive(bool active) +{ + if (page() && page()->focusController()) + page()->focusController()->setActive(active); +} + +bool WebViewImpl::isActive() const +{ + return (page() && page()->focusController()) ? page()->focusController()->isActive() : false; +} + +void WebViewImpl::didCommitLoad(bool* isNewNavigation) +{ + if (isNewNavigation) + *isNewNavigation = m_observedNewNavigation; + +#ifndef NDEBUG + ASSERT(!m_observedNewNavigation + || m_page->mainFrame()->loader()->documentLoader() == m_newNavigationLoader); + m_newNavigationLoader = 0; +#endif + m_observedNewNavigation = false; +} + +bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button, + bool ctrl, bool shift, + bool alt, bool meta, + WebNavigationPolicy* policy) +{ +#if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD) + const bool newTabModifier = (button == 1) || ctrl; +#elif PLATFORM(DARWIN) + const bool newTabModifier = (button == 1) || meta; +#endif + if (!newTabModifier && !shift && !alt) + return false; + + ASSERT(policy); + if (newTabModifier) { + if (shift) + *policy = WebNavigationPolicyNewForegroundTab; + else + *policy = WebNavigationPolicyNewBackgroundTab; + } else { + if (shift) + *policy = WebNavigationPolicyNewWindow; + else + *policy = WebNavigationPolicyDownload; + } + return true; +} + +void WebViewImpl::startDragging(const WebPoint& eventPos, + const WebDragData& dragData, + WebDragOperationsMask mask) +{ + if (!m_client) + return; + ASSERT(!m_doingDragAndDrop); + m_doingDragAndDrop = true; + m_client->startDragging(eventPos, dragData, mask); +} + +void WebViewImpl::setCurrentHistoryItem(HistoryItem* item) +{ + m_backForwardListClientImpl.setCurrentHistoryItem(item); +} + +HistoryItem* WebViewImpl::previousHistoryItem() +{ + return m_backForwardListClientImpl.previousHistoryItem(); +} + +void WebViewImpl::observeNewNavigation() +{ + m_observedNewNavigation = true; +#ifndef NDEBUG + m_newNavigationLoader = m_page->mainFrame()->loader()->documentLoader(); +#endif +} + +void WebViewImpl::hideAutoCompletePopup() +{ + if (m_autocompletePopupShowing) { + m_autocompletePopup->hidePopup(); + autoCompletePopupDidHide(); + } +} + +void WebViewImpl::autoCompletePopupDidHide() +{ + m_autocompletePopupShowing = false; +} + +void WebViewImpl::setIgnoreInputEvents(bool newValue) +{ + ASSERT(m_ignoreInputEvents != newValue); + m_ignoreInputEvents = newValue; +} + +#if ENABLE(NOTIFICATIONS) +NotificationPresenterImpl* WebViewImpl::notificationPresenterImpl() +{ + if (!m_notificationPresenter.isInitialized() && m_client) + m_notificationPresenter.initialize(m_client->notificationPresenter()); + return &m_notificationPresenter; +} +#endif + +void WebViewImpl::refreshAutofillPopup() { + ASSERT(m_autocompletePopupShowing); + + // Hide the popup if it has become empty. + if (m_autocompletePopupClient->listSize() == 0) { + hideAutoCompletePopup(); + return; + } + + IntRect oldBounds = m_autocompletePopup->boundsRect(); + m_autocompletePopup->refresh(); + IntRect newBounds = m_autocompletePopup->boundsRect(); + // Let's resize the backing window if necessary. + if (oldBounds != newBounds) { + WebPopupMenuImpl* popupMenu = + static_cast<WebPopupMenuImpl*>(m_autocompletePopup->client()); + popupMenu->client()->setWindowRect(newBounds); + } +} + +Node* WebViewImpl::focusedWebCoreNode() +{ + Frame* frame = m_page->focusController()->focusedFrame(); + if (!frame) + return 0; + + Document* document = frame->document(); + if (!document) + return 0; + + return document->focusedNode(); +} + +HitTestResult WebViewImpl::hitTestResultForWindowPos(const IntPoint& pos) +{ + IntPoint docPoint(m_page->mainFrame()->view()->windowToContents(pos)); + return m_page->mainFrame()->eventHandler()->hitTestResultAtPoint(docPoint, false); +} + +void WebViewImpl::setTabsToLinks(bool enable) +{ + m_tabsToLinks = enable; +} + +bool WebViewImpl::tabsToLinks() const +{ + return m_tabsToLinks; +} + +} // namespace WebKit diff --git a/webkit/api/src/WebViewImpl.h b/webkit/api/src/WebViewImpl.h new file mode 100644 index 0000000..7f99256 --- /dev/null +++ b/webkit/api/src/WebViewImpl.h @@ -0,0 +1,419 @@ +/* + * 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. + */ + +#ifndef WebViewImpl_h +#define WebViewImpl_h + +#include "BackForwardListClientImpl.h" +#include "ChromeClientImpl.h" +#include "ContextMenuClientImpl.h" +#include "DragClientImpl.h" +#include "EditorClientImpl.h" +#include "InspectorClientImpl.h" +#include "NotificationPresenterImpl.h" +// FIXME: remove this relative path once consumers from glue are removed. +#include "../public/WebNavigationPolicy.h" +#include "../public/WebPoint.h" +#include "../public/WebSize.h" +#include "../public/WebString.h" +#include "../public/WebView.h" +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { +class ChromiumDataObject; +class Frame; +class HistoryItem; +class HitTestResult; +class KeyboardEvent; +class Page; +class PlatformKeyboardEvent; +class PopupContainer; +class Range; +class RenderTheme; +class Widget; +} + +class WebDevToolsAgentImpl; + +namespace WebKit { +class AutocompletePopupMenuClient; +class ContextMenuClientImpl; +class WebAccessibilityObject; +class WebFrameImpl; +class WebKeyboardEvent; +class WebMouseEvent; +class WebMouseWheelEvent; +class WebSettingsImpl; + +class WebViewImpl : public WebView, public RefCounted<WebViewImpl> { +public: + // WebWidget methods: + virtual void close(); + virtual WebSize size() { return m_size; } + virtual void resize(const WebSize&); + virtual void layout(); + virtual void paint(WebCanvas*, const WebRect&); + virtual bool handleInputEvent(const WebInputEvent&); + virtual void mouseCaptureLost(); + virtual void setFocus(bool enable); + virtual bool handleCompositionEvent(WebCompositionCommand command, + int cursorPosition, + int targetStart, + int targetEnd, + const WebString& text); + virtual bool queryCompositionStatus(bool* enabled, + WebRect* caretRect); + virtual void setTextDirection(WebTextDirection direction); + + // WebView methods: + virtual void initializeMainFrame(WebFrameClient*); + virtual WebSettings* settings(); + virtual WebString pageEncoding() const; + virtual void setPageEncoding(const WebString& encoding); + virtual bool isTransparent() const; + virtual void setIsTransparent(bool value); + virtual bool tabsToLinks() const; + virtual void setTabsToLinks(bool value); + virtual bool tabKeyCyclesThroughElements() const; + virtual void setTabKeyCyclesThroughElements(bool value); + virtual bool isActive() const; + virtual void setIsActive(bool value); + virtual bool dispatchBeforeUnloadEvent(); + virtual void dispatchUnloadEvent(); + virtual WebFrame* mainFrame(); + virtual WebFrame* findFrameByName( + const WebString& name, WebFrame* relativeToFrame); + virtual WebFrame* focusedFrame(); + virtual void setFocusedFrame(WebFrame* frame); + virtual void setInitialFocus(bool reverse); + virtual void clearFocusedNode(); + virtual void zoomIn(bool textOnly); + virtual void zoomOut(bool textOnly); + virtual void zoomDefault(); + virtual void performMediaPlayerAction( + const WebMediaPlayerAction& action, + const WebPoint& location); + virtual void copyImageAt(const WebPoint& point); + virtual void dragSourceEndedAt( + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperation operation); + virtual void dragSourceMovedTo( + const WebPoint& clientPoint, + const WebPoint& screenPoint); + virtual void dragSourceSystemDragEnded(); + virtual WebDragOperation dragTargetDragEnter( + const WebDragData& dragData, int identity, + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperationsMask operationsAllowed); + virtual WebDragOperation dragTargetDragOver( + const WebPoint& clientPoint, + const WebPoint& screenPoint, + WebDragOperationsMask operationsAllowed); + virtual void dragTargetDragLeave(); + virtual void dragTargetDrop( + const WebPoint& clientPoint, + const WebPoint& screenPoint); + virtual int dragIdentity(); + virtual bool setDropEffect(bool accept); + virtual void inspectElementAt(const WebPoint& point); + virtual WebString inspectorSettings() const; + virtual void setInspectorSettings(const WebString& settings); + virtual WebDevToolsAgent* devToolsAgent(); + virtual WebAccessibilityObject accessibilityObject(); + virtual void applyAutofillSuggestions( + const WebNode&, + const WebVector<WebString>& suggestions, + int defaultSuggestionIndex); + virtual void hideAutofillPopup(); + + // WebViewImpl + + void setIgnoreInputEvents(bool newValue); + WebDevToolsAgentImpl* devToolsAgentImpl(); + + const WebPoint& lastMouseDownPoint() const + { + return m_lastMouseDownPoint; + } + + WebCore::Frame* focusedWebCoreFrame(); + + // Returns the currently focused Node or null if no node has focus. + WebCore::Node* focusedWebCoreNode(); + + static WebViewImpl* fromPage(WebCore::Page*); + + WebViewClient* client() + { + return m_client; + } + + // Returns the page object associated with this view. This may be null when + // the page is shutting down, but will be valid at all other times. + WebCore::Page* page() const + { + return m_page.get(); + } + + WebCore::RenderTheme* theme() const; + + // Returns the main frame associated with this view. This may be null when + // the page is shutting down, but will be valid at all other times. + WebFrameImpl* mainFrameImpl(); + + // History related methods: + void setCurrentHistoryItem(WebCore::HistoryItem*); + WebCore::HistoryItem* previousHistoryItem(); + void observeNewNavigation(); + + // Event related methods: + void mouseMove(const WebMouseEvent&); + void mouseLeave(const WebMouseEvent&); + void mouseDown(const WebMouseEvent&); + void mouseUp(const WebMouseEvent&); + void mouseContextMenu(const WebMouseEvent&); + void mouseDoubleClick(const WebMouseEvent&); + void mouseWheel(const WebMouseWheelEvent&); + bool keyEvent(const WebKeyboardEvent&); + bool charEvent(const WebKeyboardEvent&); + + // Handles context menu events orignated via the the keyboard. These + // include the VK_APPS virtual key and the Shift+F10 combine. Code is + // based on the Webkit function bool WebView::handleContextMenuEvent(WPARAM + // wParam, LPARAM lParam) in webkit\webkit\win\WebView.cpp. The only + // significant change in this function is the code to convert from a + // Keyboard event to the Right Mouse button down event. + bool sendContextMenuEvent(const WebKeyboardEvent&); + + // Notifies the WebView that a load has been committed. isNewNavigation + // will be true if a new session history item should be created for that + // load. + void didCommitLoad(bool* isNewNavigation); + + bool contextMenuAllowed() const + { + return m_contextMenuAllowed; + } + + // Set the disposition for how this webview is to be initially shown. + void setInitialNavigationPolicy(WebNavigationPolicy policy) + { + m_initialNavigationPolicy = policy; + } + WebNavigationPolicy initialNavigationPolicy() const + { + return m_initialNavigationPolicy; + } + + // Determines whether a page should e.g. be opened in a background tab. + // Returns false if it has no opinion, in which case it doesn't set *policy. + static bool navigationPolicyFromMouseEvent( + unsigned short button, + bool ctrl, + bool shift, + bool alt, + bool meta, + WebNavigationPolicy*); + + // Start a system drag and drop operation. + void startDragging( + const WebPoint& eventPos, + const WebDragData& dragData, + WebDragOperationsMask dragSourceOperationMask); + + // Hides the autocomplete popup if it is showing. + void hideAutoCompletePopup(); + void autoCompletePopupDidHide(); + +#if ENABLE(NOTIFICATIONS) + // Returns the provider of desktop notifications. + NotificationPresenterImpl* notificationPresenterImpl(); +#endif + + // Tries to scroll a frame or any parent of a frame. Returns true if the view + // was scrolled. + bool propagateScroll(WebCore::ScrollDirection, WebCore::ScrollGranularity); + + // HACK: currentInputEvent() is for ChromeClientImpl::show(), until we can + // fix WebKit to pass enough information up into ChromeClient::show() so we + // can decide if the window.open event was caused by a middle-mouse click + static const WebInputEvent* currentInputEvent() + { + return m_currentInputEvent; + } + +private: + friend class WebView; // So WebView::Create can call our constructor + friend class WTF::RefCounted<WebViewImpl>; + + WebViewImpl(WebViewClient* client); + ~WebViewImpl(); + + void modifySelection(uint32 message, WebCore::Frame*, const WebCore::PlatformKeyboardEvent&); + + // Returns true if the event was actually processed. + bool keyEventDefault(const WebKeyboardEvent&); + + // Returns true if the autocomple has consumed the event. + bool autocompleteHandleKeyEvent(const WebKeyboardEvent&); + + // Repaints the autofill popup. Should be called when the suggestions have + // changed. Note that this should only be called when the autofill popup is + // showing. + void refreshAutofillPopup(); + + // Returns true if the view was scrolled. + bool scrollViewWithKeyboard(int keyCode, int modifiers); + + // Converts |pos| from window coordinates to contents coordinates and gets + // the HitTestResult for it. + WebCore::HitTestResult hitTestResultForWindowPos(const WebCore::IntPoint&); + + WebViewClient* m_client; + + BackForwardListClientImpl m_backForwardListClientImpl; + ChromeClientImpl m_chromeClientImpl; + ContextMenuClientImpl m_contextMenuClientImpl; + DragClientImpl m_dragClientImpl; + EditorClientImpl m_editorClientImpl; + InspectorClientImpl m_inspectorClientImpl; + + WebSize m_size; + + WebPoint m_lastMousePosition; + OwnPtr<WebCore::Page> m_page; + + // This flag is set when a new navigation is detected. It is used to satisfy + // the corresponding argument to WebFrameClient::didCommitProvisionalLoad. + bool m_observedNewNavigation; +#ifndef NDEBUG + // Used to assert that the new navigation we observed is the same navigation + // when we make use of m_observedNewNavigation. + const WebCore::DocumentLoader* m_newNavigationLoader; +#endif + + // An object that can be used to manipulate m_page->settings() without linking + // against WebCore. This is lazily allocated the first time GetWebSettings() + // is called. + OwnPtr<WebSettingsImpl> m_webSettings; + + // A copy of the web drop data object we received from the browser. + RefPtr<WebCore::ChromiumDataObject> m_currentDragData; + + // The point relative to the client area where the mouse was last pressed + // down. This is used by the drag client to determine what was under the + // mouse when the drag was initiated. We need to track this here in + // WebViewImpl since DragClient::startDrag does not pass the position the + // mouse was at when the drag was initiated, only the current point, which + // can be misleading as it is usually not over the element the user actually + // dragged by the time a drag is initiated. + WebPoint m_lastMouseDownPoint; + + // Keeps track of the current text zoom level. 0 means no zoom, positive + // values mean larger text, negative numbers mean smaller. + int m_zoomLevel; + + bool m_contextMenuAllowed; + + bool m_doingDragAndDrop; + + bool m_ignoreInputEvents; + + // Webkit expects keyPress events to be suppressed if the associated keyDown + // event was handled. Safari implements this behavior by peeking out the + // associated WM_CHAR event if the keydown was handled. We emulate + // this behavior by setting this flag if the keyDown was handled. + bool m_suppressNextKeypressEvent; + + // The policy for how this webview is to be initially shown. + WebNavigationPolicy m_initialNavigationPolicy; + + // Represents whether or not this object should process incoming IME events. + bool m_imeAcceptEvents; + + // True while dispatching system drag and drop events to drag/drop targets + // within this WebView. + bool m_dragTargetDispatch; + + // Valid when m_dragTargetDispatch is true; the identity of the drag data + // copied from the WebDropData object sent from the browser process. + int32 m_dragIdentity; + + // Valid when m_dragTargetDispatch is true. Used to override the default + // browser drop effect with the effects "none" or "copy". + enum DragTargetDropEffect { + DropEffectDefault = -1, + DropEffectNone, + DropEffectCopy + } m_dropEffect; + + // The available drag operations (copy, move link...) allowed by the source. + WebDragOperation m_operationsAllowed; + + // The current drag operation as negotiated by the source and destination. + // When not equal to DragOperationNone, the drag data can be dropped onto the + // current drop target in this WebView (the drop target can accept the drop). + WebDragOperation m_dragOperation; + + // The autocomplete popup. Kept around and reused every-time new suggestions + // should be shown. + RefPtr<WebCore::PopupContainer> m_autocompletePopup; + + // Whether the autocomplete popup is currently showing. + bool m_autocompletePopupShowing; + + // The autocomplete client. + OwnPtr<AutocompletePopupMenuClient> m_autocompletePopupClient; + + OwnPtr<WebDevToolsAgentImpl> m_devToolsAgent; + + // Whether the webview is rendering transparently. + bool m_isTransparent; + + // Whether the user can press tab to focus links. + bool m_tabsToLinks; + + // Inspector settings. + WebString m_inspectorSettings; + +#if ENABLE(NOTIFICATIONS) + // The provider of desktop notifications; + NotificationPresenterImpl m_notificationPresenter; +#endif + + static const WebInputEvent* m_currentInputEvent; +}; + +} // namespace WebKit + +#endif diff --git a/webkit/api/src/WebWorkerClientImpl.cpp b/webkit/api/src/WebWorkerClientImpl.cpp index 16d7134..5e99b57 100644 --- a/webkit/api/src/WebWorkerClientImpl.cpp +++ b/webkit/api/src/WebWorkerClientImpl.cpp @@ -47,21 +47,19 @@ #include "WorkerContextExecutionProxy.h" #include "WorkerMessagingProxy.h" #include <wtf/Threading.h> -#undef LOG +#include "FrameLoaderClientImpl.h" #include "PlatformMessagePortChannel.h" #include "WebFrameClient.h" +#include "WebFrameImpl.h" #include "WebKit.h" #include "WebKitClient.h" #include "WebMessagePortChannel.h" #include "WebString.h" #include "WebURL.h" +#include "WebViewImpl.h" #include "WebWorker.h" #include "WebWorkerImpl.h" -// FIXME: remove the includes below -#include "webkit/glue/webframeloaderclient_impl.h" -#include "webkit/glue/webframe_impl.h" -#include "webkit/glue/webview_impl.h" using namespace WebCore; @@ -93,7 +91,7 @@ WorkerContextProxy* WebWorkerClientImpl::createWorkerContextProxy(Worker* worker if (worker->scriptExecutionContext()->isDocument()) { Document* document = static_cast<Document*>( worker->scriptExecutionContext()); - WebFrameImpl* webFrame = WebFrameImpl::FromFrame(document->frame()); + WebFrameImpl* webFrame = WebFrameImpl::fromFrame(document->frame()); webWorker = webFrame->client()->createWorker(webFrame, proxy); } else { WorkerContextExecutionProxy* currentContext = diff --git a/webkit/api/src/WebWorkerImpl.cpp b/webkit/api/src/WebWorkerImpl.cpp index 8e6f6df..4028b71 100644 --- a/webkit/api/src/WebWorkerImpl.cpp +++ b/webkit/api/src/WebWorkerImpl.cpp @@ -48,14 +48,13 @@ #include "PlatformMessagePortChannel.h" #include "WebDataSourceImpl.h" #include "WebFrameClient.h" +#include "WebFrameImpl.h" #include "WebMessagePortChannel.h" #include "WebScreenInfo.h" #include "WebString.h" #include "WebURL.h" #include "WebView.h" #include "WebWorkerClient.h" -// FIXME: webframe should eventually move to api/src too. -#include "webkit/glue/webframe_impl.h" using namespace WebCore; |