diff options
Diffstat (limited to 'WebKit/chromium/src')
119 files changed, 13757 insertions, 652 deletions
diff --git a/WebKit/chromium/src/APUAgentDelegate.h b/WebKit/chromium/src/APUAgentDelegate.h new file mode 100644 index 0000000..70be702 --- /dev/null +++ b/WebKit/chromium/src/APUAgentDelegate.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 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 APUAgentDelegate_h +#define APUAgentDelegate_h + +#include "DevToolsRPC.h" + +namespace WebKit { + +#define APU_AGENT_DELEGATE_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, MEHTOD4, METHOD5) \ + /* Sends a json object to apu. */ \ + METHOD1(dispatchToApu, String /* data */) + +DEFINE_RPC_CLASS(ApuAgentDelegate, APU_AGENT_DELEGATE_STRUCT) + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/ApplicationCacheHost.cpp b/WebKit/chromium/src/ApplicationCacheHost.cpp index ef8f860..5fa4a66 100644 --- a/WebKit/chromium/src/ApplicationCacheHost.cpp +++ b/WebKit/chromium/src/ApplicationCacheHost.cpp @@ -33,14 +33,11 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "ApplicationCacheHostInternal.h" #include "DocumentLoader.h" #include "DOMApplicationCache.h" #include "Frame.h" #include "Settings.h" -#include "WebApplicationCacheHost.h" -#include "WebApplicationCacheHostClient.h" -#include "WebKit.h" -#include "WebKitClient.h" #include "WebURL.h" #include "WebURLError.h" #include "WebURLResponse.h" @@ -51,27 +48,6 @@ using namespace WebKit; namespace WebCore { -// ApplicationCacheHostInternal ----------------------------------------------- - -class ApplicationCacheHostInternal : public WebApplicationCacheHostClient { -public: - ApplicationCacheHostInternal(ApplicationCacheHost* host) - : m_innerHost(host) - { - m_outerHost.set(WebKit::webKitClient()->createApplicationCacheHost(this)); - } - - virtual void notifyEventListener(WebApplicationCacheHost::EventID eventID) - { - m_innerHost->notifyDOMApplicationCache( - static_cast<ApplicationCacheHost::EventID>(eventID)); - } - - ApplicationCacheHost* m_innerHost; - OwnPtr<WebApplicationCacheHost> m_outerHost; -}; - -// ApplicationCacheHost ------------------------------------------------------- // We provide a custom implementation of this class that calls out to the // embedding application instead of using WebCore's built in appcache system. // This file replaces webcore/appcache/ApplicationCacheHost.cpp in our build. @@ -79,6 +55,7 @@ public: ApplicationCacheHost::ApplicationCacheHost(DocumentLoader* documentLoader) : m_domApplicationCache(0) , m_documentLoader(documentLoader) + , m_defersEvents(true) { ASSERT(m_documentLoader); } @@ -220,15 +197,32 @@ void ApplicationCacheHost::setDOMApplicationCache(DOMApplicationCache* domApplic void ApplicationCacheHost::notifyDOMApplicationCache(EventID id) { + if (m_defersEvents) { + m_deferredEvents.append(id); + return; + } if (m_domApplicationCache) { ExceptionCode ec = 0; - m_domApplicationCache->dispatchEvent( - Event::create(DOMApplicationCache::toEventType(id), false, false), - ec); + m_domApplicationCache->dispatchEvent(Event::create(DOMApplicationCache::toEventType(id), false, false), ec); ASSERT(!ec); } } +void ApplicationCacheHost::stopDeferringEvents() +{ + RefPtr<DocumentLoader> protect(documentLoader()); + for (unsigned i = 0; i < m_deferredEvents.size(); ++i) { + EventID id = m_deferredEvents[i]; + if (m_domApplicationCache) { + ExceptionCode ec = 0; + m_domApplicationCache->dispatchEvent(Event::create(DOMApplicationCache::toEventType(id), false, false), ec); + ASSERT(!ec); + } + } + m_deferredEvents.clear(); + m_defersEvents = false; +} + ApplicationCacheHost::Status ApplicationCacheHost::status() const { return m_internal ? static_cast<Status>(m_internal->m_outerHost->status()) : UNCACHED; diff --git a/WebKit/chromium/src/ApplicationCacheHostInternal.h b/WebKit/chromium/src/ApplicationCacheHostInternal.h new file mode 100644 index 0000000..3e52c1b --- /dev/null +++ b/WebKit/chromium/src/ApplicationCacheHostInternal.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 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 "ApplicationCacheHost.h" + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + +#include "WebApplicationCacheHostClient.h" +#include "WebKit.h" +#include "WebKitClient.h" + +namespace WebCore { + +class ApplicationCacheHostInternal : public WebKit::WebApplicationCacheHostClient { +public: + ApplicationCacheHostInternal(ApplicationCacheHost* host) + : m_innerHost(host) + { + m_outerHost.set(WebKit::webKitClient()->createApplicationCacheHost(this)); + } + + virtual void notifyEventListener(WebKit::WebApplicationCacheHost::EventID eventID) + { + m_innerHost->notifyDOMApplicationCache(static_cast<ApplicationCacheHost::EventID>(eventID)); + } + + static WebKit::WebApplicationCacheHost* toWebApplicationCacheHost(ApplicationCacheHost* innerHost) + { + if (innerHost && innerHost->m_internal.get()) + return innerHost->m_internal->m_outerHost.get(); + return 0; + } + +private: + friend class ApplicationCacheHost; + ApplicationCacheHost* m_innerHost; + OwnPtr<WebKit::WebApplicationCacheHost> m_outerHost; +}; + +} + +#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebKit/chromium/src/AssertMatchingEnums.cpp b/WebKit/chromium/src/AssertMatchingEnums.cpp index 35e2057..1d2948f 100644 --- a/WebKit/chromium/src/AssertMatchingEnums.cpp +++ b/WebKit/chromium/src/AssertMatchingEnums.cpp @@ -116,6 +116,8 @@ COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleWebArea, WebAreaRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleHeading, HeadingRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleListBox, ListBoxRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleListBoxOption, ListBoxOptionRole); +COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleMenuListOption, MenuListOptionRole); +COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleMenuListPopup, MenuListPopupRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleTableHeaderContainer, TableHeaderContainerRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleDefinitionListTerm, DefinitionListTermRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleDefinitionListDefinition, DefinitionListDefinitionRole); @@ -126,8 +128,11 @@ COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleTab, TabRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleTabList, TabListRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleTabPanel, TabPanelRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleTreeRole, TreeRole); +COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleTreeGrid, TreeGridRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleTreeItemRole, TreeItemRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleDirectory, DirectoryRole); +COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleEditableText, EditableTextRole); +COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleListItem, ListItemRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleLandmarkApplication, LandmarkApplicationRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleLandmarkBanner, LandmarkBannerRole); COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleLandmarkComplementary, LandmarkComplementaryRole); @@ -245,6 +250,20 @@ COMPILE_ASSERT_MATCHING_ENUM(WebInputElement::Month, HTMLInputElement::MONTH); COMPILE_ASSERT_MATCHING_ENUM(WebInputElement::Time, HTMLInputElement::TIME); COMPILE_ASSERT_MATCHING_ENUM(WebInputElement::Week, HTMLInputElement::WEEK); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::ElementNode, Node::ELEMENT_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::AttributeNode, Node::ATTRIBUTE_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::TextNode, Node::TEXT_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::CDataSectionNode, Node::CDATA_SECTION_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::EntityReferenceNode, Node::ENTITY_REFERENCE_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::EntityNode, Node::ENTITY_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::ProcessingInstructionsNode, Node::PROCESSING_INSTRUCTION_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::CommentNode, Node::COMMENT_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::DocumentNode, Node::DOCUMENT_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::DocumentTypeNode, Node::DOCUMENT_TYPE_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::DocumentFragmentNode, Node::DOCUMENT_FRAGMENT_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::NotationNode, Node::NOTATION_NODE); +COMPILE_ASSERT_MATCHING_ENUM(WebNode::XPathNamespaceNode, Node::XPATH_NAMESPACE_NODE); + #if ENABLE(VIDEO) COMPILE_ASSERT_MATCHING_ENUM(WebMediaPlayer::Empty, MediaPlayer::Empty); COMPILE_ASSERT_MATCHING_ENUM(WebMediaPlayer::Idle, MediaPlayer::Idle); diff --git a/WebKit/chromium/src/AutoFillPopupMenuClient.cpp b/WebKit/chromium/src/AutoFillPopupMenuClient.cpp new file mode 100644 index 0000000..8e6cab4 --- /dev/null +++ b/WebKit/chromium/src/AutoFillPopupMenuClient.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 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 "AutoFillPopupMenuClient.h" + +#include "HTMLInputElement.h" +#include "WebString.h" +#include "WebVector.h" + +using namespace WebCore; + +namespace WebKit { + +unsigned AutoFillPopupMenuClient::getSuggestionsCount() const +{ + return m_names.size(); +} + +WebString AutoFillPopupMenuClient::getSuggestion(unsigned listIndex) const +{ + // FIXME: Modify the PopupMenu to add the label in gray right-justified. + ASSERT(listIndex >= 0 && listIndex < m_names.size()); + return m_names[listIndex] + String(" (") + m_labels[listIndex] + String(")"); +} + +void AutoFillPopupMenuClient::removeSuggestionAtIndex(unsigned listIndex) +{ + // FIXME: Do we want to remove AutoFill suggestions? + ASSERT(listIndex >= 0 && listIndex < m_names.size()); + m_names.remove(listIndex); + m_labels.remove(listIndex); +} + +void AutoFillPopupMenuClient::initialize( + HTMLInputElement* textField, + const WebVector<WebString>& names, + const WebVector<WebString>& labels, + int defaultSuggestionIndex) +{ + ASSERT(names.size() == labels.size()); + ASSERT(defaultSuggestionIndex < static_cast<int>(names.size())); + + // The suggestions must be set before initializing the + // SuggestionsPopupMenuClient. + setSuggestions(names, labels); + + SuggestionsPopupMenuClient::initialize(textField, defaultSuggestionIndex); +} + +void AutoFillPopupMenuClient::setSuggestions(const WebVector<WebString>& names, + const WebVector<WebString>& labels) +{ + ASSERT(names.size() == labels.size()); + + m_names.clear(); + m_labels.clear(); + for (size_t i = 0; i < names.size(); ++i) { + m_names.append(names[i]); + m_labels.append(labels[i]); + } + + // Try to preserve selection if possible. + if (getSelectedIndex() >= static_cast<int>(names.size())) + setSelectedIndex(-1); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/AutoFillPopupMenuClient.h b/WebKit/chromium/src/AutoFillPopupMenuClient.h new file mode 100644 index 0000000..1912fa3 --- /dev/null +++ b/WebKit/chromium/src/AutoFillPopupMenuClient.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 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 AutoFillPopupMenuClient_h +#define AutoFillPopupMenuClient_h + +#include "SuggestionsPopupMenuClient.h" + +namespace WebCore { +class HTMLInputElement; +} + +namespace WebKit { +class WebString; +template <typename T> class WebVector; + +// The AutoFill suggestions popup menu client, used to display name suggestions +// with right-justified labels. +class AutoFillPopupMenuClient : public SuggestionsPopupMenuClient { +public: + // SuggestionsPopupMenuClient implementation: + virtual unsigned getSuggestionsCount() const; + virtual WebString getSuggestion(unsigned listIndex) const; + virtual void removeSuggestionAtIndex(unsigned listIndex); + + void initialize(WebCore::HTMLInputElement*, + const WebVector<WebString>& names, + const WebVector<WebString>& labels, + int defaultSuggestionIndex); + + void setSuggestions(const WebVector<WebString>& names, + const WebVector<WebString>& labels); + +private: + Vector<WebCore::String> m_names; + Vector<WebCore::String> m_labels; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/AutocompletePopupMenuClient.cpp b/WebKit/chromium/src/AutocompletePopupMenuClient.cpp index 62d4dff..9620ffc 100644 --- a/WebKit/chromium/src/AutocompletePopupMenuClient.cpp +++ b/WebKit/chromium/src/AutocompletePopupMenuClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 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 @@ -31,27 +31,29 @@ #include "config.h" #include "AutocompletePopupMenuClient.h" -#include "CSSStyleSelector.h" -#include "CSSValueKeywords.h" -#include "FrameView.h" #include "HTMLInputElement.h" -#include "RenderTheme.h" +#include "WebString.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) +unsigned AutocompletePopupMenuClient::getSuggestionsCount() const { + return m_suggestions.size(); } -AutocompletePopupMenuClient::~AutocompletePopupMenuClient() +WebString AutocompletePopupMenuClient::getSuggestion(unsigned listIndex) const { + ASSERT(listIndex >= 0 && listIndex < m_suggestions.size()); + return m_suggestions[listIndex]; +} + +void AutocompletePopupMenuClient::removeSuggestionAtIndex(unsigned listIndex) +{ + ASSERT(listIndex >= 0 && listIndex < m_suggestions.size()); + m_suggestions.remove(listIndex); } void AutocompletePopupMenuClient::initialize( @@ -60,90 +62,12 @@ void AutocompletePopupMenuClient::initialize( 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 0. - 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 0. - 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(); -} + // The suggestions must be set before initializing the + // SuggestionsPopupMenuClient. + setSuggestions(suggestions); -PassRefPtr<Scrollbar> AutocompletePopupMenuClient::createScrollbar( - ScrollbarClient* client, - ScrollbarOrientation orientation, - ScrollbarControlSize size) -{ - return Scrollbar::createNativeScrollbar(client, orientation, size); + SuggestionsPopupMenuClient::initialize(textField, defaultSuggestionIndex); } void AutocompletePopupMenuClient::setSuggestions(const WebVector<WebString>& suggestions) @@ -151,28 +75,10 @@ void AutocompletePopupMenuClient::setSuggestions(const WebVector<WebString>& sug 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 a 0 style in a TextField if the - // node is detached, 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; + // Try to preserve selection if possible. + if (getSelectedIndex() >= static_cast<int>(suggestions.size())) + setSelectedIndex(-1); } } // namespace WebKit diff --git a/WebKit/chromium/src/AutocompletePopupMenuClient.h b/WebKit/chromium/src/AutocompletePopupMenuClient.h index ad24e54..16a3771 100644 --- a/WebKit/chromium/src/AutocompletePopupMenuClient.h +++ b/WebKit/chromium/src/AutocompletePopupMenuClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 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 @@ -28,69 +28,38 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "PopupMenuClient.h" +#ifndef AutocompletePopupMenuClient_h +#define AutocompletePopupMenuClient_h + +#include "SuggestionsPopupMenuClient.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 { +// The Autocomplete suggestions popup menu client, used to display a list of +// autocomplete suggestions. +class AutocompletePopupMenuClient : public SuggestionsPopupMenuClient { public: - AutocompletePopupMenuClient(WebViewImpl* webview); - ~AutocompletePopupMenuClient(); + // SuggestionsPopupMenuClient implementation: + virtual unsigned getSuggestionsCount() const; + virtual WebString getSuggestion(unsigned listIndex) const; + virtual void removeSuggestionAtIndex(unsigned listIndex); 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 + +#endif diff --git a/WebKit/chromium/src/BackForwardListClientImpl.cpp b/WebKit/chromium/src/BackForwardListClientImpl.cpp index 8feae32..f5b04ab 100644 --- a/WebKit/chromium/src/BackForwardListClientImpl.cpp +++ b/WebKit/chromium/src/BackForwardListClientImpl.cpp @@ -90,7 +90,7 @@ HistoryItem* BackForwardListClientImpl::currentItem() HistoryItem* BackForwardListClientImpl::itemAtIndex(int index) { - if (!m_webView->client()) + if (!m_webView->client() || index > forwardListCount() || -index > backListCount()) return 0; // Since we don't keep the entire back/forward list, we have no way to diff --git a/WebKit/chromium/src/BoundObject.cpp b/WebKit/chromium/src/BoundObject.cpp new file mode 100644 index 0000000..90096c2 --- /dev/null +++ b/WebKit/chromium/src/BoundObject.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 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 "BoundObject.h" + +#include "V8Proxy.h" + +namespace WebKit { + +BoundObject::BoundObject(v8::Handle<v8::Context> context, void* v8This, const char* objectName) + : m_objectName(objectName) + , m_context(context) + , m_v8This(v8This) +{ + v8::Context::Scope contextScope(context); + v8::Local<v8::FunctionTemplate> localTemplate = v8::FunctionTemplate::New(WebCore::V8Proxy::checkNewLegal); + m_hostTemplate = v8::Persistent<v8::FunctionTemplate>::New(localTemplate); + m_hostTemplate->SetClassName(v8::String::New(objectName)); +} + +BoundObject::~BoundObject() +{ + m_hostTemplate.Dispose(); +} + +void BoundObject::addProtoFunction(const char* name, v8::InvocationCallback callback) +{ + v8::Context::Scope contextScope(m_context); + v8::Local<v8::Signature> signature = v8::Signature::New(m_hostTemplate); + v8::Local<v8::ObjectTemplate> proto = m_hostTemplate->PrototypeTemplate(); + v8::Local<v8::External> v8This = v8::External::New(m_v8This); + proto->Set( + v8::String::New(name), + v8::FunctionTemplate::New( + callback, + v8This, + signature), + static_cast<v8::PropertyAttribute>(v8::DontDelete)); +} + +void BoundObject::build() +{ + v8::Context::Scope contextScope(m_context); + v8::Local<v8::Function> constructor = m_hostTemplate->GetFunction(); + v8::Local<v8::Object> boundObject = WebCore::SafeAllocation::newInstance(constructor); + + v8::Handle<v8::Object> global = m_context->Global(); + global->Set(v8::String::New(m_objectName), boundObject); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/BoundObject.h b/WebKit/chromium/src/BoundObject.h new file mode 100644 index 0000000..769e83f --- /dev/null +++ b/WebKit/chromium/src/BoundObject.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 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 BoundObject_h +#define BoundObject_h + +#include <v8.h> +#include <wtf/Noncopyable.h> + +namespace WebKit { + +// BoundObject is a helper class that lets you map JavaScript method calls +// directly to C++ method calls. It should be destroyed once JS object is +// built. +class BoundObject : public Noncopyable { +public: + BoundObject(v8::Handle<v8::Context> context, void* v8This, const char* objectName); + virtual ~BoundObject(); + + void addProtoFunction(const char* name, v8::InvocationCallback callback); + void build(); + +private: + v8::HandleScope m_handleScope; + const char* m_objectName; + v8::Handle<v8::Context> m_context; + v8::Persistent<v8::FunctionTemplate> m_hostTemplate; + void* m_v8This; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/ChromeClientImpl.cpp b/WebKit/chromium/src/ChromeClientImpl.cpp index 4e20124..ce2f00c 100644 --- a/WebKit/chromium/src/ChromeClientImpl.cpp +++ b/WebKit/chromium/src/ChromeClientImpl.cpp @@ -560,22 +560,20 @@ void ChromeClientImpl::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> fileCh if (!client) return; - bool multipleFiles = fileChooser->allowsMultipleFiles(); - - WebString suggestion; - if (fileChooser->filenames().size() > 0) - suggestion = fileChooser->filenames()[0]; - + WebFileChooserParams params; + params.multiSelect = fileChooser->allowsMultipleFiles(); + params.acceptTypes = fileChooser->acceptTypes(); + params.selectedFiles = fileChooser->filenames(); + if (params.selectedFiles.size() > 0) + params.initialValue = params.selectedFiles[0]; WebFileChooserCompletionImpl* chooserCompletion = new WebFileChooserCompletionImpl(fileChooser); - bool ok = client->runFileChooser(multipleFiles, - WebString(), - suggestion, - chooserCompletion); - if (!ok) { - // Choosing failed, so do callback with an empty list. - chooserCompletion->didChooseFile(WebVector<WebString>()); - } + + if (client->runFileChooser(params, chooserCompletion)) + return; + + // Choosing failed, so do callback with an empty list. + chooserCompletion->didChooseFile(WebVector<WebString>()); } void ChromeClientImpl::popupOpened(PopupContainer* popupContainer, @@ -661,6 +659,14 @@ void ChromeClientImpl::getPopupMenuInfo(PopupContainer* popupContainer, info->items.swap(outputItems); } +void ChromeClientImpl::didChangeAccessibilityObjectState(AccessibilityObject* obj) +{ + // Alert assistive technology about the accessibility object state change + if (obj) + m_webView->client()->didChangeAccessibilityObjectState(WebAccessibilityObject(obj)); +} + + #if ENABLE(NOTIFICATIONS) NotificationPresenter* ChromeClientImpl::notificationPresenter() const { diff --git a/WebKit/chromium/src/ChromeClientImpl.h b/WebKit/chromium/src/ChromeClientImpl.h index 5a1e9cc..9e8c2e3 100644 --- a/WebKit/chromium/src/ChromeClientImpl.h +++ b/WebKit/chromium/src/ChromeClientImpl.h @@ -34,6 +34,7 @@ #include "ChromeClientChromium.h" namespace WebCore { +class AccessibilityObject; class HTMLParserQuirks; class PopupContainer; class SecurityOrigin; @@ -132,6 +133,7 @@ public: const WebCore::IntRect& bounds, bool activatable, bool handleExternally); + virtual void didChangeAccessibilityObjectState(WebCore::AccessibilityObject*); // ChromeClientImpl: void setCursor(const WebCursorInfo& cursor); diff --git a/WebKit/chromium/src/ChromiumBridge.cpp b/WebKit/chromium/src/ChromiumBridge.cpp index 93d92ba..0fd0825 100644 --- a/WebKit/chromium/src/ChromiumBridge.cpp +++ b/WebKit/chromium/src/ChromiumBridge.cpp @@ -33,6 +33,7 @@ #include <googleurl/src/url_util.h> +#include "Chrome.h" #include "ChromeClientImpl.h" #include "WebClipboard.h" #include "WebCookie.h" @@ -54,13 +55,13 @@ #include "WebViewImpl.h" #include "WebWorkerClientImpl.h" -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) #include "WebRect.h" #include "WebSandboxSupport.h" #include "WebThemeEngine.h" #endif -#if PLATFORM(LINUX) +#if OS(LINUX) #include "WebSandboxSupport.h" #include "WebFontInfo.h" #endif @@ -212,6 +213,12 @@ void ChromiumBridge::deleteCookie(const KURL& url, const String& cookieName) webKitClient()->deleteCookie(url, cookieName); } +bool ChromiumBridge::cookiesEnabled(const KURL& url, + const KURL& firstPartyForCookies) +{ + return webKitClient()->cookiesEnabled(url, firstPartyForCookies); +} + // DNS ------------------------------------------------------------------------ void ChromiumBridge::prefetchDNS(const String& hostname) @@ -278,7 +285,7 @@ KURL ChromiumBridge::filePathToURL(const String& path) // Font ----------------------------------------------------------------------- -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) bool ChromiumBridge::ensureFontLoaded(HFONT font) { WebSandboxSupport* ss = webKitClient()->sandboxSupport(); @@ -289,7 +296,7 @@ bool ChromiumBridge::ensureFontLoaded(HFONT font) } #endif -#if PLATFORM(LINUX) +#if OS(LINUX) String ChromiumBridge::getFontFamilyForCharacters(const UChar* characters, size_t numCharacters) { if (webKitClient()->sandboxSupport()) @@ -473,7 +480,7 @@ double ChromiumBridge::currentTime() // Theming -------------------------------------------------------------------- -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) void ChromiumBridge::paintButton( GraphicsContext* gc, int part, int state, int classicState, diff --git a/WebKit/chromium/src/ContextMenuClientImpl.cpp b/WebKit/chromium/src/ContextMenuClientImpl.cpp index f23919e..8472082 100644 --- a/WebKit/chromium/src/ContextMenuClientImpl.cpp +++ b/WebKit/chromium/src/ContextMenuClientImpl.cpp @@ -31,6 +31,8 @@ #include "config.h" #include "ContextMenuClientImpl.h" +#include "CSSPropertyNames.h" +#include "CSSStyleDeclaration.h" #include "ContextMenu.h" #include "Document.h" #include "DocumentLoader.h" @@ -79,17 +81,17 @@ static WebURL urlFromFrame(Frame* frame) return WebURL(); } -// Helper function to determine whether text is a single word or a sentence. +// Helper function to determine whether text is a single word. static bool isASingleWord(const String& text) { - TextBreakIterator* it = characterBreakIterator(text.characters(), text.length()); - return it && textBreakNext(it) == TextBreakDone; + TextBreakIterator* it = wordBreakIterator(text.characters(), text.length()); + return it && textBreakNext(it) == static_cast<int>(text.length()); } // Helper function to get misspelled word on which context menu // is to be evolked. This function also sets the word on which context menu // has been evoked to be the selected word, as required. This function changes -// the selection only when there were no selected characters. +// the selection only when there were no selected characters on OS X. static String selectMisspelledWord(const ContextMenu* defaultMenu, Frame* selectedFrame) { // First select from selectedText to check for multiple word selection. @@ -110,27 +112,21 @@ static String selectMisspelledWord(const ContextMenu* defaultMenu, Frame* select VisiblePosition pos(innerNode->renderer()->positionForPoint( hitTestResult.localPoint())); - VisibleSelection selection; - if (pos.isNotNull()) { - selection = VisibleSelection(pos); - selection.expandUsingGranularity(WordGranularity); - } - - if (selection.isRange()) - selectedFrame->setSelectionGranularity(WordGranularity); - - if (selectedFrame->shouldChangeSelection(selection)) - selectedFrame->selection()->setSelection(selection); + if (pos.isNull()) + return misspelledWord; // It is empty. + WebFrameImpl::selectWordAroundPosition(selectedFrame, pos); misspelledWord = selectedFrame->selectedText().stripWhiteSpace(); +#if OS(DARWIN) // If misspelled word is still empty, then that portion should not be // selected. Set the selection to that position only, and do not expand. - if (misspelledWord.isEmpty()) { - selection = VisibleSelection(pos); - selectedFrame->selection()->setSelection(selection); - } - + if (misspelledWord.isEmpty()) + selectedFrame->selection()->setSelection(VisibleSelection(pos)); +#else + // On non-Mac, right-click should not make a range selection in any case. + selectedFrame->selection()->setSelection(VisibleSelection(pos)); +#endif return misspelledWord; } @@ -207,6 +203,22 @@ PlatformMenuDescription ContextMenuClientImpl::getCustomMenuFromDefaultItems( } } +#if OS(DARWIN) + // Writing direction context menu. + data.writingDirectionDefault = WebContextMenuData::CheckableMenuItemDisabled; + data.writingDirectionLeftToRight = WebContextMenuData::CheckableMenuItemEnabled; + data.writingDirectionRightToLeft = WebContextMenuData::CheckableMenuItemEnabled; + + ExceptionCode ec = 0; + RefPtr<CSSStyleDeclaration> style = selectedFrame->document()->createCSSStyleDeclaration(); + style->setProperty(CSSPropertyDirection, "ltr", false, ec); + if (selectedFrame->editor()->selectionHasStyle(style.get()) != FalseTriState) + data.writingDirectionLeftToRight |= WebContextMenuData::CheckableMenuItemChecked; + style->setProperty(CSSPropertyDirection, "rtl", false, ec); + if (selectedFrame->editor()->selectionHasStyle(style.get()) != FalseTriState) + data.writingDirectionRightToLeft |= WebContextMenuData::CheckableMenuItemChecked; +#endif // OS(DARWIN) + // Now retrieve the security info. DocumentLoader* dl = selectedFrame->loader()->documentLoader(); WebDataSource* ds = WebDataSourceImpl::fromDocumentLoader(dl); diff --git a/WebKit/chromium/src/DOMUtilitiesPrivate.cpp b/WebKit/chromium/src/DOMUtilitiesPrivate.cpp index ffdd85d..4081db6 100644 --- a/WebKit/chromium/src/DOMUtilitiesPrivate.cpp +++ b/WebKit/chromium/src/DOMUtilitiesPrivate.cpp @@ -84,4 +84,53 @@ String nameOfInputElement(HTMLInputElement* element) return WebInputElement(element).nameForAutofill(); } +bool elementHasLegalLinkAttribute(const Element* element, + const QualifiedName& attrName) +{ + if (attrName == HTMLNames::srcAttr) { + // Check src attribute. + if (element->hasTagName(HTMLNames::imgTag) + || element->hasTagName(HTMLNames::scriptTag) + || element->hasTagName(HTMLNames::iframeTag) + || element->hasTagName(HTMLNames::frameTag)) + return true; + if (element->hasTagName(HTMLNames::inputTag)) { + const HTMLInputElement* input = + static_cast<const HTMLInputElement*>(element); + if (input->inputType() == HTMLInputElement::IMAGE) + return true; + } + } else if (attrName == HTMLNames::hrefAttr) { + // Check href attribute. + if (element->hasTagName(HTMLNames::linkTag) + || element->hasTagName(HTMLNames::aTag) + || element->hasTagName(HTMLNames::areaTag)) + return true; + } else if (attrName == HTMLNames::actionAttr) { + if (element->hasTagName(HTMLNames::formTag)) + return true; + } else if (attrName == HTMLNames::backgroundAttr) { + if (element->hasTagName(HTMLNames::bodyTag) + || element->hasTagName(HTMLNames::tableTag) + || element->hasTagName(HTMLNames::trTag) + || element->hasTagName(HTMLNames::tdTag)) + return true; + } else if (attrName == HTMLNames::citeAttr) { + if (element->hasTagName(HTMLNames::blockquoteTag) + || element->hasTagName(HTMLNames::qTag) + || element->hasTagName(HTMLNames::delTag) + || element->hasTagName(HTMLNames::insTag)) + return true; + } else if (attrName == HTMLNames::classidAttr + || attrName == HTMLNames::dataAttr) { + if (element->hasTagName(HTMLNames::objectTag)) + return true; + } else if (attrName == HTMLNames::codebaseAttr) { + if (element->hasTagName(HTMLNames::objectTag) + || element->hasTagName(HTMLNames::appletTag)) + return true; + } + return false; +} + } // namespace WebKit diff --git a/WebKit/chromium/src/DOMUtilitiesPrivate.h b/WebKit/chromium/src/DOMUtilitiesPrivate.h index c02bf84..253ab3f 100644 --- a/WebKit/chromium/src/DOMUtilitiesPrivate.h +++ b/WebKit/chromium/src/DOMUtilitiesPrivate.h @@ -32,11 +32,13 @@ #define DOMUtilitiesPrivate_h namespace WebCore { +class Element; class HTMLInputElement; class HTMLLinkElement; class HTMLMetaElement; class HTMLOptionElement; class Node; +class QualifiedName; class String; } @@ -53,6 +55,16 @@ WebCore::HTMLOptionElement* toHTMLOptionElement(WebCore::Node*); // FIXME: Deprecate. Use WebInputElement::nameForAutofill instead. WebCore::String nameOfInputElement(WebCore::HTMLInputElement*); +// For img, script, iframe, frame element, when attribute name is src, +// for link, a, area element, when attribute name is href, +// for form element, when attribute name is action, +// for input, type=image, when attribute name is src, +// for body, table, tr, td, when attribute name is background, +// for blockquote, q, del, ins, when attribute name is cite, +// we can consider the attribute value has legal link. +bool elementHasLegalLinkAttribute(const WebCore::Element* element, + const WebCore::QualifiedName& attrName); + } // namespace WebKit #endif diff --git a/WebKit/chromium/src/DebuggerAgent.h b/WebKit/chromium/src/DebuggerAgent.h new file mode 100644 index 0000000..17cde11 --- /dev/null +++ b/WebKit/chromium/src/DebuggerAgent.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 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 DebuggerAgent_h +#define DebuggerAgent_h + +#include "DevToolsRPC.h" + +namespace WebKit { + +#define DEBUGGER_AGENT_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4, METHOD5) \ + /* Requests global context id of the inspected tab. */ \ + METHOD0(getContextId) \ + \ + /* Request v8 to process all debug commands in the queue. */ \ + METHOD0(processDebugCommands) + +DEFINE_RPC_CLASS(DebuggerAgent, DEBUGGER_AGENT_STRUCT) + +#define DEBUGGER_AGENT_DELEGATE_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4, METHOD5) \ + METHOD1(debuggerOutput, String /* output text */) \ + \ + /* Pushes debugger context id into the client. */ \ + METHOD1(setContextId, int /* context id */) + +DEFINE_RPC_CLASS(DebuggerAgentDelegate, DEBUGGER_AGENT_DELEGATE_STRUCT) + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/DebuggerAgentImpl.cpp b/WebKit/chromium/src/DebuggerAgentImpl.cpp new file mode 100644 index 0000000..d592710 --- /dev/null +++ b/WebKit/chromium/src/DebuggerAgentImpl.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2010 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 "DebuggerAgentImpl.h" + +#include "DebuggerAgentManager.h" +#include "Document.h" +#include "Frame.h" +#include "Page.h" +#include "V8Binding.h" +#include "V8DOMWindow.h" +#include "V8Index.h" +#include "V8Proxy.h" +#include "WebDevToolsAgentImpl.h" +#include "WebViewImpl.h" +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +using WebCore::DOMWindow; +using WebCore::Document; +using WebCore::Frame; +using WebCore::Page; +using WebCore::String; +using WebCore::V8ClassIndex; +using WebCore::V8DOMWindow; +using WebCore::V8DOMWrapper; +using WebCore::V8Proxy; + +namespace WebKit { + +DebuggerAgentImpl::DebuggerAgentImpl( + WebViewImpl* webViewImpl, + DebuggerAgentDelegate* delegate, + WebDevToolsAgentImpl* webdevtoolsAgent) + : m_webViewImpl(webViewImpl) + , m_delegate(delegate) + , m_webdevtoolsAgent(webdevtoolsAgent) + , m_autoContinueOnException(false) +{ + DebuggerAgentManager::debugAttach(this); +} + +DebuggerAgentImpl::~DebuggerAgentImpl() +{ + DebuggerAgentManager::debugDetach(this); +} + +void DebuggerAgentImpl::getContextId() +{ + m_delegate->setContextId(m_webdevtoolsAgent->hostId()); +} + +void DebuggerAgentImpl::processDebugCommands() +{ + DebuggerAgentManager::UtilityContextScope utilityScope; + v8::Debug::ProcessDebugMessages(); +} + +void DebuggerAgentImpl::debuggerOutput(const String& command) +{ + m_delegate->debuggerOutput(command); + m_webdevtoolsAgent->forceRepaint(); +} + +// static +void DebuggerAgentImpl::createUtilityContext(Frame* frame, v8::Persistent<v8::Context>* context) +{ + v8::HandleScope scope; + bool canExecuteScripts = frame->script()->canExecuteScripts(); + + // Set up the DOM window as the prototype of the new global object. + v8::Handle<v8::Context> windowContext = V8Proxy::context(frame); + v8::Handle<v8::Object> windowGlobal; + v8::Handle<v8::Object> windowWrapper; + if (canExecuteScripts) { + // FIXME: This check prevents renderer from crashing, while providing limited capabilities for + // DOM inspection, Resources tracking, no scripts support, some timeline profiling. Console will + // result in exceptions for each evaluation. There is still some work that needs to be done in + // order to polish the script-less experience. + windowGlobal = windowContext->Global(); + windowWrapper = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), windowGlobal); + ASSERT(V8DOMWindow::toNative(windowWrapper) == frame->domWindow()); + } + + v8::Handle<v8::ObjectTemplate> globalTemplate = v8::ObjectTemplate::New(); + + // TODO(yurys): provide a function in v8 bindings that would make the + // utility context more like main world context of the inspected frame, + // otherwise we need to manually make it satisfy various invariants + // that V8Proxy::getEntered and some other V8Proxy methods expect to find + // on v8 contexts on the contexts stack. + // See V8Proxy::createNewContext. + // + // Install a security handler with V8. + globalTemplate->SetAccessCheckCallbacks( + V8DOMWindow::namedSecurityCheck, + V8DOMWindow::indexedSecurityCheck, + v8::Integer::New(V8ClassIndex::DOMWINDOW)); + // We set number of internal fields to match that in V8DOMWindow wrapper. + // See http://crbug.com/28961 + globalTemplate->SetInternalFieldCount(V8DOMWindow::internalFieldCount); + + *context = v8::Context::New(0 /* no extensions */, globalTemplate, v8::Handle<v8::Object>()); + v8::Context::Scope contextScope(*context); + v8::Handle<v8::Object> global = (*context)->Global(); + + v8::Handle<v8::String> implicitProtoString = v8::String::New("__proto__"); + if (canExecuteScripts) + global->Set(implicitProtoString, windowWrapper); + + // Give the code running in the new context a way to get access to the + // original context. + if (canExecuteScripts) + global->Set(v8::String::New("contentWindow"), windowGlobal); +} + +String DebuggerAgentImpl::executeUtilityFunction( + v8::Handle<v8::Context> context, + int callId, + const char* object, + const String &functionName, + const String& jsonArgs, + bool async, + String* exception) +{ + v8::HandleScope scope; + ASSERT(!context.IsEmpty()); + if (context.IsEmpty()) { + *exception = "No window context."; + return ""; + } + v8::Context::Scope contextScope(context); + + DebuggerAgentManager::UtilityContextScope utilityScope; + + v8::Handle<v8::Object> dispatchObject = v8::Handle<v8::Object>::Cast( + context->Global()->Get(v8::String::New(object))); + + v8::Handle<v8::Value> dispatchFunction = dispatchObject->Get(v8::String::New("dispatch")); + ASSERT(dispatchFunction->IsFunction()); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(dispatchFunction); + + v8::Handle<v8::String> functionNameWrapper = v8::Handle<v8::String>( + v8::String::New(functionName.utf8().data())); + v8::Handle<v8::String> jsonArgsWrapper = v8::Handle<v8::String>( + v8::String::New(jsonArgs.utf8().data())); + v8::Handle<v8::Number> callIdWrapper = v8::Handle<v8::Number>( + v8::Number::New(async ? callId : 0)); + + v8::Handle<v8::Value> args[] = { + functionNameWrapper, + jsonArgsWrapper, + callIdWrapper + }; + + v8::TryCatch tryCatch; + v8::Handle<v8::Value> resObj = function->Call(context->Global(), 3, args); + if (tryCatch.HasCaught()) { + v8::Local<v8::Message> message = tryCatch.Message(); + if (message.IsEmpty()) + *exception = "Unknown exception"; + else + *exception = WebCore::toWebCoreString(message->Get()); + return ""; + } + return WebCore::toWebCoreStringWithNullCheck(resObj); +} + +WebCore::Page* DebuggerAgentImpl::page() +{ + return m_webViewImpl->page(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/DebuggerAgentImpl.h b/WebKit/chromium/src/DebuggerAgentImpl.h new file mode 100644 index 0000000..6eaf576 --- /dev/null +++ b/WebKit/chromium/src/DebuggerAgentImpl.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 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 DebuggerAgentImpl_h +#define DebuggerAgentImpl_h + +#include "DebuggerAgent.h" + +#include <v8.h> +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { +class Document; +class Frame; +class Node; +class Page; +class String; +} + +namespace WebKit { + +class WebDevToolsAgentImpl; +class WebViewImpl; + +class DebuggerAgentImpl : public DebuggerAgent { +public: + // Creates utility context with injected js agent. + static void createUtilityContext(WebCore::Frame* frame, v8::Persistent<v8::Context>* context); + + DebuggerAgentImpl(WebKit::WebViewImpl* webViewImpl, + DebuggerAgentDelegate* delegate, + WebDevToolsAgentImpl* webdevtoolsAgent); + virtual ~DebuggerAgentImpl(); + + // DebuggerAgent implementation. + virtual void getContextId(); + virtual void processDebugCommands(); + + void debuggerOutput(const WebCore::String& out); + + void setAutoContinueOnException(bool autoContinue) { m_autoContinueOnException = autoContinue; } + + bool autoContinueOnException() { return m_autoContinueOnException; } + + // Executes function with the given name in the utility context. Passes node + // and json args as parameters. Note that the function called must be + // implemented in the inject_dispatch.js file. + WebCore::String executeUtilityFunction( + v8::Handle<v8::Context> context, + int callId, + const char* object, + const WebCore::String& functionName, + const WebCore::String& jsonArgs, + bool async, + WebCore::String* exception); + + + WebCore::Page* page(); + WebDevToolsAgentImpl* webdevtoolsAgent() { return m_webdevtoolsAgent; } + + WebKit::WebViewImpl* webView() { return m_webViewImpl; } + +private: + WebKit::WebViewImpl* m_webViewImpl; + DebuggerAgentDelegate* m_delegate; + WebDevToolsAgentImpl* m_webdevtoolsAgent; + bool m_autoContinueOnException; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/DebuggerAgentManager.cpp b/WebKit/chromium/src/DebuggerAgentManager.cpp new file mode 100644 index 0000000..faafaff --- /dev/null +++ b/WebKit/chromium/src/DebuggerAgentManager.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2010 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 "DebuggerAgentManager.h" + +#include "DebuggerAgentImpl.h" +#include "Frame.h" +#include "PageGroupLoadDeferrer.h" +#include "V8Proxy.h" +#include "WebDevToolsAgentImpl.h" +#include "WebFrameImpl.h" +#include "WebViewImpl.h" +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> + +namespace WebKit { + +WebDevToolsAgent::MessageLoopDispatchHandler DebuggerAgentManager::s_messageLoopDispatchHandler = 0; + +bool DebuggerAgentManager::s_inHostDispatchHandler = false; + +DebuggerAgentManager::DeferrersMap DebuggerAgentManager::s_pageDeferrers; + +bool DebuggerAgentManager::s_inUtilityContext = false; + +bool DebuggerAgentManager::s_debugBreakDelayed = false; + +namespace { + +class CallerIdWrapper : public v8::Debug::ClientData, public Noncopyable { +public: + CallerIdWrapper() : m_callerIsMananager(true), m_callerId(0) { } + explicit CallerIdWrapper(int callerId) + : m_callerIsMananager(false) + , m_callerId(callerId) { } + ~CallerIdWrapper() { } + bool callerIsMananager() const { return m_callerIsMananager; } + int callerId() const { return m_callerId; } +private: + bool m_callerIsMananager; + int m_callerId; +}; + +} // namespace + + +void DebuggerAgentManager::debugHostDispatchHandler() +{ + if (!s_messageLoopDispatchHandler || !s_attachedAgentsMap) + return; + + if (s_inHostDispatchHandler) + return; + + s_inHostDispatchHandler = true; + + Vector<WebViewImpl*> views; + // 1. Disable active objects and input events. + for (AttachedAgentsMap::iterator it = s_attachedAgentsMap->begin(); it != s_attachedAgentsMap->end(); ++it) { + DebuggerAgentImpl* agent = it->second; + s_pageDeferrers.set(agent->webView(), new WebCore::PageGroupLoadDeferrer(agent->page(), true)); + views.append(agent->webView()); + agent->webView()->setIgnoreInputEvents(true); + } + + // 2. Process messages. + s_messageLoopDispatchHandler(); + + // 3. Bring things back. + for (Vector<WebViewImpl*>::iterator it = views.begin(); it != views.end(); ++it) { + if (s_pageDeferrers.contains(*it)) { + // The view was not closed during the dispatch. + (*it)->setIgnoreInputEvents(false); + } + } + deleteAllValues(s_pageDeferrers); + s_pageDeferrers.clear(); + + s_inHostDispatchHandler = false; + if (!s_attachedAgentsMap) { + // Remove handlers if all agents were detached within host dispatch. + v8::Debug::SetMessageHandler(0); + v8::Debug::SetHostDispatchHandler(0); + } +} + +DebuggerAgentManager::AttachedAgentsMap* DebuggerAgentManager::s_attachedAgentsMap = 0; + +void DebuggerAgentManager::debugAttach(DebuggerAgentImpl* debuggerAgent) +{ + if (!s_attachedAgentsMap) { + s_attachedAgentsMap = new AttachedAgentsMap(); + v8::Debug::SetMessageHandler2(&DebuggerAgentManager::onV8DebugMessage); + v8::Debug::SetHostDispatchHandler(&DebuggerAgentManager::debugHostDispatchHandler, 100 /* ms */); + } + int hostId = debuggerAgent->webdevtoolsAgent()->hostId(); + ASSERT(hostId); + s_attachedAgentsMap->set(hostId, debuggerAgent); +} + +void DebuggerAgentManager::debugDetach(DebuggerAgentImpl* debuggerAgent) +{ + if (!s_attachedAgentsMap) { + ASSERT_NOT_REACHED(); + return; + } + int hostId = debuggerAgent->webdevtoolsAgent()->hostId(); + ASSERT(s_attachedAgentsMap->get(hostId) == debuggerAgent); + bool isOnBreakpoint = (findAgentForCurrentV8Context() == debuggerAgent); + s_attachedAgentsMap->remove(hostId); + + if (s_attachedAgentsMap->isEmpty()) { + delete s_attachedAgentsMap; + s_attachedAgentsMap = 0; + // Note that we do not empty handlers while in dispatch - we schedule + // continue and do removal once we are out of the dispatch. Also there is + // no need to send continue command in this case since removing message + // handler will cause debugger unload and all breakpoints will be cleared. + if (!s_inHostDispatchHandler) { + v8::Debug::SetMessageHandler2(0); + v8::Debug::SetHostDispatchHandler(0); + } + } else { + // Remove all breakpoints set by the agent. + String clearBreakpointGroupCmd = String::format( + "{\"seq\":1,\"type\":\"request\",\"command\":\"clearbreakpointgroup\"," + "\"arguments\":{\"groupId\":%d}}", + hostId); + sendCommandToV8(clearBreakpointGroupCmd, new CallerIdWrapper()); + + if (isOnBreakpoint) { + // Force continue if detach happened in nessted message loop while + // debugger was paused on a breakpoint(as long as there are other + // attached agents v8 will wait for explicit'continue' message). + sendContinueCommandToV8(); + } + } +} + +void DebuggerAgentManager::onV8DebugMessage(const v8::Debug::Message& message) +{ + v8::HandleScope scope; + v8::String::Value value(message.GetJSON()); + String out(reinterpret_cast<const UChar*>(*value), value.length()); + + // If callerData is not 0 the message is a response to a debugger command. + if (v8::Debug::ClientData* callerData = message.GetClientData()) { + CallerIdWrapper* wrapper = static_cast<CallerIdWrapper*>(callerData); + if (wrapper->callerIsMananager()) { + // Just ignore messages sent by this manager. + return; + } + DebuggerAgentImpl* debuggerAgent = debuggerAgentForHostId(wrapper->callerId()); + if (debuggerAgent) + debuggerAgent->debuggerOutput(out); + else if (!message.WillStartRunning()) { + // Autocontinue execution if there is no handler. + sendContinueCommandToV8(); + } + return; + } // Otherwise it's an event message. + ASSERT(message.IsEvent()); + + // Ignore unsupported event types. + if (message.GetEvent() != v8::AfterCompile && message.GetEvent() != v8::Break && message.GetEvent() != v8::Exception) + return; + + v8::Handle<v8::Context> context = message.GetEventContext(); + // If the context is from one of the inpected tabs it should have its context + // data. + if (context.IsEmpty()) { + // Unknown context, skip the event. + return; + } + + if (s_inUtilityContext && message.GetEvent() == v8::Break) { + // This may happen when two tabs are being debugged in the same process. + // Suppose that first debugger is pauesed on an exception. It will run + // nested MessageLoop which may process Break request from the second + // debugger. + s_debugBreakDelayed = true; + } else { + // If the context is from one of the inpected tabs or injected extension + // scripts it must have hostId in the data field. + int hostId = WebCore::V8Proxy::contextDebugId(context); + if (hostId != -1) { + DebuggerAgentImpl* agent = debuggerAgentForHostId(hostId); + if (agent) { + if (agent->autoContinueOnException() + && message.GetEvent() == v8::Exception) { + sendContinueCommandToV8(); + return; + } + + agent->debuggerOutput(out); + return; + } + } + } + + if (!message.WillStartRunning()) { + // Autocontinue execution on break and exception events if there is no + // handler. + sendContinueCommandToV8(); + } +} + +void DebuggerAgentManager::pauseScript() +{ + if (s_inUtilityContext) + s_debugBreakDelayed = true; + else + v8::Debug::DebugBreak(); +} + +void DebuggerAgentManager::executeDebuggerCommand(const String& command, int callerId) +{ + sendCommandToV8(command, new CallerIdWrapper(callerId)); +} + +void DebuggerAgentManager::setMessageLoopDispatchHandler(WebDevToolsAgent::MessageLoopDispatchHandler handler) +{ + s_messageLoopDispatchHandler = handler; +} + +void DebuggerAgentManager::setHostId(WebFrameImpl* webframe, int hostId) +{ + ASSERT(hostId > 0); + WebCore::V8Proxy* proxy = WebCore::V8Proxy::retrieve(webframe->frame()); + if (proxy) + proxy->setContextDebugId(hostId); +} + +void DebuggerAgentManager::onWebViewClosed(WebViewImpl* webview) +{ + if (s_pageDeferrers.contains(webview)) { + delete s_pageDeferrers.get(webview); + s_pageDeferrers.remove(webview); + } +} + +void DebuggerAgentManager::onNavigate() +{ + if (s_inHostDispatchHandler) + DebuggerAgentManager::sendContinueCommandToV8(); +} + +void DebuggerAgentManager::sendCommandToV8(const String& cmd, v8::Debug::ClientData* data) +{ + v8::Debug::SendCommand(reinterpret_cast<const uint16_t*>(cmd.characters()), cmd.length(), data); +} + +void DebuggerAgentManager::sendContinueCommandToV8() +{ + String continueCmd("{\"seq\":1,\"type\":\"request\",\"command\":\"continue\"}"); + sendCommandToV8(continueCmd, new CallerIdWrapper()); +} + +DebuggerAgentImpl* DebuggerAgentManager::findAgentForCurrentV8Context() +{ + if (!s_attachedAgentsMap) + return 0; + ASSERT(!s_attachedAgentsMap->isEmpty()); + + WebCore::Frame* frame = WebCore::V8Proxy::retrieveFrameForEnteredContext(); + if (!frame) + return 0; + WebCore::Page* page = frame->page(); + for (AttachedAgentsMap::iterator it = s_attachedAgentsMap->begin(); it != s_attachedAgentsMap->end(); ++it) { + if (it->second->page() == page) + return it->second; + } + return 0; +} + +DebuggerAgentImpl* DebuggerAgentManager::debuggerAgentForHostId(int hostId) +{ + if (!s_attachedAgentsMap) + return 0; + return s_attachedAgentsMap->get(hostId); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/DebuggerAgentManager.h b/WebKit/chromium/src/DebuggerAgentManager.h new file mode 100644 index 0000000..a2e9030 --- /dev/null +++ b/WebKit/chromium/src/DebuggerAgentManager.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 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 DebuggerAgentManager_h +#define DebuggerAgentManager_h + +#include "WebDevToolsAgent.h" +#include <v8-debug.h> +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { +class PageGroupLoadDeferrer; +class String; +} + +namespace WebKit { + +class DebuggerAgentImpl; +class DictionaryValue; +class WebFrameImpl; +class WebViewImpl; + +// There is single v8 instance per render process. Also there may be several +// RenderViews and consequently devtools agents in the process that want to talk +// to the v8 debugger. This class coordinates communication between the debug +// agents and v8 debugger. It will set debug output handler as long as at least +// one debugger agent is attached and remove it when last debugger agent is +// detached. When message is received from debugger it will route it to the +// right debugger agent if there is one otherwise the message will be ignored. +// +// v8 may send a message(e.g. exception event) after which it +// would expect some actions from the handler. If there is no appropriate +// debugger agent to handle such messages the manager will perform the action +// itself, otherwise v8 may hang waiting for the action. +class DebuggerAgentManager : public Noncopyable { +public: + static void debugAttach(DebuggerAgentImpl* debuggerAgent); + static void debugDetach(DebuggerAgentImpl* debuggerAgent); + static void pauseScript(); + static void executeDebuggerCommand(const WebCore::String& command, int callerId); + static void setMessageLoopDispatchHandler(WebDevToolsAgent::MessageLoopDispatchHandler handler); + + // Sets |hostId| as the frame context data. This id is used to filter scripts + // related to the inspected page. + static void setHostId(WebFrameImpl* webframe, int hostId); + + static void onWebViewClosed(WebViewImpl* webview); + + static void onNavigate(); + + class UtilityContextScope : public Noncopyable { + public: + UtilityContextScope() + { + ASSERT(!s_inUtilityContext); + s_inUtilityContext = true; + } + ~UtilityContextScope() + { + if (s_debugBreakDelayed) { + v8::Debug::DebugBreak(); + s_debugBreakDelayed = false; + } + s_inUtilityContext = false; + } + }; + +private: + DebuggerAgentManager(); + ~DebuggerAgentManager(); + + static void debugHostDispatchHandler(); + static void onV8DebugMessage(const v8::Debug::Message& message); + static void sendCommandToV8(const WebCore::String& cmd, + v8::Debug::ClientData* data); + static void sendContinueCommandToV8(); + + static DebuggerAgentImpl* findAgentForCurrentV8Context(); + static DebuggerAgentImpl* debuggerAgentForHostId(int hostId); + + typedef HashMap<int, DebuggerAgentImpl*> AttachedAgentsMap; + static AttachedAgentsMap* s_attachedAgentsMap; + + static WebDevToolsAgent::MessageLoopDispatchHandler s_messageLoopDispatchHandler; + static bool s_inHostDispatchHandler; + typedef HashMap<WebViewImpl*, WebCore::PageGroupLoadDeferrer*> DeferrersMap; + static DeferrersMap s_pageDeferrers; + + static bool s_inUtilityContext; + static bool s_debugBreakDelayed; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/DevToolsRPC.h b/WebKit/chromium/src/DevToolsRPC.h new file mode 100644 index 0000000..7176821 --- /dev/null +++ b/WebKit/chromium/src/DevToolsRPC.h @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2010 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. + */ + +// DevTools RPC subsystem is a simple string serialization-based rpc +// implementation. The client is responsible for defining the Rpc-enabled +// interface in terms of its macros: +// +// #define MYAPI_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3) +// METHOD0(Method1) +// METHOD1(Method3, int) +// (snippet above should be multiline macro, add trailing backslashes) +// +// DEFINE_RPC_CLASS(MyApi, MYAPI_STRUCT) +// +// The snippet above will generate three classes: MyApi, MyApiStub and +// MyApiDispatch. +// +// 1. For each method defined in the marco MyApi will have a +// pure virtual function generated, so that MyApi would look like: +// +// class MyApi { +// private: +// MyApi() { } +// ~MyApi() { } +// virtual void method1() = 0; +// virtual void method2( +// int param1, +// const String& param2, +// const Value& param3) = 0; +// virtual void method3(int param1) = 0; +// }; +// +// 2. MyApiStub will implement MyApi interface and would serialize all calls +// into the string-based calls of the underlying transport: +// +// DevToolsRPC::Delegate* transport; +// myApi = new MyApiStub(transport); +// myApi->method1(); +// myApi->method3(2); +// +// 3. MyApiDelegate is capable of dispatching the calls and convert them to the +// calls to the underlying MyApi methods: +// +// MyApi* realObject; +// MyApiDispatch::dispatch(realObject, rawStringCallGeneratedByStub); +// +// will make corresponding calls to the real object. + +#ifndef DevToolsRPC_h +#define DevToolsRPC_h + +#include "PlatformString.h" +#include "Vector.h" +#include "WebDevToolsMessageData.h" + +#include <wtf/Noncopyable.h> + +namespace WebCore { +class String; +} + +using WebCore::String; +using WTF::Vector; + +namespace WebKit { + +/////////////////////////////////////////////////////// +// RPC dispatch macro + +template<typename T> +struct RpcTypeTrait { + typedef T ApiType; +}; + +template<> +struct RpcTypeTrait<bool> { + typedef bool ApiType; + static bool parse(const WebCore::String& t) + { + return t == "true"; + } + static WebCore::String toString(bool b) + { + return b ? "true" : "false"; + } +}; + +template<> +struct RpcTypeTrait<int> { + typedef int ApiType; + static int parse(const WebCore::String& t) + { + bool success; + int i = t.toIntStrict(&success); + ASSERT(success); + return i; + } + static WebCore::String toString(int i) + { + return WebCore::String::number(i); + } +}; + +template<> +struct RpcTypeTrait<String> { + typedef const String& ApiType; + static String parse(const WebCore::String& t) + { + return t; + } + static WebCore::String toString(const String& t) + { + return t; + } +}; + +/////////////////////////////////////////////////////// +// RPC Api method declarations + +#define TOOLS_RPC_API_METHOD0(Method) \ + virtual void Method() = 0; + +#define TOOLS_RPC_API_METHOD1(Method, T1) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1) = 0; + +#define TOOLS_RPC_API_METHOD2(Method, T1, T2) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2) = 0; + +#define TOOLS_RPC_API_METHOD3(Method, T1, T2, T3) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2, \ + RpcTypeTrait<T3>::ApiType t3) = 0; + +#define TOOLS_RPC_API_METHOD4(Method, T1, T2, T3, T4) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2, \ + RpcTypeTrait<T3>::ApiType t3, \ + RpcTypeTrait<T4>::ApiType t4) = 0; + +#define TOOLS_RPC_API_METHOD5(Method, T1, T2, T3, T4, T5) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2, \ + RpcTypeTrait<T3>::ApiType t3, \ + RpcTypeTrait<T4>::ApiType t4, \ + RpcTypeTrait<T5>::ApiType t5) = 0; + +/////////////////////////////////////////////////////// +// RPC stub method implementations + +#define TOOLS_RPC_STUB_METHOD0(Method) \ + virtual void Method() { \ + Vector<String> args; \ + this->sendRpcMessage(m_className, #Method, args); \ + } + +#define TOOLS_RPC_STUB_METHOD1(Method, T1) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1) { \ + Vector<String> args(1); \ + args[0] = RpcTypeTrait<T1>::toString(t1); \ + this->sendRpcMessage(m_className, #Method, args); \ + } + +#define TOOLS_RPC_STUB_METHOD2(Method, T1, T2) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2) { \ + Vector<String> args(2); \ + args[0] = RpcTypeTrait<T1>::toString(t1); \ + args[1] = RpcTypeTrait<T2>::toString(t2); \ + this->sendRpcMessage(m_className, #Method, args); \ + } + +#define TOOLS_RPC_STUB_METHOD3(Method, T1, T2, T3) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2, \ + RpcTypeTrait<T3>::ApiType t3) { \ + Vector<String> args(3); \ + args[0] = RpcTypeTrait<T1>::toString(t1); \ + args[1] = RpcTypeTrait<T2>::toString(t2); \ + args[2] = RpcTypeTrait<T3>::toString(t3); \ + this->sendRpcMessage(m_className, #Method, args); \ + } + +#define TOOLS_RPC_STUB_METHOD4(Method, T1, T2, T3, T4) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2, \ + RpcTypeTrait<T3>::ApiType t3, \ + RpcTypeTrait<T4>::ApiType t4) { \ + Vector<String> args(4); \ + args[0] = RpcTypeTrait<T1>::toString(t1); \ + args[1] = RpcTypeTrait<T2>::toString(t2); \ + args[2] = RpcTypeTrait<T3>::toString(t3); \ + args[3] = RpcTypeTrait<T4>::toString(t4); \ + this->sendRpcMessage(m_className, #Method, args); \ + } + +#define TOOLS_RPC_STUB_METHOD5(Method, T1, T2, T3, T4, T5) \ + virtual void Method(RpcTypeTrait<T1>::ApiType t1, \ + RpcTypeTrait<T2>::ApiType t2, \ + RpcTypeTrait<T3>::ApiType t3, \ + RpcTypeTrait<T4>::ApiType t4, \ + RpcTypeTrait<T5>::ApiType t5) { \ + Vector<String> args(5); \ + args[0] = RpcTypeTrait<T1>::toString(t1); \ + args[1] = RpcTypeTrait<T2>::toString(t2); \ + args[2] = RpcTypeTrait<T3>::toString(t3); \ + args[3] = RpcTypeTrait<T4>::toString(t4); \ + args[4] = RpcTypeTrait<T5>::toString(t5); \ + this->sendRpcMessage(m_className, #Method, args); \ + } + +/////////////////////////////////////////////////////// +// RPC dispatch method implementations + +#define TOOLS_RPC_DISPATCH0(Method) \ +if (methodName == #Method) { \ + delegate->Method(); \ + return true; \ +} + +#define TOOLS_RPC_DISPATCH1(Method, T1) \ +if (methodName == #Method) { \ + delegate->Method(RpcTypeTrait<T1>::parse(args[0])); \ + return true; \ +} + +#define TOOLS_RPC_DISPATCH2(Method, T1, T2) \ +if (methodName == #Method) { \ + delegate->Method( \ + RpcTypeTrait<T1>::parse(args[0]), \ + RpcTypeTrait<T2>::parse(args[1]) \ + ); \ + return true; \ +} + +#define TOOLS_RPC_DISPATCH3(Method, T1, T2, T3) \ +if (methodName == #Method) { \ + delegate->Method( \ + RpcTypeTrait<T1>::parse(args[0]), \ + RpcTypeTrait<T2>::parse(args[1]), \ + RpcTypeTrait<T3>::parse(args[2]) \ + ); \ + return true; \ +} + +#define TOOLS_RPC_DISPATCH4(Method, T1, T2, T3, T4) \ +if (methodName == #Method) { \ + delegate->Method( \ + RpcTypeTrait<T1>::parse(args[0]), \ + RpcTypeTrait<T2>::parse(args[1]), \ + RpcTypeTrait<T3>::parse(args[2]), \ + RpcTypeTrait<T4>::parse(args[3]) \ + ); \ + return true; \ +} + +#define TOOLS_RPC_DISPATCH5(Method, T1, T2, T3, T4, T5) \ +if (methodName == #Method) { \ + delegate->Method( \ + RpcTypeTrait<T1>::parse(args[0]), \ + RpcTypeTrait<T2>::parse(args[1]), \ + RpcTypeTrait<T3>::parse(args[2]), \ + RpcTypeTrait<T4>::parse(args[3]), \ + RpcTypeTrait<T5>::parse(args[4]) \ + ); \ + return true; \ +} + +#define TOOLS_END_RPC_DISPATCH() \ +} + +// This macro defines three classes: Class with the Api, ClassStub that is +// serializing method calls and ClassDispatch that is capable of dispatching +// the serialized message into its delegate. +#define DEFINE_RPC_CLASS(Class, STRUCT) \ +class Class : public Noncopyable {\ +public: \ + Class() \ + { \ + m_className = #Class; \ + } \ + virtual ~Class() { } \ + \ + STRUCT( \ + TOOLS_RPC_API_METHOD0, \ + TOOLS_RPC_API_METHOD1, \ + TOOLS_RPC_API_METHOD2, \ + TOOLS_RPC_API_METHOD3, \ + TOOLS_RPC_API_METHOD4, \ + TOOLS_RPC_API_METHOD5) \ + WebCore::String m_className; \ +}; \ +\ +class Class##Stub \ + : public Class \ + , public DevToolsRPC { \ +public: \ + explicit Class##Stub(Delegate* delegate) : DevToolsRPC(delegate) { } \ + virtual ~Class##Stub() { } \ + typedef Class CLASS; \ + STRUCT( \ + TOOLS_RPC_STUB_METHOD0, \ + TOOLS_RPC_STUB_METHOD1, \ + TOOLS_RPC_STUB_METHOD2, \ + TOOLS_RPC_STUB_METHOD3, \ + TOOLS_RPC_STUB_METHOD4, \ + TOOLS_RPC_STUB_METHOD5) \ +}; \ +\ +class Class##Dispatch : public Noncopyable { \ +public: \ + Class##Dispatch() { } \ + virtual ~Class##Dispatch() { } \ + \ + static bool dispatch(Class* delegate, \ + const WebKit::WebDevToolsMessageData& data) { \ + String className = data.className; \ + if (className != #Class) \ + return false; \ + String methodName = data.methodName; \ + Vector<String> args; \ + for (size_t i = 0; i < data.arguments.size(); i++) \ + args.append(data.arguments[i]); \ + typedef Class CLASS; \ + STRUCT( \ + TOOLS_RPC_DISPATCH0, \ + TOOLS_RPC_DISPATCH1, \ + TOOLS_RPC_DISPATCH2, \ + TOOLS_RPC_DISPATCH3, \ + TOOLS_RPC_DISPATCH4, \ + TOOLS_RPC_DISPATCH5) \ + return false; \ + } \ +}; + +/////////////////////////////////////////////////////// +// RPC base class +class DevToolsRPC { +public: + class Delegate { + public: + Delegate() { } + virtual ~Delegate() { } + virtual void sendRpcMessage(const WebKit::WebDevToolsMessageData& data) = 0; + }; + + explicit DevToolsRPC(Delegate* delegate) : m_delegate(delegate) { } + virtual ~DevToolsRPC() { }; + +protected: + void sendRpcMessage(const String& className, + const String& methodName, + const Vector<String>& args) { + WebKit::WebVector<WebKit::WebString> webArgs(args.size()); + for (size_t i = 0; i < args.size(); i++) + webArgs[i] = args[i]; + WebKit::WebDevToolsMessageData data; + data.className = className; + data.methodName = methodName; + data.arguments.swap(webArgs); + this->m_delegate->sendRpcMessage(data); + } + + Delegate* m_delegate; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/DevToolsRPCJS.h b/WebKit/chromium/src/DevToolsRPCJS.h new file mode 100644 index 0000000..8ae279f --- /dev/null +++ b/WebKit/chromium/src/DevToolsRPCJS.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010 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. + */ + +// Additional set of macros for the JS RPC. + +#ifndef DevToolsRPCJS_h +#define DevToolsRPCJS_h + +// Do not remove this one although it is not used. +#include "BoundObject.h" +#include "DevToolsRPC.h" +#include "WebFrame.h" +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> + +namespace WebKit { + +/////////////////////////////////////////////////////// +// JS RPC binds and stubs + +#define TOOLS_RPC_JS_BIND_METHOD0(Method) \ + boundObj.addProtoFunction(#Method, OCLASS::js##Method); + +#define TOOLS_RPC_JS_BIND_METHOD1(Method, T1) \ + boundObj.addProtoFunction(#Method, OCLASS::js##Method); + +#define TOOLS_RPC_JS_BIND_METHOD2(Method, T1, T2) \ + boundObj.addProtoFunction(#Method, OCLASS::js##Method); + +#define TOOLS_RPC_JS_BIND_METHOD3(Method, T1, T2, T3) \ + boundObj.addProtoFunction(#Method, OCLASS::js##Method); + +#define TOOLS_RPC_JS_BIND_METHOD4(Method, T1, T2, T3, T4) \ + boundObj.addProtoFunction(#Method, OCLASS::js##Method); + +#define TOOLS_RPC_JS_BIND_METHOD5(Method, T1, T2, T3, T4, T5) \ + boundObj.addProtoFunction(#Method, OCLASS::js##Method); + +#define TOOLS_RPC_JS_STUB_METHOD0(Method) \ + static v8::Handle<v8::Value> js##Method(const v8::Arguments& args) { \ + sendRpcMessageFromJS(#Method, args, 0); \ + return v8::Undefined(); \ + } + +#define TOOLS_RPC_JS_STUB_METHOD1(Method, T1) \ + static v8::Handle<v8::Value> js##Method(const v8::Arguments& args) { \ + sendRpcMessageFromJS(#Method, args, 1); \ + return v8::Undefined(); \ + } + +#define TOOLS_RPC_JS_STUB_METHOD2(Method, T1, T2) \ + static v8::Handle<v8::Value> js##Method(const v8::Arguments& args) { \ + sendRpcMessageFromJS(#Method, args, 2); \ + return v8::Undefined(); \ + } + +#define TOOLS_RPC_JS_STUB_METHOD3(Method, T1, T2, T3) \ + static v8::Handle<v8::Value> js##Method(const v8::Arguments& args) { \ + sendRpcMessageFromJS(#Method, args, 3); \ + return v8::Undefined(); \ + } + +#define TOOLS_RPC_JS_STUB_METHOD4(Method, T1, T2, T3, T4) \ + static v8::Handle<v8::Value> js##Method(const v8::Arguments& args) { \ + sendRpcMessageFromJS(#Method, args, 4); \ + return v8::Undefined(); \ + } + +#define TOOLS_RPC_JS_STUB_METHOD5(Method, T1, T2, T3, T4, T5) \ + static v8::Handle<v8::Value> js##Method(const v8::Arguments& args) { \ + sendRpcMessageFromJS(#Method, args, 5); \ + return v8::Undefined(); \ + } + +/////////////////////////////////////////////////////// +// JS RPC main obj macro + +#define DEFINE_RPC_JS_BOUND_OBJ(Class, STRUCT, DClass, DELEGATE_STRUCT) \ +class JS##Class##BoundObj : public Class##Stub { \ +public: \ + JS##Class##BoundObj(Delegate* rpcDelegate, \ + v8::Handle<v8::Context> context, \ + const char* classname) \ + : Class##Stub(rpcDelegate) { \ + BoundObject boundObj(context, this, classname); \ + STRUCT( \ + TOOLS_RPC_JS_BIND_METHOD0, \ + TOOLS_RPC_JS_BIND_METHOD1, \ + TOOLS_RPC_JS_BIND_METHOD2, \ + TOOLS_RPC_JS_BIND_METHOD3, \ + TOOLS_RPC_JS_BIND_METHOD4, \ + TOOLS_RPC_JS_BIND_METHOD5) \ + boundObj.build(); \ + } \ + virtual ~JS##Class##BoundObj() { } \ + typedef JS##Class##BoundObj OCLASS; \ + STRUCT( \ + TOOLS_RPC_JS_STUB_METHOD0, \ + TOOLS_RPC_JS_STUB_METHOD1, \ + TOOLS_RPC_JS_STUB_METHOD2, \ + TOOLS_RPC_JS_STUB_METHOD3, \ + TOOLS_RPC_JS_STUB_METHOD4, \ + TOOLS_RPC_JS_STUB_METHOD5) \ +private: \ + static void sendRpcMessageFromJS(const char* method, \ + const v8::Arguments& jsArguments, \ + size_t argsCount) \ + { \ + Vector<String> args(argsCount); \ + for (size_t i = 0; i < argsCount; i++) \ + args[i] = WebCore::toWebCoreStringWithNullCheck(jsArguments[i]); \ + void* selfPtr = v8::External::Cast(*jsArguments.Data())->Value(); \ + JS##Class##BoundObj* self = static_cast<JS##Class##BoundObj*>(selfPtr); \ + self->sendRpcMessage(#Class, method, args); \ + } \ +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/DragClientImpl.cpp b/WebKit/chromium/src/DragClientImpl.cpp index 5d8a9c3..671e7ca 100644 --- a/WebKit/chromium/src/DragClientImpl.cpp +++ b/WebKit/chromium/src/DragClientImpl.cpp @@ -79,9 +79,7 @@ void DragClientImpl::startDrag(DragImageRef dragImage, WebDragData dragData = static_cast<ClipboardChromium*>(clipboard)->dataObject(); - DragOperation dragOperationMask; - if (!clipboard->sourceOperation(dragOperationMask)) - dragOperationMask = DragOperationEvery; + DragOperation dragOperationMask = clipboard->sourceOperation(); m_webView->startDragging( eventPos, dragData, static_cast<WebDragOperationsMask>(dragOperationMask)); diff --git a/WebKit/chromium/src/EditorClientImpl.cpp b/WebKit/chromium/src/EditorClientImpl.cpp index 5db8ff7..d5bddc5 100644 --- a/WebKit/chromium/src/EditorClientImpl.cpp +++ b/WebKit/chromium/src/EditorClientImpl.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2006, 2007 Apple, Inc. All rights reserved. - * Copyright (C) 2009 Google, Inc. All rights reserved. + * Copyright (C) 2010 Google, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -105,7 +105,7 @@ bool EditorClientImpl::isSelectTrailingWhitespaceEnabled() { if (m_webView->client()) return m_webView->client()->isSelectTrailingWhitespaceEnabled(); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) return true; #else return false; @@ -357,7 +357,7 @@ static const unsigned CtrlKey = 1 << 0; static const unsigned AltKey = 1 << 1; static const unsigned ShiftKey = 1 << 2; static const unsigned MetaKey = 1 << 3; -#if PLATFORM(DARWIN) +#if OS(DARWIN) // Aliases for the generic key defintions to make kbd shortcuts definitions more // readable on OS X. static const unsigned OptionKey = AltKey; @@ -387,7 +387,7 @@ struct KeyPressEntry { static const KeyDownEntry keyDownEntries[] = { { VKEY_LEFT, 0, "MoveLeft" }, { VKEY_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, -#if PLATFORM(DARWIN) +#if OS(DARWIN) { VKEY_LEFT, OptionKey, "MoveWordLeft" }, { VKEY_LEFT, OptionKey | ShiftKey, "MoveWordLeftAndModifySelection" }, @@ -398,7 +398,7 @@ static const KeyDownEntry keyDownEntries[] = { #endif { VKEY_RIGHT, 0, "MoveRight" }, { VKEY_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, -#if PLATFORM(DARWIN) +#if OS(DARWIN) { VKEY_RIGHT, OptionKey, "MoveWordRight" }, { VKEY_RIGHT, OptionKey | ShiftKey, "MoveWordRightAndModifySelection" }, @@ -418,12 +418,12 @@ static const KeyDownEntry keyDownEntries[] = { { VKEY_HOME, 0, "MoveToBeginningOfLine" }, { VKEY_HOME, ShiftKey, "MoveToBeginningOfLineAndModifySelection" }, -#if PLATFORM(DARWIN) +#if OS(DARWIN) { VKEY_LEFT, CommandKey, "MoveToBeginningOfLine" }, { VKEY_LEFT, CommandKey | ShiftKey, "MoveToBeginningOfLineAndModifySelection" }, #endif -#if PLATFORM(DARWIN) +#if OS(DARWIN) { VKEY_UP, CommandKey, "MoveToBeginningOfDocument" }, { VKEY_UP, CommandKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" }, @@ -434,7 +434,7 @@ static const KeyDownEntry keyDownEntries[] = { #endif { VKEY_END, 0, "MoveToEndOfLine" }, { VKEY_END, ShiftKey, "MoveToEndOfLineAndModifySelection" }, -#if PLATFORM(DARWIN) +#if OS(DARWIN) { VKEY_DOWN, CommandKey, "MoveToEndOfDocument" }, { VKEY_DOWN, CommandKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection" }, @@ -443,7 +443,7 @@ static const KeyDownEntry keyDownEntries[] = { { VKEY_END, CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection" }, #endif -#if PLATFORM(DARWIN) +#if OS(DARWIN) { VKEY_RIGHT, CommandKey, "MoveToEndOfLine" }, { VKEY_RIGHT, CommandKey | ShiftKey, "MoveToEndOfLineAndModifySelection" }, @@ -451,7 +451,7 @@ static const KeyDownEntry keyDownEntries[] = { { VKEY_BACK, 0, "DeleteBackward" }, { VKEY_BACK, ShiftKey, "DeleteBackward" }, { VKEY_DELETE, 0, "DeleteForward" }, -#if PLATFORM(DARWIN) +#if OS(DARWIN) { VKEY_BACK, OptionKey, "DeleteWordBackward" }, { VKEY_DELETE, OptionKey, "DeleteWordForward" }, #else @@ -473,7 +473,7 @@ static const KeyDownEntry keyDownEntries[] = { { VKEY_INSERT, CtrlKey, "Copy" }, { VKEY_INSERT, ShiftKey, "Paste" }, { VKEY_DELETE, ShiftKey, "Cut" }, -#if !PLATFORM(DARWIN) +#if !OS(DARWIN) // On OS X, we pipe these back to the browser, so that it can do menu item // blinking. { 'C', CtrlKey, "Copy" }, @@ -600,13 +600,13 @@ bool EditorClientImpl::handleEditingKeyboardEvent(KeyboardEvent* evt) // unexpected behaviour if (ch < ' ') return false; -#if !PLATFORM(WIN_OS) +#if !OS(WINDOWS) // Don't insert ASCII character if ctrl w/o alt or meta is on. // On Mac, we should ignore events when meta is on (Command-<x>). if (ch < 0x80) { if (evt->keyEvent()->ctrlKey() && !evt->keyEvent()->altKey()) return false; -#if PLATFORM(DARWIN) +#if OS(DARWIN) if (evt->keyEvent()->metaKey()) return false; #endif @@ -654,7 +654,7 @@ void EditorClientImpl::textFieldDidEndEditing(Element* element) m_autofillTimer.stop(); // Hide any showing popup. - m_webView->hideAutoCompletePopup(); + m_webView->hideSuggestionsPopup(); if (!m_webView->client()) return; // The page is getting closed, don't fill the password. @@ -748,7 +748,7 @@ void EditorClientImpl::doAutofill(Timer<EditorClientImpl>* timer) && inputElement->selectionEnd() == static_cast<int>(value.length()); if ((!args->autofillOnEmptyValue && value.isEmpty()) || !isCaretAtEnd) { - m_webView->hideAutoCompletePopup(); + m_webView->hideSuggestionsPopup(); return; } diff --git a/WebKit/chromium/src/EventListenerWrapper.cpp b/WebKit/chromium/src/EventListenerWrapper.cpp new file mode 100644 index 0000000..f2d2979 --- /dev/null +++ b/WebKit/chromium/src/EventListenerWrapper.cpp @@ -0,0 +1,72 @@ +/*
+ * Copyright (C) 2010 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 "EventListenerWrapper.h"
+
+#include "Event.h"
+#include "EventListener.h"
+
+#include "WebEvent.h"
+#include "WebEventListener.h"
+
+namespace WebKit {
+
+EventListenerWrapper::EventListenerWrapper(WebEventListener* webEventListener)
+ : EventListener(EventListener::JSEventListenerType)
+ , m_webEventListener(webEventListener)
+{
+}
+
+EventListenerWrapper::~EventListenerWrapper()
+{
+ if (m_webEventListener)
+ m_webEventListener->notifyEventListenerDeleted(this);
+}
+
+bool EventListenerWrapper::operator==(const EventListener& listener)
+{
+ return this == &listener;
+}
+
+void EventListenerWrapper::handleEvent(ScriptExecutionContext* context, Event* event)
+{
+ if (!m_webEventListener)
+ return;
+ WebEvent webEvent(event);
+ m_webEventListener->handleEvent(webEvent);
+}
+
+void EventListenerWrapper::webEventListenerDeleted()
+{
+ m_webEventListener = 0;
+}
+
+} // namespace WebKit
diff --git a/WebKit/chromium/src/EventListenerWrapper.h b/WebKit/chromium/src/EventListenerWrapper.h new file mode 100644 index 0000000..2a0cbbb --- /dev/null +++ b/WebKit/chromium/src/EventListenerWrapper.h @@ -0,0 +1,62 @@ +/*
+ * Copyright (C) 2010 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 EventListenerWrapper_h
+#define EventListenerWrapper_h
+
+#include "EventListener.h"
+
+namespace WebCore {
+class ScriptExecutionContext;
+}
+
+using namespace WebCore;
+
+namespace WebKit {
+
+class WebEventListener;
+
+class EventListenerWrapper : public EventListener {
+public:
+ EventListenerWrapper(WebEventListener*);
+ ~EventListenerWrapper();
+
+ virtual bool operator==(const EventListener&);
+ virtual void handleEvent(ScriptExecutionContext*, Event*);
+
+ void webEventListenerDeleted();
+
+private:
+ WebEventListener* m_webEventListener;
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/WebKit/chromium/src/FrameLoaderClientImpl.cpp b/WebKit/chromium/src/FrameLoaderClientImpl.cpp index 3ca8e8d..b984308 100644 --- a/WebKit/chromium/src/FrameLoaderClientImpl.cpp +++ b/WebKit/chromium/src/FrameLoaderClientImpl.cpp @@ -164,6 +164,22 @@ bool FrameLoaderClientImpl::allowJavaScript(bool enabledPerSettings) return enabledPerSettings; } +bool FrameLoaderClientImpl::allowPlugins(bool enabledPerSettings) +{ + if (m_webFrame->client()) + return m_webFrame->client()->allowPlugins(m_webFrame, enabledPerSettings); + + return enabledPerSettings; +} + +bool FrameLoaderClientImpl::allowImages(bool enabledPerSettings) +{ + if (m_webFrame->client()) + return m_webFrame->client()->allowImages(m_webFrame, enabledPerSettings); + + return enabledPerSettings; +} + bool FrameLoaderClientImpl::hasWebView() const { return m_webFrame->viewImpl(); @@ -1325,7 +1341,7 @@ PassRefPtr<Widget> FrameLoaderClientImpl::createPlugin( const String& mimeType, bool loadManually) { -#if !PLATFORM(WIN_OS) +#if !OS(WINDOWS) // 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 diff --git a/WebKit/chromium/src/FrameLoaderClientImpl.h b/WebKit/chromium/src/FrameLoaderClientImpl.h index 4ddfe5f..901600c 100644 --- a/WebKit/chromium/src/FrameLoaderClientImpl.h +++ b/WebKit/chromium/src/FrameLoaderClientImpl.h @@ -66,7 +66,7 @@ public: virtual void didCreateScriptContextForFrame(); virtual void didDestroyScriptContextForFrame(); - // A context untied to a frame was created (through evaluateInNewContext). + // A context untied to a frame was created (through evaluateInIsolatedWorld). // This context is not tied to the lifetime of its frame, and is destroyed // in garbage collection. virtual void didCreateIsolatedScriptContext(); @@ -192,6 +192,8 @@ public: virtual void registerForIconNotification(bool listen = true); virtual void didChangeScrollOffset(); virtual bool allowJavaScript(bool enabledPerSettings); + virtual bool allowPlugins(bool enabledPerSettings); + virtual bool allowImages(bool enabledPerSettings); private: void makeDocumentView(); diff --git a/WebKit/chromium/src/GraphicsContext3D.cpp b/WebKit/chromium/src/GraphicsContext3D.cpp index c9ba5a1..83574da 100644 --- a/WebKit/chromium/src/GraphicsContext3D.cpp +++ b/WebKit/chromium/src/GraphicsContext3D.cpp @@ -56,7 +56,7 @@ #include <stdio.h> #include <wtf/FastMalloc.h> -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) #include <windows.h> #endif @@ -76,11 +76,11 @@ #include "NativeImageSkia.h" #endif -#if PLATFORM(DARWIN) +#if OS(DARWIN) #define USE_TEXTURE_RECTANGLE_FOR_FRAMEBUFFER #endif -#if PLATFORM(LINUX) +#if OS(LINUX) #include <dlfcn.h> #include "GL/glxew.h" #endif @@ -98,7 +98,7 @@ namespace WebCore { class GraphicsContext3DInternal { public: - GraphicsContext3DInternal(); + GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs); ~GraphicsContext3DInternal(); bool makeContextCurrent(); @@ -116,12 +116,15 @@ public: void activeTexture(unsigned long texture); void bindBuffer(unsigned long target, WebGLBuffer* buffer); + void bindFramebuffer(unsigned long target, + WebGLFramebuffer* framebuffer); void bindTexture(unsigned long target, WebGLTexture* texture); void bufferDataImpl(unsigned long target, int size, const void* data, unsigned long usage); void disableVertexAttribArray(unsigned long index); void enableVertexAttribArray(unsigned long index); unsigned long getError(); + GraphicsContext3D::Attributes getContextAttributes(); void vertexAttribPointer(unsigned long indx, int size, int type, bool normalized, unsigned long stride, unsigned long offset); void viewportImpl(long x, long y, unsigned long width, unsigned long height); @@ -129,11 +132,16 @@ public: void synthesizeGLError(unsigned long error); private: + GraphicsContext3D::Attributes m_attrs; + unsigned int m_texture; unsigned int m_fbo; unsigned int m_depthBuffer; unsigned int m_cachedWidth, m_cachedHeight; + // For tracking which FBO is bound + unsigned int m_boundFBO; + #ifdef FLIP_FRAMEBUFFER_VERTICALLY unsigned char* m_scanline; void flipVertically(unsigned char* framebuffer, @@ -176,7 +184,8 @@ private: SkBitmap* m_resizingBitmap; #endif -#if PLATFORM(WIN_OS) + static bool s_initializedGLEW; +#if OS(WINDOWS) HWND m_canvasWindow; HDC m_canvasDC; HGLRC m_contextObj; @@ -184,30 +193,153 @@ private: CGLPBufferObj m_pbuffer; CGLContextObj m_contextObj; unsigned char* m_renderOutput; - CGContextRef m_cgContext; -#elif PLATFORM(LINUX) - Display* m_display; +#elif OS(LINUX) GLXContext m_contextObj; GLXPbuffer m_pbuffer; + // In order to avoid problems caused by linking against libGL, we // dynamically look up all the symbols we need. // http://code.google.com/p/chromium/issues/detail?id=16800 - void* m_libGL; - PFNGLXCHOOSEFBCONFIGPROC m_glXChooseFBConfig; - PFNGLXCREATENEWCONTEXTPROC m_glXCreateNewContext; - PFNGLXCREATEPBUFFERPROC m_glXCreatePbuffer; - PFNGLXDESTROYPBUFFERPROC m_glXDestroyPbuffer; - typedef Bool (* PFNGLXMAKECURRENTPROC)(Display* dpy, GLXDrawable drawable, GLXContext ctx); - PFNGLXMAKECURRENTPROC m_glXMakeCurrent; - typedef void (* PFNGLXDESTROYCONTEXTPROC)(Display* dpy, GLXContext ctx); - PFNGLXDESTROYCONTEXTPROC m_glXDestroyContext; - typedef GLXContext (* PFNGLXGETCURRENTCONTEXTPROC)(void); - PFNGLXGETCURRENTCONTEXTPROC m_glXGetCurrentContext; + class GLConnection { + public: + ~GLConnection(); + + static GLConnection* create(); + + GLXFBConfig* chooseFBConfig(int screen, const int *attrib_list, int *nelements) + { + return m_glXChooseFBConfig(m_display, screen, attrib_list, nelements); + } + + GLXContext createNewContext(GLXFBConfig config, int renderType, GLXContext shareList, Bool direct) + { + return m_glXCreateNewContext(m_display, config, renderType, shareList, direct); + } + + GLXPbuffer createPbuffer(GLXFBConfig config, const int *attribList) + { + return m_glXCreatePbuffer(m_display, config, attribList); + } + + void destroyPbuffer(GLXPbuffer pbuf) + { + m_glXDestroyPbuffer(m_display, pbuf); + } + + Bool makeCurrent(GLXDrawable drawable, GLXContext ctx) + { + return m_glXMakeCurrent(m_display, drawable, ctx); + } + + void destroyContext(GLXContext ctx) + { + m_glXDestroyContext(m_display, ctx); + } + + GLXContext getCurrentContext() + { + return m_glXGetCurrentContext(); + } + + private: + Display* m_display; + void* m_libGL; + PFNGLXCHOOSEFBCONFIGPROC m_glXChooseFBConfig; + PFNGLXCREATENEWCONTEXTPROC m_glXCreateNewContext; + PFNGLXCREATEPBUFFERPROC m_glXCreatePbuffer; + PFNGLXDESTROYPBUFFERPROC m_glXDestroyPbuffer; + typedef Bool (* PFNGLXMAKECURRENTPROC)(Display* dpy, GLXDrawable drawable, GLXContext ctx); + PFNGLXMAKECURRENTPROC m_glXMakeCurrent; + typedef void (* PFNGLXDESTROYCONTEXTPROC)(Display* dpy, GLXContext ctx); + PFNGLXDESTROYCONTEXTPROC m_glXDestroyContext; + typedef GLXContext (* PFNGLXGETCURRENTCONTEXTPROC)(void); + PFNGLXGETCURRENTCONTEXTPROC m_glXGetCurrentContext; + + GLConnection(Display* display, + void* libGL, + PFNGLXCHOOSEFBCONFIGPROC chooseFBConfig, + PFNGLXCREATENEWCONTEXTPROC createNewContext, + PFNGLXCREATEPBUFFERPROC createPbuffer, + PFNGLXDESTROYPBUFFERPROC destroyPbuffer, + PFNGLXMAKECURRENTPROC makeCurrent, + PFNGLXDESTROYCONTEXTPROC destroyContext, + PFNGLXGETCURRENTCONTEXTPROC getCurrentContext) + : m_libGL(libGL) + , m_display(display) + , m_glXChooseFBConfig(chooseFBConfig) + , m_glXCreateNewContext(createNewContext) + , m_glXCreatePbuffer(createPbuffer) + , m_glXDestroyPbuffer(destroyPbuffer) + , m_glXMakeCurrent(makeCurrent) + , m_glXDestroyContext(destroyContext) + , m_glXGetCurrentContext(getCurrentContext) + { + } + }; + + static GLConnection* s_gl; #else #error Must port GraphicsContext3D to your platform #endif }; +bool GraphicsContext3DInternal::s_initializedGLEW = false; + +#if OS(LINUX) +GraphicsContext3DInternal::GLConnection* GraphicsContext3DInternal::s_gl = 0; + +GraphicsContext3DInternal::GLConnection* GraphicsContext3DInternal::GLConnection::create() +{ + Display* dpy = XOpenDisplay(0); + if (!dpy) { + printf("GraphicsContext3D: error opening X display\n"); + return 0; + } + + // We use RTLD_GLOBAL semantics so that GLEW initialization works; + // GLEW expects to be able to open the current process's handle + // and do dlsym's of GL entry points from there. + void* libGL = dlopen("libGL.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (!libGL) { + XCloseDisplay(dpy); + printf("GraphicsContext3D: error opening libGL.so.1: %s\n", dlerror()); + return 0; + } + + PFNGLXCHOOSEFBCONFIGPROC chooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC) dlsym(libGL, "glXChooseFBConfig"); + PFNGLXCREATENEWCONTEXTPROC createNewContext = (PFNGLXCREATENEWCONTEXTPROC) dlsym(libGL, "glXCreateNewContext"); + PFNGLXCREATEPBUFFERPROC createPbuffer = (PFNGLXCREATEPBUFFERPROC) dlsym(libGL, "glXCreatePbuffer"); + PFNGLXDESTROYPBUFFERPROC destroyPbuffer = (PFNGLXDESTROYPBUFFERPROC) dlsym(libGL, "glXDestroyPbuffer"); + PFNGLXMAKECURRENTPROC makeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(libGL, "glXMakeCurrent"); + PFNGLXDESTROYCONTEXTPROC destroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(libGL, "glXDestroyContext"); + PFNGLXGETCURRENTCONTEXTPROC getCurrentContext = (PFNGLXGETCURRENTCONTEXTPROC) dlsym(libGL, "glXGetCurrentContext"); + if (!chooseFBConfig || !createNewContext || !createPbuffer + || !destroyPbuffer || !makeCurrent || !destroyContext + || !getCurrentContext) { + XCloseDisplay(dpy); + dlclose(libGL); + printf("GraphicsContext3D: error looking up bootstrapping entry points\n"); + return 0; + } + return new GLConnection(dpy, + libGL, + chooseFBConfig, + createNewContext, + createPbuffer, + destroyPbuffer, + makeCurrent, + destroyContext, + getCurrentContext); +} + +GraphicsContext3DInternal::GLConnection::~GLConnection() +{ + XCloseDisplay(m_display); + dlclose(m_libGL); +} + +#endif // OS(LINUX) + GraphicsContext3DInternal::VertexAttribPointerState::VertexAttribPointerState() : enabled(false) , buffer(0) @@ -220,20 +352,12 @@ GraphicsContext3DInternal::VertexAttribPointerState::VertexAttribPointerState() { } -#if PLATFORM(LINUX) -static void* tryLoad(const char* libName) -{ - // We use RTLD_GLOBAL semantics so that GLEW initialization works; - // GLEW expects to be able to open the current process's handle - // and do dlsym's of GL entry points from there. - return dlopen(libName, RTLD_LAZY | RTLD_GLOBAL); -} -#endif - -GraphicsContext3DInternal::GraphicsContext3DInternal() - : m_texture(0) +GraphicsContext3DInternal::GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs) + : m_attrs(attrs) + , m_texture(0) , m_fbo(0) , m_depthBuffer(0) + , m_boundFBO(0) #ifdef FLIP_FRAMEBUFFER_VERTICALLY , m_scanline(0) #endif @@ -241,7 +365,7 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() #if PLATFORM(SKIA) , m_resizingBitmap(0) #endif -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) , m_canvasWindow(0) , m_canvasDC(0) , m_contextObj(0) @@ -249,23 +373,24 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() , m_pbuffer(0) , m_contextObj(0) , m_renderOutput(0) - , m_cgContext(0) -#elif PLATFORM(LINUX) - , m_display(0) +#elif OS(LINUX) , m_contextObj(0) , m_pbuffer(0) - , m_glXChooseFBConfig(0) - , m_glXCreateNewContext(0) - , m_glXCreatePbuffer(0) - , m_glXDestroyPbuffer(0) - , m_glXMakeCurrent(0) - , m_glXDestroyContext(0) - , m_glXGetCurrentContext(0) #else #error Must port to your platform #endif { -#if PLATFORM(WIN_OS) + // FIXME: we need to take into account the user's requested + // context creation attributes, in particular stencil and + // antialias, and determine which could and could not be honored + // based on the capabilities of the OpenGL implementation. + m_attrs.alpha = true; + m_attrs.depth = true; + m_attrs.stencil = false; + m_attrs.antialias = false; + m_attrs.premultipliedAlpha = true; + +#if OS(WINDOWS) WNDCLASS wc; if (!GetClassInfo(GetModuleHandle(0), L"CANVASGL", &wc)) { ZeroMemory(&wc, sizeof(WNDCLASS)); @@ -377,43 +502,13 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() } m_pbuffer = pbuffer; m_contextObj = context; -#elif PLATFORM(LINUX) - m_display = XOpenDisplay(0); - if (!m_display) { - printf("GraphicsContext3D: error opening X display\n"); - return; +#elif OS(LINUX) + if (!s_gl) { + s_gl = GLConnection::create(); + if (!s_gl) + return; } - const char* libNames[] = { - "/usr/lib/libGL.so.1", - "/usr/lib32/libGL.so.1", - "/usr/lib64/libGL.so.1", - }; - for (int i = 0; i < sizeof(libNames) / sizeof(const char*); i++) { - m_libGL = tryLoad(libNames[i]); - if (m_libGL) - break; - } - if (!m_libGL) { - printf("GraphicsContext3D: error opening libGL.so.1\n"); - printf("GraphicsContext3D: tried:"); - for (int i = 0; i < sizeof(libNames) / sizeof(const char*); i++) - printf(" %s", libNames[i]); - return; - } - m_glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC) dlsym(m_libGL, "glXChooseFBConfig"); - m_glXCreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) dlsym(m_libGL, "glXCreateNewContext"); - m_glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC) dlsym(m_libGL, "glXCreatePbuffer"); - m_glXDestroyPbuffer = (PFNGLXDESTROYPBUFFERPROC) dlsym(m_libGL, "glXDestroyPbuffer"); - m_glXMakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(m_libGL, "glXMakeCurrent"); - m_glXDestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(m_libGL, "glXDestroyContext"); - m_glXGetCurrentContext = (PFNGLXGETCURRENTCONTEXTPROC) dlsym(m_libGL, "glXGetCurrentContext"); - if (!m_glXChooseFBConfig || !m_glXCreateNewContext || !m_glXCreatePbuffer - || !m_glXDestroyPbuffer || !m_glXMakeCurrent || !m_glXDestroyContext - || !m_glXGetCurrentContext) { - printf("GraphicsContext3D: error looking up bootstrapping entry points\n"); - return; - } int configAttrs[] = { GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, @@ -424,7 +519,7 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() 0 }; int nelements = 0; - GLXFBConfig* config = m_glXChooseFBConfig(m_display, 0, configAttrs, &nelements); + GLXFBConfig* config = s_gl->chooseFBConfig(0, configAttrs, &nelements); if (!config) { printf("GraphicsContext3D: glXChooseFBConfig failed\n"); return; @@ -434,7 +529,7 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() XFree(config); return; } - GLXContext context = m_glXCreateNewContext(m_display, config[0], GLX_RGBA_TYPE, 0, True); + GLXContext context = s_gl->createNewContext(config[0], GLX_RGBA_TYPE, 0, True); if (!context) { printf("GraphicsContext3D: glXCreateNewContext failed\n"); XFree(config); @@ -447,13 +542,13 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() 1, 0 }; - GLXPbuffer pbuffer = m_glXCreatePbuffer(m_display, config[0], pbufferAttrs); + GLXPbuffer pbuffer = s_gl->createPbuffer(config[0], pbufferAttrs); XFree(config); if (!pbuffer) { printf("GraphicsContext3D: glxCreatePbuffer failed\n"); return; } - if (!m_glXMakeCurrent(m_display, pbuffer, context)) { + if (!s_gl->makeCurrent(pbuffer, context)) { printf("GraphicsContext3D: glXMakeCurrent failed\n"); return; } @@ -463,8 +558,7 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() #error Must port to your platform #endif - static bool initializedGLEW = false; - if (!initializedGLEW) { + if (!s_initializedGLEW) { // Initialize GLEW and check for GL 2.0 support by the drivers. GLenum glewInitResult = glewInit(); if (glewInitResult != GLEW_OK) { @@ -475,7 +569,7 @@ GraphicsContext3DInternal::GraphicsContext3DInternal() printf("GraphicsContext3D: OpenGL 2.0 not supported\n"); return; } - initializedGLEW = true; + s_initializedGLEW = true; } } @@ -495,7 +589,7 @@ GraphicsContext3DInternal::~GraphicsContext3DInternal() if (m_resizingBitmap) delete m_resizingBitmap; #endif -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) wglMakeCurrent(0, 0); wglDeleteContext(m_contextObj); ReleaseDC(m_canvasWindow, m_canvasDC); @@ -504,16 +598,12 @@ GraphicsContext3DInternal::~GraphicsContext3DInternal() CGLSetCurrentContext(0); CGLDestroyContext(m_contextObj); CGLDestroyPBuffer(m_pbuffer); - if (m_cgContext) - CGContextRelease(m_cgContext); if (m_renderOutput) delete[] m_renderOutput; -#elif PLATFORM(LINUX) - m_glXMakeCurrent(m_display, 0, 0); - m_glXDestroyContext(m_display, m_contextObj); - m_glXDestroyPbuffer(m_display, m_pbuffer); - XCloseDisplay(m_display); - dlclose(m_libGL); +#elif OS(LINUX) + s_gl->makeCurrent(0, 0); + s_gl->destroyContext(m_contextObj); + s_gl->destroyPbuffer(m_pbuffer); #else #error Must port to your platform #endif @@ -522,7 +612,7 @@ GraphicsContext3DInternal::~GraphicsContext3DInternal() bool GraphicsContext3DInternal::makeContextCurrent() { -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) if (wglGetCurrentContext() != m_contextObj) if (wglMakeCurrent(m_canvasDC, m_contextObj)) return true; @@ -530,9 +620,9 @@ bool GraphicsContext3DInternal::makeContextCurrent() if (CGLGetCurrentContext() != m_contextObj) if (CGLSetCurrentContext(m_contextObj) == kCGLNoError) return true; -#elif PLATFORM(LINUX) - if (m_glXGetCurrentContext() != m_contextObj) - if (m_glXMakeCurrent(m_display, m_pbuffer, m_contextObj)) +#elif OS(LINUX) + if (s_gl->getCurrentContext() != m_contextObj) + if (s_gl->makeCurrent(m_pbuffer, m_contextObj)) return true; #else #error Must port to your platform @@ -594,6 +684,7 @@ void GraphicsContext3DInternal::reshape(int width, int height) glBindTexture(target, 0); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); + m_boundFBO = m_fbo; glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthBuffer); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); @@ -618,25 +709,16 @@ void GraphicsContext3DInternal::reshape(int width, int height) #endif // FLIP_FRAMEBUFFER_VERTICALLY glClear(GL_COLOR_BUFFER_BIT); - viewportImpl(0, 0, width, height); #if PLATFORM(CG) // Need to reallocate the client-side backing store. // FIXME: make this more efficient. - if (m_cgContext) { - CGContextRelease(m_cgContext); - m_cgContext = 0; - } if (m_renderOutput) { delete[] m_renderOutput; m_renderOutput = 0; } int rowBytes = width * 4; m_renderOutput = new unsigned char[height * rowBytes]; - CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - m_cgContext = CGBitmapContextCreate(m_renderOutput, width, height, 8, rowBytes, - colorSpace, kCGImageAlphaPremultipliedLast); - CGColorSpaceRelease(colorSpace); #endif // PLATFORM(CG) } @@ -681,6 +763,9 @@ void GraphicsContext3DInternal::beginPaint(WebGLRenderingContext* context) HTMLCanvasElement* canvas = context->canvas(); ImageBuffer* imageBuffer = canvas->buffer(); unsigned char* pixels = 0; + bool mustRestoreFBO = (m_boundFBO != m_fbo); + if (mustRestoreFBO) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo); #if PLATFORM(SKIA) const SkBitmap* canvasBitmap = imageBuffer->context()->platformContext()->bitmap(); const SkBitmap* readbackBitmap = 0; @@ -721,15 +806,16 @@ void GraphicsContext3DInternal::beginPaint(WebGLRenderingContext* context) glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels); #elif PLATFORM(CG) if (m_renderOutput) { - ASSERT(CGBitmapContextGetWidth(m_cgContext) == m_cachedWidth); - ASSERT(CGBitmapContextGetHeight(m_cgContext) == m_cachedHeight); pixels = m_renderOutput; - glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels); } #else #error Must port to your platform #endif + if (mustRestoreFBO) + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO); + #ifdef FLIP_FRAMEBUFFER_VERTICALLY if (pixels) flipVertically(pixels, m_cachedWidth, m_cachedHeight); @@ -745,7 +831,20 @@ void GraphicsContext3DInternal::beginPaint(WebGLRenderingContext* context) } #elif PLATFORM(CG) if (m_renderOutput) { - CGImageRef cgImage = CGBitmapContextCreateImage(m_cgContext); + int rowBytes = m_cachedWidth * 4; + CGDataProviderRef dataProvider = CGDataProviderCreateWithData(0, m_renderOutput, rowBytes * m_cachedHeight, 0); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGImageRef cgImage = CGImageCreate(m_cachedWidth, + m_cachedHeight, + 8, + 32, + rowBytes, + colorSpace, + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, + dataProvider, + 0, + false, + kCGRenderingIntentDefault); // CSS styling may cause the canvas's content to be resized on // the page. Go back to the Canvas to figure out the correct // width and height to draw. @@ -756,9 +855,13 @@ void GraphicsContext3DInternal::beginPaint(WebGLRenderingContext* context) // rendering results. CGContextSetBlendMode(imageBuffer->context()->platformContext(), kCGBlendModeCopy); + CGContextSetInterpolationQuality(imageBuffer->context()->platformContext(), + kCGInterpolationNone); CGContextDrawImage(imageBuffer->context()->platformContext(), rect, cgImage); CGImageRelease(cgImage); + CGColorSpaceRelease(colorSpace); + CGDataProviderRelease(dataProvider); } #else #error Must port to your platform @@ -788,6 +891,17 @@ void GraphicsContext3DInternal::bindBuffer(unsigned long target, glBindBuffer(target, bufID); } +void GraphicsContext3DInternal::bindFramebuffer(unsigned long target, + WebGLFramebuffer* framebuffer) +{ + makeContextCurrent(); + GLuint id = EXTRACT(framebuffer); + if (!id) + id = m_fbo; + glBindFramebufferEXT(target, id); + m_boundFBO = id; +} + // If we didn't have to hack GL_TEXTURE_WRAP_R for cube maps, // we could just use: // GL_SAME_METHOD_2_X2(BindTexture, bindTexture, unsigned long, WebGLTexture*) @@ -864,6 +978,11 @@ unsigned long GraphicsContext3DInternal::getError() return glGetError(); } +GraphicsContext3D::Attributes GraphicsContext3DInternal::getContextAttributes() +{ + return m_attrs; +} + void GraphicsContext3DInternal::vertexAttribPointer(unsigned long indx, int size, int type, bool normalized, unsigned long stride, unsigned long offset) { @@ -1012,17 +1131,17 @@ void GraphicsContext3D::name(t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 gl##glname(a1, a2, a3, a4, a5, a6, a7, a8); \ } -PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create() +PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs) { - PassOwnPtr<GraphicsContext3D> context = new GraphicsContext3D(); + PassOwnPtr<GraphicsContext3D> context = new GraphicsContext3D(attrs); // FIXME: add error checking return context; } -GraphicsContext3D::GraphicsContext3D() +GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs) : m_currentWidth(0) , m_currentHeight(0) - , m_internal(new GraphicsContext3DInternal()) + , m_internal(new GraphicsContext3DInternal(attrs)) { } @@ -1190,7 +1309,10 @@ void GraphicsContext3D::bindBuffer(unsigned long target, m_internal->bindBuffer(target, buffer); } -GL_SAME_METHOD_2_X2(BindFramebufferEXT, bindFramebuffer, unsigned long, WebGLFramebuffer*) +void GraphicsContext3D::bindFramebuffer(unsigned long target, WebGLFramebuffer* framebuffer) +{ + m_internal->bindFramebuffer(target, framebuffer); +} GL_SAME_METHOD_2_X2(BindRenderbufferEXT, bindRenderbuffer, unsigned long, WebGLRenderbuffer*) @@ -1448,6 +1570,11 @@ void GraphicsContext3D::getBufferParameteriv(unsigned long target, unsigned long glGetBufferParameteriv(target, pname, value); } +GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes() +{ + return m_internal->getContextAttributes(); +} + unsigned long GraphicsContext3D::getError() { return m_internal->getError(); @@ -1735,7 +1862,7 @@ int GraphicsContext3D::texImage2D(unsigned target, unsigned border, unsigned format, unsigned type, - WebGLArray* pixels) + void* pixels) { // FIXME: must do validation similar to JOGL's to ensure that // the incoming array is of the appropriate length. @@ -1747,25 +1874,10 @@ int GraphicsContext3D::texImage2D(unsigned target, border, format, type, - pixels->baseAddress()); + pixels); return 0; } -int GraphicsContext3D::texImage2D(unsigned target, - unsigned level, - unsigned internalformat, - unsigned width, - unsigned height, - unsigned border, - unsigned format, - unsigned type, - ImageData* pixels) -{ - // FIXME: implement. - notImplemented(); - return -1; -} - // Remove premultiplied alpha from color channels. // FIXME: this is lossy. Must retrieve original values from HTMLImageElement. static void unmultiplyAlpha(unsigned char* rgbaData, int numPixels) @@ -1926,6 +2038,7 @@ int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, colorSpace, kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); + CGContextSetBlendMode(tmpContext, kCGBlendModeCopy); CGContextDrawImage(tmpContext, CGRectMake(0, 0, static_cast<CGFloat>(width), static_cast<CGFloat>(height)), cgImage); @@ -1939,14 +2052,6 @@ int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image, return res; } -int GraphicsContext3D::texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, - bool flipY, bool premultiplyAlpha) -{ - // FIXME: implement. - notImplemented(); - return -1; -} - GL_SAME_METHOD_3(TexParameterf, texParameterf, unsigned, unsigned, float); GL_SAME_METHOD_3(TexParameteri, texParameteri, unsigned, unsigned, int); @@ -1959,34 +2064,16 @@ int GraphicsContext3D::texSubImage2D(unsigned target, unsigned height, unsigned format, unsigned type, - WebGLArray* pixels) + void* pixels) { - // FIXME: implement. - notImplemented(); - return -1; -} - -int GraphicsContext3D::texSubImage2D(unsigned target, - unsigned level, - unsigned xoffset, - unsigned yoffset, - unsigned width, - unsigned height, - unsigned format, - unsigned type, - ImageData* pixels) -{ - // FIXME: implement. - notImplemented(); - return -1; + glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); + return 0; } int GraphicsContext3D::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - unsigned width, - unsigned height, Image* image, bool flipY, bool premultiplyAlpha) @@ -1996,21 +2083,6 @@ int GraphicsContext3D::texSubImage2D(unsigned target, return -1; } -int GraphicsContext3D::texSubImage2D(unsigned target, - unsigned level, - unsigned xoffset, - unsigned yoffset, - unsigned width, - unsigned height, - HTMLVideoElement* video, - bool flipY, - bool premultiplyAlpha) -{ - // FIXME: implement. - notImplemented(); - return -1; -} - GL_SAME_METHOD_2(Uniform1f, uniform1f, long, float) void GraphicsContext3D::uniform1fv(long location, float* v, int size) diff --git a/WebKit/chromium/src/LocalizedStrings.cpp b/WebKit/chromium/src/LocalizedStrings.cpp index 1121931..4e01848 100644 --- a/WebKit/chromium/src/LocalizedStrings.cpp +++ b/WebKit/chromium/src/LocalizedStrings.cpp @@ -165,6 +165,16 @@ String AXLinkActionVerb() return query(WebLocalizedString::AXLinkActionVerb); } +String AXMenuListPopupActionVerb() +{ + return String(); +} + +String AXMenuListActionVerb() +{ + return String(); +} + String multipleFileUploadText(unsigned numberOfFiles) { return query(WebLocalizedString::MultipleFileUploadText, numberOfFiles); diff --git a/WebKit/chromium/src/NotificationPresenterImpl.cpp b/WebKit/chromium/src/NotificationPresenterImpl.cpp index 6b22319..a38b8b5 100644 --- a/WebKit/chromium/src/NotificationPresenterImpl.cpp +++ b/WebKit/chromium/src/NotificationPresenterImpl.cpp @@ -33,12 +33,15 @@ #if ENABLE(NOTIFICATIONS) +#include "Document.h" #include "Notification.h" #include "SecurityOrigin.h" +#include "WebDocument.h" #include "WebNotification.h" #include "WebNotificationPermissionCallback.h" #include "WebNotificationPresenter.h" +#include "WebURL.h" #include <wtf/PassRefPtr.h> @@ -89,9 +92,13 @@ void NotificationPresenterImpl::notificationObjectDestroyed(Notification* notifi m_presenter->objectDestroyed(PassRefPtr<Notification>(notification)); } -NotificationPresenter::Permission NotificationPresenterImpl::checkPermission(SecurityOrigin* origin) +NotificationPresenter::Permission NotificationPresenterImpl::checkPermission(const KURL& url, Document* document) { - int result = m_presenter->checkPermission(origin->toString()); + WebDocument webDocument; + if (document) + webDocument = document; + + int result = m_presenter->checkPermission(url, document ? &webDocument : 0); return static_cast<NotificationPresenter::Permission>(result); } diff --git a/WebKit/chromium/src/NotificationPresenterImpl.h b/WebKit/chromium/src/NotificationPresenterImpl.h index 4afe9dc..8e3799c 100644 --- a/WebKit/chromium/src/NotificationPresenterImpl.h +++ b/WebKit/chromium/src/NotificationPresenterImpl.h @@ -54,7 +54,7 @@ public: virtual bool show(WebCore::Notification* object); virtual void cancel(WebCore::Notification* object); virtual void notificationObjectDestroyed(WebCore::Notification* object); - virtual WebCore::NotificationPresenter::Permission checkPermission(WebCore::SecurityOrigin* origin); + virtual WebCore::NotificationPresenter::Permission checkPermission(const WebCore::KURL& url, WebCore::Document* document); virtual void requestPermission(WebCore::SecurityOrigin* origin, WTF::PassRefPtr<WebCore::VoidCallback> callback); private: diff --git a/WebKit/chromium/src/PlatformMessagePortChannel.cpp b/WebKit/chromium/src/PlatformMessagePortChannel.cpp index f8c41d3..aa42a10 100644 --- a/WebKit/chromium/src/PlatformMessagePortChannel.cpp +++ b/WebKit/chromium/src/PlatformMessagePortChannel.cpp @@ -179,7 +179,7 @@ void PlatformMessagePortChannel::postMessageToRemote(PassOwnPtr<MessagePortChann if (!m_localPort || !m_webChannel) return; - WebString messageString = message->message()->toString(); + WebString messageString = message->message()->toWireString(); OwnPtr<WebCore::MessagePortChannelArray> channels = message->channels(); WebMessagePortChannelArray* webChannels = 0; if (channels.get() && channels->size()) { @@ -211,7 +211,7 @@ bool PlatformMessagePortChannel::tryGetMessageFromRemote(OwnPtr<MessagePortChann (*channels)[i] = MessagePortChannel::create(platformChannel); } } - RefPtr<SerializedScriptValue> serializedMessage = SerializedScriptValue::create(message); + RefPtr<SerializedScriptValue> serializedMessage = SerializedScriptValue::createFromWire(message); result = MessagePortChannel::EventData::create(serializedMessage.release(), channels.release()); } diff --git a/WebKit/chromium/src/ProfilerAgent.h b/WebKit/chromium/src/ProfilerAgent.h new file mode 100644 index 0000000..52337b8 --- /dev/null +++ b/WebKit/chromium/src/ProfilerAgent.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 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 ProfilerAgent_h +#define ProfilerAgent_h + +#include "DevToolsRPC.h" + +namespace WebKit { + +// Profiler agent provides API for retrieving profiler data. +// These methods are handled on the IO thread, so profiler can +// operate while a script on a page performs heavy work. +#define PROFILER_AGENT_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4, METHOD5) \ + /* Requests current profiler state. */ \ + METHOD0(getActiveProfilerModules) \ + \ + /* Retrieves portion of profiler log. */ \ + METHOD1(getLogLines, int /* position */) + +DEFINE_RPC_CLASS(ProfilerAgent, PROFILER_AGENT_STRUCT) + +#define PROFILER_AGENT_DELEGATE_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4, METHOD5) \ + /* Response to getActiveProfilerModules. */ \ + METHOD1(didGetActiveProfilerModules, int /* flags */) \ + \ + /* Response to getLogLines. */ \ + METHOD2(didGetLogLines, int /* position */, String /* log */) + +DEFINE_RPC_CLASS(ProfilerAgentDelegate, PROFILER_AGENT_DELEGATE_STRUCT) + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/ProfilerAgentImpl.cpp b/WebKit/chromium/src/ProfilerAgentImpl.cpp new file mode 100644 index 0000000..07570df --- /dev/null +++ b/WebKit/chromium/src/ProfilerAgentImpl.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 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 "ProfilerAgentImpl.h" + +#include <v8.h> + +namespace WebKit { + +void ProfilerAgentImpl::getActiveProfilerModules() +{ + m_delegate->didGetActiveProfilerModules(v8::V8::GetActiveProfilerModules()); +} + +void ProfilerAgentImpl::getLogLines(int position) +{ + static char buffer[65536]; + const int readSize = v8::V8::GetLogLines(position, buffer, sizeof(buffer) - 1); + buffer[readSize] = '\0'; + position += readSize; + m_delegate->didGetLogLines(position, buffer); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/ProfilerAgentImpl.h b/WebKit/chromium/src/ProfilerAgentImpl.h new file mode 100644 index 0000000..d38f57c --- /dev/null +++ b/WebKit/chromium/src/ProfilerAgentImpl.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 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 ProfilerAgentImpl_h +#define ProfilerAgentImpl_h + +#include "ProfilerAgent.h" + +namespace WebKit { + +class ProfilerAgentImpl : public ProfilerAgent { +public: + ProfilerAgentImpl(ProfilerAgentDelegate* delegate) : m_delegate(delegate) { } + virtual ~ProfilerAgentImpl() { } + + // ProfilerAgent implementation. + + // This method is called on IO thread. + virtual void getActiveProfilerModules(); + + // This method is called on IO thread. + virtual void getLogLines(int position); + +private: + ProfilerAgentDelegate* m_delegate; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/SharedWorkerRepository.cpp b/WebKit/chromium/src/SharedWorkerRepository.cpp index 5e5bc46..c803aac 100644 --- a/WebKit/chromium/src/SharedWorkerRepository.cpp +++ b/WebKit/chromium/src/SharedWorkerRepository.cpp @@ -61,26 +61,30 @@ using WebKit::WebSharedWorker; using WebKit::WebSharedWorkerRepository; // Callback class that keeps the SharedWorker and WebSharedWorker objects alive while loads are potentially happening, and also translates load errors into error events on the worker. -class SharedWorkerScriptLoader : private WorkerScriptLoaderClient, private WebSharedWorker::ConnectListener, private ActiveDOMObject { +class SharedWorkerScriptLoader : private WorkerScriptLoaderClient, private WebSharedWorker::ConnectListener { public: SharedWorkerScriptLoader(PassRefPtr<SharedWorker> worker, const KURL& url, const String& name, PassOwnPtr<MessagePortChannel> port, PassOwnPtr<WebSharedWorker> webWorker) - : ActiveDOMObject(worker->scriptExecutionContext(), this) - , m_worker(worker) + : m_worker(worker) , m_url(url) , m_name(name) , m_webWorker(webWorker) , m_port(port) + , m_loading(false) { } + ~SharedWorkerScriptLoader(); void load(); - virtual void contextDestroyed(); + static void stopAllLoadersForContext(ScriptExecutionContext*); + private: // WorkerScriptLoaderClient callback virtual void notifyFinished(); virtual void connected(); + const ScriptExecutionContext* loadingContext() { return m_worker->scriptExecutionContext(); } + void sendConnect(); RefPtr<SharedWorker> m_worker; @@ -89,15 +93,47 @@ private: OwnPtr<WebSharedWorker> m_webWorker; OwnPtr<MessagePortChannel> m_port; WorkerScriptLoader m_scriptLoader; + bool m_loading; }; +static Vector<SharedWorkerScriptLoader*>& pendingLoaders() +{ + AtomicallyInitializedStatic(Vector<SharedWorkerScriptLoader*>&, loaders = *new Vector<SharedWorkerScriptLoader*>); + return loaders; +} + +void SharedWorkerScriptLoader::stopAllLoadersForContext(ScriptExecutionContext* context) +{ + // Walk our list of pending loaders and shutdown any that belong to this context. + Vector<SharedWorkerScriptLoader*>& loaders = pendingLoaders(); + for (unsigned i = 0; i < loaders.size(); ) { + SharedWorkerScriptLoader* loader = loaders[i]; + if (context == loader->loadingContext()) { + loaders.remove(i); + delete loader; + } else + i++; + } +} + +SharedWorkerScriptLoader::~SharedWorkerScriptLoader() +{ + if (m_loading) + m_worker->unsetPendingActivity(m_worker.get()); +} + void SharedWorkerScriptLoader::load() { + ASSERT(!m_loading); // If the shared worker is not yet running, load the script resource for it, otherwise just send it a connect event. if (m_webWorker->isStarted()) sendConnect(); - else + else { m_scriptLoader.loadAsynchronously(m_worker->scriptExecutionContext(), m_url, DenyCrossOriginRequests, this); + // Keep the worker + JS wrapper alive until the resource load is complete in case we need to dispatch an error event. + m_worker->setPendingActivity(m_worker.get()); + m_loading = true; + } } // Extracts a WebMessagePortChannel from a MessagePortChannel. @@ -128,12 +164,6 @@ void SharedWorkerScriptLoader::sendConnect() m_webWorker->connect(getWebPort(m_port.release()), this); } -void SharedWorkerScriptLoader::contextDestroyed() -{ - ActiveDOMObject::contextDestroyed(); - delete this; -} - void SharedWorkerScriptLoader::connected() { // Connect event has been sent, so free ourselves (this releases the SharedWorker so it can be freed as well if unreferenced). @@ -185,6 +215,10 @@ void SharedWorkerRepository::documentDetached(Document* document) WebSharedWorkerRepository* repo = WebKit::webKitClient()->sharedWorkerRepository(); if (repo) repo->documentDetached(getId(document)); + + // Stop the creation of any pending SharedWorkers for this context. + // FIXME: Need a way to invoke this for WorkerContexts as well when we support for nested workers. + SharedWorkerScriptLoader::stopAllLoadersForContext(document); } bool SharedWorkerRepository::hasSharedWorkers(Document* document) diff --git a/WebKit/chromium/src/StorageAreaProxy.cpp b/WebKit/chromium/src/StorageAreaProxy.cpp index 551507f..c9185fe 100644 --- a/WebKit/chromium/src/StorageAreaProxy.cpp +++ b/WebKit/chromium/src/StorageAreaProxy.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Google Inc. All Rights Reserved. + * (C) 2008 Apple Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,11 +29,16 @@ #if ENABLE(DOM_STORAGE) +#include "DOMWindow.h" #include "Document.h" +#include "EventNames.h" #include "ExceptionCode.h" #include "Frame.h" +#include "Page.h" +#include "PageGroup.h" #include "SecurityOrigin.h" #include "StorageAreaImpl.h" +#include "StorageEvent.h" #include "WebStorageArea.h" #include "WebString.h" @@ -40,8 +46,9 @@ namespace WebCore { -StorageAreaProxy::StorageAreaProxy(WebKit::WebStorageArea* storageArea) +StorageAreaProxy::StorageAreaProxy(WebKit::WebStorageArea* storageArea, StorageType storageType) : m_storageArea(storageArea) + , m_storageType(storageType) { } @@ -64,21 +71,34 @@ String StorageAreaProxy::getItem(const String& key) const return m_storageArea->getItem(key); } -void StorageAreaProxy::setItem(const String& key, const String& value, ExceptionCode& ec, Frame* frame) +String StorageAreaProxy::setItem(const String& key, const String& value, ExceptionCode& ec, Frame* frame) { bool quotaException = false; - m_storageArea->setItem(key, value, frame->document()->url(), quotaException); + WebKit::WebString oldValue; + m_storageArea->setItem(key, value, frame->document()->url(), quotaException, oldValue); ec = quotaException ? QUOTA_EXCEEDED_ERR : 0; + String oldValueString = oldValue; + if (oldValueString != value) + storageEvent(key, oldValue, value, m_storageType, frame->document()->securityOrigin(), frame); + return oldValue; } -void StorageAreaProxy::removeItem(const String& key, Frame* frame) +String StorageAreaProxy::removeItem(const String& key, Frame* frame) { - m_storageArea->removeItem(key, frame->document()->url()); + WebKit::WebString oldValue; + m_storageArea->removeItem(key, frame->document()->url(), oldValue); + if (!oldValue.isNull()) + storageEvent(key, oldValue, String(), m_storageType, frame->document()->securityOrigin(), frame); + return oldValue; } -void StorageAreaProxy::clear(Frame* frame) +bool StorageAreaProxy::clear(Frame* frame) { - m_storageArea->clear(frame->document()->url()); + bool clearedSomething; + m_storageArea->clear(frame->document()->url(), clearedSomething); + if (clearedSomething) + storageEvent(String(), String(), String(), m_storageType, frame->document()->securityOrigin(), frame); + return clearedSomething; } bool StorageAreaProxy::contains(const String& key) const @@ -86,6 +106,41 @@ bool StorageAreaProxy::contains(const String& key) const return !getItem(key).isNull(); } +// Copied from WebCore/storage/StorageEventDispatcher.cpp out of necessity. It's probably best to keep it current. +void StorageAreaProxy::storageEvent(const String& key, const String& oldValue, const String& newValue, StorageType storageType, SecurityOrigin* securityOrigin, Frame* sourceFrame) +{ + Page* page = sourceFrame->page(); + if (!page) + return; + + // We need to copy all relevant frames from every page to a vector since sending the event to one frame might mutate the frame tree + // of any given page in the group or mutate the page group itself. + Vector<RefPtr<Frame> > frames; + if (storageType == SessionStorage) { + // Send events only to our page. + for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (sourceFrame != frame && frame->document()->securityOrigin()->equal(securityOrigin)) + frames.append(frame); + } + + for (unsigned i = 0; i < frames.size(); ++i) + frames[i]->document()->enqueueStorageEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->url(), frames[i]->domWindow()->sessionStorage())); + } else { + // Send events to every page. + const HashSet<Page*>& pages = page->group().pages(); + HashSet<Page*>::const_iterator end = pages.end(); + for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) { + for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (sourceFrame != frame && frame->document()->securityOrigin()->equal(securityOrigin)) + frames.append(frame); + } + } + + for (unsigned i = 0; i < frames.size(); ++i) + frames[i]->document()->enqueueStorageEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->url(), frames[i]->domWindow()->localStorage())); + } +} + } // namespace WebCore #endif // ENABLE(DOM_STORAGE) diff --git a/WebKit/chromium/src/StorageAreaProxy.h b/WebKit/chromium/src/StorageAreaProxy.h index 5d09d82..b169828 100644 --- a/WebKit/chromium/src/StorageAreaProxy.h +++ b/WebKit/chromium/src/StorageAreaProxy.h @@ -34,22 +34,28 @@ namespace WebKit { class WebStorageArea; } namespace WebCore { +class Frame; +class SecurityOrigin; + class StorageAreaProxy : public StorageArea { public: - StorageAreaProxy(WebKit::WebStorageArea* storageArea); + StorageAreaProxy(WebKit::WebStorageArea*, StorageType); virtual ~StorageAreaProxy(); // The HTML5 DOM Storage API virtual unsigned length() const; virtual String key(unsigned index) const; virtual String getItem(const String& key) const; - virtual void setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame); - virtual void removeItem(const String& key, Frame* sourceFrame); - virtual void clear(Frame* sourceFrame); + virtual String setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame); + virtual String removeItem(const String& key, Frame* sourceFrame); + virtual bool clear(Frame* sourceFrame); virtual bool contains(const String& key) const; private: + void storageEvent(const String& key, const String& oldValue, const String& newValue, StorageType, SecurityOrigin*, Frame* sourceFrame); + OwnPtr<WebKit::WebStorageArea> m_storageArea; + StorageType m_storageType; }; } // namespace WebCore diff --git a/WebKit/chromium/src/StorageNamespaceProxy.cpp b/WebKit/chromium/src/StorageNamespaceProxy.cpp index e22bbef..1be1967 100644 --- a/WebKit/chromium/src/StorageNamespaceProxy.cpp +++ b/WebKit/chromium/src/StorageNamespaceProxy.cpp @@ -28,27 +28,35 @@ #if ENABLE(DOM_STORAGE) +#include "Chrome.h" +#include "ChromeClientImpl.h" +#include "Page.h" #include "SecurityOrigin.h" #include "StorageAreaProxy.h" #include "WebKit.h" #include "WebKitClient.h" #include "WebStorageNamespace.h" #include "WebString.h" +#include "WebViewClient.h" +#include "WebViewImpl.h" namespace WebCore { PassRefPtr<StorageNamespace> StorageNamespace::localStorageNamespace(const String& path, unsigned quota) { - return new StorageNamespaceProxy(WebKit::webKitClient()->createLocalStorageNamespace(path, quota)); + return adoptRef(new StorageNamespaceProxy(WebKit::webKitClient()->createLocalStorageNamespace(path, quota), LocalStorage)); } -PassRefPtr<StorageNamespace> StorageNamespace::sessionStorageNamespace() +PassRefPtr<StorageNamespace> StorageNamespace::sessionStorageNamespace(Page* page) { - return new StorageNamespaceProxy(WebKit::webKitClient()->createSessionStorageNamespace()); + WebKit::ChromeClientImpl* chromeClientImpl = static_cast<WebKit::ChromeClientImpl*>(page->chrome()->client()); + WebKit::WebViewClient* webViewClient = chromeClientImpl->webView()->client(); + return adoptRef(new StorageNamespaceProxy(webViewClient->createSessionStorageNamespace(), SessionStorage)); } -StorageNamespaceProxy::StorageNamespaceProxy(WebKit::WebStorageNamespace* storageNamespace) +StorageNamespaceProxy::StorageNamespaceProxy(WebKit::WebStorageNamespace* storageNamespace, StorageType storageType) : m_storageNamespace(storageNamespace) + , m_storageType(storageType) { } @@ -58,12 +66,19 @@ StorageNamespaceProxy::~StorageNamespaceProxy() PassRefPtr<StorageNamespace> StorageNamespaceProxy::copy() { - return adoptRef(new StorageNamespaceProxy(m_storageNamespace->copy())); + ASSERT(m_storageType == SessionStorage); + // The WebViewClient knows what its session storage namespace id is but we + // do not. Returning 0 here causes it to be fetched (via the WebViewClient) + // on its next use. Note that it is WebViewClient::createView's + // responsibility to clone the session storage namespace id and that the + // only time copy() is called is directly after the createView call...which + // is why all of this is safe. + return 0; } PassRefPtr<StorageArea> StorageNamespaceProxy::storageArea(PassRefPtr<SecurityOrigin> origin) { - return adoptRef(new StorageAreaProxy(m_storageNamespace->createStorageArea(origin->toString()))); + return adoptRef(new StorageAreaProxy(m_storageNamespace->createStorageArea(origin->toString()), m_storageType)); } void StorageNamespaceProxy::close() diff --git a/WebKit/chromium/src/StorageNamespaceProxy.h b/WebKit/chromium/src/StorageNamespaceProxy.h index 9ff624b..28d7a23 100644 --- a/WebKit/chromium/src/StorageNamespaceProxy.h +++ b/WebKit/chromium/src/StorageNamespaceProxy.h @@ -28,6 +28,7 @@ #if ENABLE(DOM_STORAGE) +#include "StorageArea.h" #include "StorageNamespace.h" namespace WebKit { class WebStorageNamespace; } @@ -36,7 +37,7 @@ namespace WebCore { class StorageNamespaceProxy : public StorageNamespace { public: - StorageNamespaceProxy(WebKit::WebStorageNamespace* storageNamespace); + StorageNamespaceProxy(WebKit::WebStorageNamespace*, StorageType); virtual ~StorageNamespaceProxy(); virtual PassRefPtr<StorageArea> storageArea(PassRefPtr<SecurityOrigin>); virtual PassRefPtr<StorageNamespace> copy(); @@ -45,6 +46,7 @@ public: private: OwnPtr<WebKit::WebStorageNamespace> m_storageNamespace; + StorageType m_storageType; }; } // namespace WebCore diff --git a/WebKit/chromium/src/SuggestionsPopupMenuClient.cpp b/WebKit/chromium/src/SuggestionsPopupMenuClient.cpp new file mode 100644 index 0000000..b4a77a3 --- /dev/null +++ b/WebKit/chromium/src/SuggestionsPopupMenuClient.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2010 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 "SuggestionsPopupMenuClient.h" + +#include "CSSStyleSelector.h" +#include "CSSValueKeywords.h" +#include "Chrome.h" +#include "FrameView.h" +#include "HTMLInputElement.h" +#include "RenderTheme.h" +#include "WebViewImpl.h" + +using namespace WebCore; + +namespace WebKit { + +SuggestionsPopupMenuClient::SuggestionsPopupMenuClient() + : m_textField(0) + , m_selectedIndex(0) +{ +} + +SuggestionsPopupMenuClient::~SuggestionsPopupMenuClient() +{ +} + +// FIXME: Implement this per-derived class? +void SuggestionsPopupMenuClient::valueChanged(unsigned listIndex, bool fireEvents) +{ + m_textField->setValue(getSuggestion(listIndex)); + + WebViewImpl* webView = getWebView(); + if (!webView) + return; + + EditorClientImpl* editor = + static_cast<EditorClientImpl*>(webView->page()->editorClient()); + ASSERT(editor); + editor->onAutofillSuggestionAccepted( + static_cast<HTMLInputElement*>(m_textField.get())); +} + +String SuggestionsPopupMenuClient::itemText(unsigned listIndex) const +{ + return getSuggestion(listIndex); +} + +PopupMenuStyle SuggestionsPopupMenuClient::itemStyle(unsigned listIndex) const +{ + return *m_style; +} + +PopupMenuStyle SuggestionsPopupMenuClient::menuStyle() const +{ + return *m_style; +} + +int SuggestionsPopupMenuClient::clientPaddingLeft() const +{ + // Bug http://crbug.com/7708 seems to indicate the style can be 0. + RenderStyle* style = textFieldStyle(); + if (!style) + return 0; + + return RenderTheme::defaultTheme()->popupInternalPaddingLeft(style); +} + +int SuggestionsPopupMenuClient::clientPaddingRight() const +{ + // Bug http://crbug.com/7708 seems to indicate the style can be 0. + RenderStyle* style = textFieldStyle(); + if (!style) + return 0; + + return RenderTheme::defaultTheme()->popupInternalPaddingRight(style); +} + +void SuggestionsPopupMenuClient::popupDidHide() +{ + WebViewImpl* webView = getWebView(); + if (webView) + webView->suggestionsPopupDidHide(); +} + +void SuggestionsPopupMenuClient::setTextFromItem(unsigned listIndex) +{ + m_textField->setValue(getSuggestion(listIndex)); +} + +FontSelector* SuggestionsPopupMenuClient::fontSelector() const +{ + return m_textField->document()->styleSelector()->fontSelector(); +} + +HostWindow* SuggestionsPopupMenuClient::hostWindow() const +{ + return m_textField->document()->view()->hostWindow(); +} + +PassRefPtr<Scrollbar> SuggestionsPopupMenuClient::createScrollbar( + ScrollbarClient* client, + ScrollbarOrientation orientation, + ScrollbarControlSize size) +{ + return Scrollbar::createNativeScrollbar(client, orientation, size); +} + +RenderStyle* SuggestionsPopupMenuClient::textFieldStyle() const +{ + RenderStyle* style = m_textField->computedStyle(); + if (!style) { + // It seems we can only have a 0 style in a TextField if the + // node is detached, 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; +} + +void SuggestionsPopupMenuClient::initialize(HTMLInputElement* textField, + int defaultSuggestionIndex) +{ + m_textField = textField; + m_selectedIndex = defaultSuggestionIndex; + + FontDescription fontDescription; + RenderTheme::defaultTheme()->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())); +} + +WebViewImpl* SuggestionsPopupMenuClient::getWebView() const +{ + Frame* frame = m_textField->document()->frame(); + if (!frame) + return 0; + + Page* page = frame->page(); + if (!page) + return 0; + + return static_cast<ChromeClientImpl*>(page->chrome()->client())->webView(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/SuggestionsPopupMenuClient.h b/WebKit/chromium/src/SuggestionsPopupMenuClient.h new file mode 100644 index 0000000..edc4c09 --- /dev/null +++ b/WebKit/chromium/src/SuggestionsPopupMenuClient.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 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" + +#ifndef SuggestionsPopupMenuClient_h +#define SuggestionsPopupMenuClient_h + +namespace WebCore { +class HTMLInputElement; +class PopupMenuStyle; +class RenderStyle; +} + +namespace WebKit { +class WebString; +class WebViewImpl; +template <typename T> class WebVector; + +// The Suggestions popup menu client, used to display a list of suggestions. +class SuggestionsPopupMenuClient : public WebCore::PopupMenuClient { +public: + SuggestionsPopupMenuClient(); + virtual ~SuggestionsPopupMenuClient(); + + // Returns the number of suggestions available. + virtual unsigned getSuggestionsCount() const = 0; + + // Returns the suggestion at |listIndex|. + virtual WebString getSuggestion(unsigned listIndex) const = 0; + + // Removes the suggestion at |listIndex| from the list of suggestions. + virtual void removeSuggestionAtIndex(unsigned listIndex) = 0; + + // 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 getSuggestionsCount(); } + 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); + +protected: + void initialize(WebCore::HTMLInputElement* textField, + int defaultSuggestionIndex); + + int getSelectedIndex() const { return m_selectedIndex; } + void setSelectedIndex(int index) { m_selectedIndex = index; } + + WebViewImpl* getWebView() const; + WebCore::HTMLInputElement* getTextField() const { return m_textField.get(); } + +private: + WebCore::RenderStyle* textFieldStyle() const; + + RefPtr<WebCore::HTMLInputElement> m_textField; + int m_selectedIndex; + OwnPtr<WebCore::PopupMenuStyle> m_style; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/ToolsAgent.h b/WebKit/chromium/src/ToolsAgent.h new file mode 100644 index 0000000..ab48153 --- /dev/null +++ b/WebKit/chromium/src/ToolsAgent.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 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 ToolsAgent_h +#define ToolsAgent_h + +#include "DevToolsRPC.h" + +namespace WebKit { + +// Tools agent provides API for enabling / disabling other agents as well as +// API for auxiliary UI functions such as dom elements highlighting. +#define TOOLS_AGENT_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4, METHOD5) \ + /* Dispatches given function on the InspectorController object */ \ + METHOD3(dispatchOnInspectorController, int /* call_id */, \ + String /* function_name */, String /* json_args */) \ + \ + /* Dispatches given function on the InjectedScript object */ \ + METHOD5(dispatchOnInjectedScript, int /* call_id */, \ + int /* injected_script_id */, String /* function_name */, \ + String /* json_args */, bool /* async */) + +DEFINE_RPC_CLASS(ToolsAgent, TOOLS_AGENT_STRUCT) + +#define TOOLS_AGENT_DELEGATE_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4, METHOD5) \ + /* Updates focused node on the client. */ \ + METHOD1(frameNavigate, String /* url */) \ + \ + /* Response to the DispatchOn*. */ \ + METHOD3(didDispatchOn, int /* call_id */, String /* result */, String /* exception */) \ + \ + /* Sends InspectorFrontend message to be dispatched on client. */ \ + METHOD1(dispatchOnClient, String /* data */) + +DEFINE_RPC_CLASS(ToolsAgentDelegate, TOOLS_AGENT_DELEGATE_STRUCT) + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/WebAccessibilityObject.cpp b/WebKit/chromium/src/WebAccessibilityObject.cpp index bdbd260..c386d44 100644 --- a/WebKit/chromium/src/WebAccessibilityObject.cpp +++ b/WebKit/chromium/src/WebAccessibilityObject.cpp @@ -231,13 +231,13 @@ bool WebAccessibilityObject::isIndeterminate() const return m_private->isIndeterminate(); } -bool WebAccessibilityObject::isMultiSelect() const +bool WebAccessibilityObject::isMultiSelectable() const { if (!m_private) return 0; m_private->updateBackingStore(); - return m_private->isMultiSelect(); + return m_private->isMultiSelectable(); } bool WebAccessibilityObject::isOffScreen() const diff --git a/WebKit/chromium/src/WebAnimationControllerImpl.cpp b/WebKit/chromium/src/WebAnimationControllerImpl.cpp new file mode 100644 index 0000000..32a7a61 --- /dev/null +++ b/WebKit/chromium/src/WebAnimationControllerImpl.cpp @@ -0,0 +1,90 @@ +/* + * 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 "WebAnimationControllerImpl.h" + +#include "AnimationController.h" +#include "Element.h" + +#include "WebElement.h" +#include "WebFrameImpl.h" +#include "WebString.h" + +using namespace WebCore; + +namespace WebKit { + +WebAnimationControllerImpl::WebAnimationControllerImpl(WebFrameImpl* frameImpl) + : m_frameImpl(frameImpl) +{ + ASSERT(m_frameImpl); +} + +AnimationController* WebAnimationControllerImpl::animationController() const +{ + if (!m_frameImpl->frame()) + return 0; + return m_frameImpl->frame()->animation(); +} + +bool WebAnimationControllerImpl::pauseAnimationAtTime(WebElement& element, + const WebString& animationName, + double time) +{ + AnimationController* controller = animationController(); + if (!controller) + return 0; + return controller->pauseAnimationAtTime(PassRefPtr<Element>(element)->renderer(), + animationName, + time); +} + +bool WebAnimationControllerImpl::pauseTransitionAtTime(WebElement& element, + const WebString& propertyName, + double time) +{ + AnimationController* controller = animationController(); + if (!controller) + return 0; + return controller->pauseTransitionAtTime(PassRefPtr<Element>(element)->renderer(), + propertyName, + time); +} + +unsigned WebAnimationControllerImpl::numberOfActiveAnimations() const +{ + AnimationController* controller = animationController(); + if (!controller) + return 0; + return controller->numberOfActiveAnimations(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebAnimationControllerImpl.h b/WebKit/chromium/src/WebAnimationControllerImpl.h new file mode 100644 index 0000000..8b0676e --- /dev/null +++ b/WebKit/chromium/src/WebAnimationControllerImpl.h @@ -0,0 +1,64 @@ +/* + * 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 WebAnimationControllerImpl_h +#define WebAnimationControllerImpl_h + +// FIXME: This relative path is a temporary hack to support using this +// header from webkit/glue. +#include "../public/WebAnimationController.h" + +namespace WebCore { +class AnimationController; +} + +namespace WebKit { +class WebFrameImpl; + +class WebAnimationControllerImpl : public WebAnimationController { +public: + explicit WebAnimationControllerImpl(WebFrameImpl*); + virtual ~WebAnimationControllerImpl() { } + + virtual bool pauseAnimationAtTime(WebElement&, + const WebString& animationName, + double time); + virtual bool pauseTransitionAtTime(WebElement&, + const WebString& propertyName, + double time); + virtual unsigned numberOfActiveAnimations() const; +private: + WebFrameImpl* m_frameImpl; + WebCore::AnimationController* animationController() const; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/WebBindings.cpp b/WebKit/chromium/src/WebBindings.cpp index 0bc67b6..04f2f85 100644 --- a/WebKit/chromium/src/WebBindings.cpp +++ b/WebKit/chromium/src/WebBindings.cpp @@ -44,9 +44,12 @@ #include "MouseEvent.h" #include "NPV8Object.h" // for PrivateIdentifier #include "Range.h" +#include "V8BindingState.h" #include "V8DOMWrapper.h" +#include "V8Event.h" #include "V8Helpers.h" #include "V8Proxy.h" +#include "V8Range.h" #elif USE(JSC) #include "bridge/c/c_utility.h" #endif @@ -223,20 +226,20 @@ static bool getDragDataImpl(NPObject* npobj, int* eventId, WebDragData* data) // Get the current WebCore event. v8::Handle<v8::Value> currentEvent(getEvent(context)); - Event* event = V8DOMWrapper::convertToNativeEvent(currentEvent); + Event* event = V8Event::toNative(v8::Handle<v8::Object>::Cast(currentEvent)); if (!event) return false; // Check that the given npobj is that event. V8NPObject* object = reinterpret_cast<V8NPObject*>(npobj); - Event* given = V8DOMWrapper::convertToNativeEvent(object->v8Object); + Event* given = V8Event::toNative(object->v8Object); if (given != event) return false; // Check the execution frames are same origin. V8Proxy* current = V8Proxy::retrieve(V8Proxy::retrieveFrameForCurrentContext()); Frame* frame = V8Proxy::retrieveFrame(context); - if (!current || !current->canAccessFrame(frame, false)) + if (!current || !V8BindingSecurity::canAccessFrame(V8BindingState::Only(), frame, false)) return false; const EventNames& names(eventNames()); @@ -284,7 +287,7 @@ static bool getRangeImpl(NPObject* npobj, WebRange* range) if (V8ClassIndex::RANGE != V8DOMWrapper::domWrapperType(v8object)) return false; - Range* native = V8DOMWrapper::convertToNativeObject<WebCore::Range>(V8ClassIndex::RANGE, v8object); + Range* native = V8Range::toNative(v8object); if (!native) return false; diff --git a/WebKit/chromium/src/WebDataSourceImpl.cpp b/WebKit/chromium/src/WebDataSourceImpl.cpp index 456706a..5a315cf 100644 --- a/WebKit/chromium/src/WebDataSourceImpl.cpp +++ b/WebKit/chromium/src/WebDataSourceImpl.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "WebDataSourceImpl.h" +#include "ApplicationCacheHostInternal.h" #include "WebURL.h" #include "WebURLError.h" #include "WebVector.h" @@ -108,6 +109,14 @@ void WebDataSourceImpl::setExtraData(ExtraData* extraData) m_extraData.set(extraData); } +WebApplicationCacheHost* WebDataSourceImpl::applicationCacheHost() { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + return ApplicationCacheHostInternal::toWebApplicationCacheHost(DocumentLoader::applicationCacheHost()); +#else + return 0; +#endif +} + WebNavigationType WebDataSourceImpl::toWebNavigationType(NavigationType type) { switch (type) { diff --git a/WebKit/chromium/src/WebDataSourceImpl.h b/WebKit/chromium/src/WebDataSourceImpl.h index 8860fe4..f868e95 100644 --- a/WebKit/chromium/src/WebDataSourceImpl.h +++ b/WebKit/chromium/src/WebDataSourceImpl.h @@ -46,7 +46,6 @@ #include <wtf/PassOwnPtr.h> #include <wtf/Vector.h> - namespace WebKit { class WebPluginLoadObserver; @@ -73,6 +72,7 @@ public: virtual double triggeringEventTime() const; virtual ExtraData* extraData() const; virtual void setExtraData(ExtraData*); + virtual WebApplicationCacheHost* applicationCacheHost(); static WebNavigationType toWebNavigationType(WebCore::NavigationType type); diff --git a/WebKit/chromium/src/WebDatabase.cpp b/WebKit/chromium/src/WebDatabase.cpp index 2cd36b6..50b9220 100644 --- a/WebKit/chromium/src/WebDatabase.cpp +++ b/WebKit/chromium/src/WebDatabase.cpp @@ -32,7 +32,9 @@ #include "WebDatabase.h" #include "Database.h" +#include "DatabaseTask.h" #include "DatabaseThread.h" +#include "DatabaseTracker.h" #include "Document.h" #include "KURL.h" #include "QuotaTracker.h" @@ -106,6 +108,22 @@ void WebDatabase::updateDatabaseSize( originIdentifier, databaseName, databaseSize, spaceAvailable); } +void WebDatabase::closeDatabaseImmediately(const WebString& originIdentifier, const WebString& databaseName) +{ + HashSet<RefPtr<Database> > databaseHandles; + PassRefPtr<SecurityOrigin> originPrp(*WebSecurityOrigin::createFromDatabaseIdentifier(originIdentifier)); + RefPtr<SecurityOrigin> origin = originPrp; + DatabaseTracker::tracker().getOpenDatabases(origin.get(), databaseName, &databaseHandles); + for (HashSet<RefPtr<Database> >::iterator it = databaseHandles.begin(); it != databaseHandles.end(); ++it) { + Database* database = it->get(); + DatabaseThread* databaseThread = database->scriptExecutionContext()->databaseThread(); + if (databaseThread && !databaseThread->terminationRequested()) { + database->stop(); + databaseThread->scheduleTask(DatabaseCloseTask::create(database, 0)); + } + } +} + WebDatabase::WebDatabase(const WTF::PassRefPtr<Database>& database) : m_private(static_cast<WebDatabasePrivate*>(database.releaseRef())) { diff --git a/WebKit/chromium/src/WebDevToolsAgentImpl.cpp b/WebKit/chromium/src/WebDevToolsAgentImpl.cpp new file mode 100644 index 0000000..9ce35b4 --- /dev/null +++ b/WebKit/chromium/src/WebDevToolsAgentImpl.cpp @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2010 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 "WebDevToolsAgentImpl.h" + +#include "BoundObject.h" +#include "DebuggerAgentImpl.h" +#include "DebuggerAgentManager.h" +#include "Document.h" +#include "EventListener.h" +#include "InjectedScriptHost.h" +#include "InspectorBackend.h" +#include "InspectorController.h" +#include "InspectorFrontend.h" +#include "InspectorResource.h" +#include "Node.h" +#include "Page.h" +#include "PlatformString.h" +#include "ProfilerAgentImpl.h" +#include "ResourceError.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "ScriptObject.h" +#include "ScriptState.h" +#include "ScriptValue.h" +#include "V8Binding.h" +#include "V8InspectorBackend.h" +#include "V8Proxy.h" +#include "V8Utilities.h" +#include "WebDataSource.h" +#include "WebDevToolsAgentClient.h" +#include "WebDevToolsMessageData.h" +#include "WebFrameImpl.h" +#include "WebString.h" +#include "WebURL.h" +#include "WebURLError.h" +#include "WebURLRequest.h" +#include "WebURLResponse.h" +#include "WebViewImpl.h" +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> + +using WebCore::Document; +using WebCore::DocumentLoader; +using WebCore::FrameLoader; +using WebCore::InjectedScriptHost; +using WebCore::InspectorBackend; +using WebCore::InspectorController; +using WebCore::InspectorFrontend; +using WebCore::InspectorResource; +using WebCore::Node; +using WebCore::Page; +using WebCore::ResourceError; +using WebCore::ResourceRequest; +using WebCore::ResourceResponse; +using WebCore::SafeAllocation; +using WebCore::ScriptObject; +using WebCore::ScriptState; +using WebCore::ScriptValue; +using WebCore::String; +using WebCore::V8ClassIndex; +using WebCore::V8DOMWrapper; +using WebCore::V8InspectorBackend; +using WebCore::V8Proxy; + +namespace WebKit { + +namespace { + +void InspectorBackendWeakReferenceCallback(v8::Persistent<v8::Value> object, void* parameter) +{ + InspectorBackend* backend = static_cast<InspectorBackend*>(parameter); + backend->deref(); + object.Dispose(); +} + +void SetApuAgentEnabledInUtilityContext(v8::Handle<v8::Context> context, bool enabled) +{ + v8::HandleScope handleScope; + v8::Context::Scope contextScope(context); + v8::Handle<v8::Object> dispatcher = v8::Local<v8::Object>::Cast( + context->Global()->Get(v8::String::New("ApuAgentDispatcher"))); + if (dispatcher.IsEmpty()) + return; + dispatcher->Set(v8::String::New("enabled"), v8::Boolean::New(enabled)); +} + +// TODO(pfeldman): Make this public in WebDevToolsAgent API. +static const char kApuAgentFeatureName[] = "apu-agent"; + +// Keep these in sync with the ones in inject_dispatch.js. +static const char kTimelineFeatureName[] = "timeline-profiler"; +static const char kResourceTrackingFeatureName[] = "resource-tracking"; + +class IORPCDelegate : public DevToolsRPC::Delegate, public Noncopyable { +public: + IORPCDelegate() { } + virtual ~IORPCDelegate() { } + virtual void sendRpcMessage(const WebDevToolsMessageData& data) + { + WebDevToolsAgentClient::sendMessageToFrontendOnIOThread(data); + } +}; + +} // namespace + +WebDevToolsAgentImpl::WebDevToolsAgentImpl( + WebViewImpl* webViewImpl, + WebDevToolsAgentClient* client) + : m_hostId(client->hostIdentifier()) + , m_client(client) + , m_webViewImpl(webViewImpl) + , m_apuAgentEnabled(false) + , m_resourceTrackingWasEnabled(false) + , m_attached(false) +{ + m_debuggerAgentDelegateStub.set(new DebuggerAgentDelegateStub(this)); + m_toolsAgentDelegateStub.set(new ToolsAgentDelegateStub(this)); + m_apuAgentDelegateStub.set(new ApuAgentDelegateStub(this)); +} + +WebDevToolsAgentImpl::~WebDevToolsAgentImpl() +{ + DebuggerAgentManager::onWebViewClosed(m_webViewImpl); + disposeUtilityContext(); +} + +void WebDevToolsAgentImpl::disposeUtilityContext() +{ + if (!m_utilityContext.IsEmpty()) { + m_utilityContext.Dispose(); + m_utilityContext.Clear(); + } +} + +void WebDevToolsAgentImpl::unhideResourcesPanelIfNecessary() +{ + InspectorController* ic = m_webViewImpl->page()->inspectorController(); + ic->ensureResourceTrackingSettingsLoaded(); + String command = String::format("[\"setResourcesPanelEnabled\", %s]", + ic->resourceTrackingEnabled() ? "true" : "false"); + m_toolsAgentDelegateStub->dispatchOnClient(command); +} + +void WebDevToolsAgentImpl::attach() +{ + if (m_attached) + return; + m_debuggerAgentImpl.set( + new DebuggerAgentImpl(m_webViewImpl, + m_debuggerAgentDelegateStub.get(), + this)); + resetInspectorFrontendProxy(); + unhideResourcesPanelIfNecessary(); + // Allow controller to send messages to the frontend. + InspectorController* ic = inspectorController(); + + { // TODO(yurys): the source should have already been pushed by the frontend. + v8::HandleScope scope; + v8::Context::Scope contextScope(m_utilityContext); + v8::Handle<v8::Value> constructorValue = m_utilityContext->Global()->Get( + v8::String::New("injectedScriptConstructor")); + if (constructorValue->IsFunction()) { + String source = WebCore::toWebCoreString(constructorValue); + ic->injectedScriptHost()->setInjectedScriptSource("(" + source + ")"); + } + } + + ic->setWindowVisible(true, false); + m_attached = true; +} + +void WebDevToolsAgentImpl::detach() +{ + // Prevent controller from sending messages to the frontend. + InspectorController* ic = m_webViewImpl->page()->inspectorController(); + ic->hideHighlight(); + ic->close(); + disposeUtilityContext(); + m_debuggerAgentImpl.set(0); + m_attached = false; + m_apuAgentEnabled = false; +} + +void WebDevToolsAgentImpl::didNavigate() +{ + DebuggerAgentManager::onNavigate(); +} + +void WebDevToolsAgentImpl::didCommitProvisionalLoad(WebFrameImpl* webframe, bool isNewNavigation) +{ + if (!m_attached) + return; + WebDataSource* ds = webframe->dataSource(); + const WebURLRequest& request = ds->request(); + WebURL url = ds->hasUnreachableURL() ? + ds->unreachableURL() : + request.url(); + if (!webframe->parent()) { + resetInspectorFrontendProxy(); + m_toolsAgentDelegateStub->frameNavigate(WebCore::KURL(url).string()); + SetApuAgentEnabledInUtilityContext(m_utilityContext, m_apuAgentEnabled); + unhideResourcesPanelIfNecessary(); + } +} + +void WebDevToolsAgentImpl::didClearWindowObject(WebFrameImpl* webframe) +{ + DebuggerAgentManager::setHostId(webframe, m_hostId); + if (m_attached) { + // Push context id into the client if it is already attached. + m_debuggerAgentDelegateStub->setContextId(m_hostId); + } +} + +void WebDevToolsAgentImpl::forceRepaint() +{ + m_client->forceRepaint(); +} + +void WebDevToolsAgentImpl::dispatchOnInspectorController(int callId, const String& functionName, const String& jsonArgs) +{ + String result; + String exception; + result = m_debuggerAgentImpl->executeUtilityFunction(m_utilityContext, callId, + "InspectorControllerDispatcher", functionName, jsonArgs, false /* is sync */, &exception); + m_toolsAgentDelegateStub->didDispatchOn(callId, result, exception); +} + +void WebDevToolsAgentImpl::dispatchOnInjectedScript(int callId, int injectedScriptId, const String& functionName, const String& jsonArgs, bool async) +{ + inspectorController()->inspectorBackend()->dispatchOnInjectedScript( + callId, + injectedScriptId, + functionName, + jsonArgs, + async); +} + +void WebDevToolsAgentImpl::dispatchMessageFromFrontend(const WebDevToolsMessageData& data) +{ + if (ToolsAgentDispatch::dispatch(this, data)) + return; + + if (!m_attached) + return; + + if (m_debuggerAgentImpl.get() && DebuggerAgentDispatch::dispatch(m_debuggerAgentImpl.get(), data)) + return; +} + +void WebDevToolsAgentImpl::inspectElementAt(const WebPoint& point) +{ + m_webViewImpl->inspectElementAt(point); +} + +void WebDevToolsAgentImpl::setRuntimeFeatureEnabled(const WebString& feature, bool enabled) +{ + if (feature == kApuAgentFeatureName) + setApuAgentEnabled(enabled); + else if (feature == kTimelineFeatureName) + setTimelineProfilingEnabled(enabled); + else if (feature == kResourceTrackingFeatureName) { + InspectorController* ic = m_webViewImpl->page()->inspectorController(); + if (enabled) + ic->enableResourceTracking(false /* not sticky */, false /* no reload */); + else + ic->disableResourceTracking(false /* not sticky */); + } +} + +void WebDevToolsAgentImpl::sendRpcMessage(const WebDevToolsMessageData& data) +{ + m_client->sendMessageToFrontend(data); +} + +void WebDevToolsAgentImpl::compileUtilityScripts() +{ + v8::HandleScope handleScope; + v8::Context::Scope contextScope(m_utilityContext); + // Inject javascript into the context. + WebCString injectedScriptJs = m_client->injectedScriptSource(); + v8::Script::Compile(v8::String::New( + injectedScriptJs.data(), + injectedScriptJs.length()))->Run(); + WebCString injectDispatchJs = m_client->injectedScriptDispatcherSource(); + v8::Script::Compile(v8::String::New( + injectDispatchJs.data(), + injectDispatchJs.length()))->Run(); +} + +void WebDevToolsAgentImpl::initDevToolsAgentHost() +{ + BoundObject devtoolsAgentHost(m_utilityContext, this, "DevToolsAgentHost"); + devtoolsAgentHost.addProtoFunction( + "dispatch", + WebDevToolsAgentImpl::jsDispatchOnClient); + devtoolsAgentHost.addProtoFunction( + "dispatchToApu", + WebDevToolsAgentImpl::jsDispatchToApu); + devtoolsAgentHost.addProtoFunction( + "evaluateOnSelf", + WebDevToolsAgentImpl::jsEvaluateOnSelf); + devtoolsAgentHost.addProtoFunction( + "runtimeFeatureStateChanged", + WebDevToolsAgentImpl::jsOnRuntimeFeatureStateChanged); + devtoolsAgentHost.build(); + + v8::HandleScope scope; + v8::Context::Scope utilityScope(m_utilityContext); + // Call custom code to create inspector backend wrapper in the utility context + // instead of calling V8DOMWrapper::convertToV8Object that would create the + // wrapper in the Page main frame context. + v8::Handle<v8::Object> backendWrapper = createInspectorBackendV8Wrapper(); + if (backendWrapper.IsEmpty()) + return; + m_utilityContext->Global()->Set(v8::String::New("InspectorBackend"), backendWrapper); +} + +v8::Local<v8::Object> WebDevToolsAgentImpl::createInspectorBackendV8Wrapper() +{ + V8ClassIndex::V8WrapperType descriptorType = V8ClassIndex::INSPECTORBACKEND; + v8::Handle<v8::Function> function = V8InspectorBackend::GetTemplate()->GetFunction(); + if (function.IsEmpty()) { + // Return if allocation failed. + return v8::Local<v8::Object>(); + } + v8::Local<v8::Object> instance = SafeAllocation::newInstance(function); + if (instance.IsEmpty()) { + // Avoid setting the wrapper if allocation failed. + return v8::Local<v8::Object>(); + } + InspectorBackend* backend = m_webViewImpl->page()->inspectorController()->inspectorBackend(); + V8DOMWrapper::setDOMWrapper(instance, V8ClassIndex::ToInt(descriptorType), backend); + // Create a weak reference to the v8 wrapper of InspectorBackend to deref + // InspectorBackend when the wrapper is garbage collected. + backend->ref(); + v8::Persistent<v8::Object> weakHandle = v8::Persistent<v8::Object>::New(instance); + weakHandle.MakeWeak(backend, &InspectorBackendWeakReferenceCallback); + return instance; +} + +void WebDevToolsAgentImpl::resetInspectorFrontendProxy() +{ + disposeUtilityContext(); + m_debuggerAgentImpl->createUtilityContext(m_webViewImpl->page()->mainFrame(), &m_utilityContext); + compileUtilityScripts(); + initDevToolsAgentHost(); + + v8::HandleScope scope; + v8::Context::Scope contextScope(m_utilityContext); + ScriptState* state = ScriptState::forContext( + v8::Local<v8::Context>::New(m_utilityContext)); + InspectorController* ic = inspectorController(); + ic->setFrontendProxyObject(state, ScriptObject(state, m_utilityContext->Global())); +} + +void WebDevToolsAgentImpl::setApuAgentEnabled(bool enabled) +{ + m_apuAgentEnabled = enabled; + SetApuAgentEnabledInUtilityContext(m_utilityContext, enabled); + InspectorController* ic = m_webViewImpl->page()->inspectorController(); + if (enabled) { + m_resourceTrackingWasEnabled = ic->resourceTrackingEnabled(); + ic->startTimelineProfiler(); + if (!m_resourceTrackingWasEnabled) { + // TODO(knorton): Introduce some kind of agents dependency here so that + // user could turn off resource tracking while apu agent is on. + ic->enableResourceTracking(false, false); + } + m_debuggerAgentImpl->setAutoContinueOnException(true); + } else { + ic->stopTimelineProfiler(); + if (!m_resourceTrackingWasEnabled) + ic->disableResourceTracking(false); + m_resourceTrackingWasEnabled = false; + } + m_client->runtimeFeatureStateChanged( + kApuAgentFeatureName, + enabled); +} + +// static +v8::Handle<v8::Value> WebDevToolsAgentImpl::jsDispatchOnClient(const v8::Arguments& args) +{ + v8::TryCatch exceptionCatcher; + String message = WebCore::toWebCoreStringWithNullCheck(args[0]); + if (message.isEmpty() || exceptionCatcher.HasCaught()) + return v8::Undefined(); + WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>(v8::External::Cast(*args.Data())->Value()); + agent->m_toolsAgentDelegateStub->dispatchOnClient(message); + return v8::Undefined(); +} + +// static +v8::Handle<v8::Value> WebDevToolsAgentImpl::jsDispatchToApu(const v8::Arguments& args) +{ + v8::TryCatch exceptionCatcher; + String message = WebCore::toWebCoreStringWithNullCheck(args[0]); + if (message.isEmpty() || exceptionCatcher.HasCaught()) + return v8::Undefined(); + WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>( + v8::External::Cast(*args.Data())->Value()); + agent->m_apuAgentDelegateStub->dispatchToApu(message); + return v8::Undefined(); +} + +// static +v8::Handle<v8::Value> WebDevToolsAgentImpl::jsEvaluateOnSelf(const v8::Arguments& args) +{ + String code; + { + v8::TryCatch exceptionCatcher; + code = WebCore::toWebCoreStringWithNullCheck(args[0]); + if (code.isEmpty() || exceptionCatcher.HasCaught()) + return v8::Undefined(); + } + WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>(v8::External::Cast(*args.Data())->Value()); + v8::Context::Scope(agent->m_utilityContext); + V8Proxy* proxy = V8Proxy::retrieve(agent->m_webViewImpl->page()->mainFrame()); + v8::Local<v8::Value> result = proxy->runScript(v8::Script::Compile(v8::String::New(code.utf8().data())), true); + return result; +} + +// static +v8::Handle<v8::Value> WebDevToolsAgentImpl::jsOnRuntimeFeatureStateChanged(const v8::Arguments& args) +{ + v8::TryCatch exceptionCatcher; + String feature = WebCore::toWebCoreStringWithNullCheck(args[0]); + bool enabled = args[1]->ToBoolean()->Value(); + if (feature.isEmpty() || exceptionCatcher.HasCaught()) + return v8::Undefined(); + WebDevToolsAgentImpl* agent = static_cast<WebDevToolsAgentImpl*>(v8::External::Cast(*args.Data())->Value()); + agent->m_client->runtimeFeatureStateChanged(feature, enabled); + return v8::Undefined(); +} + + +WebCore::InspectorController* WebDevToolsAgentImpl::inspectorController() +{ + if (Page* page = m_webViewImpl->page()) + return page->inspectorController(); + return 0; +} + + +//------- plugin resource load notifications --------------- +void WebDevToolsAgentImpl::identifierForInitialRequest( + unsigned long resourceId, + WebFrame* frame, + const WebURLRequest& request) +{ + if (InspectorController* ic = inspectorController()) { + WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(frame); + FrameLoader* frameLoader = webFrameImpl->frame()->loader(); + DocumentLoader* loader = frameLoader->activeDocumentLoader(); + ic->identifierForInitialRequest(resourceId, loader, request.toResourceRequest()); + } +} + +void WebDevToolsAgentImpl::willSendRequest(unsigned long resourceId, const WebURLRequest& request) +{ + if (InspectorController* ic = inspectorController()) + ic->willSendRequest(resourceId, request.toResourceRequest(), ResourceResponse()); +} + +void WebDevToolsAgentImpl::didReceiveData(unsigned long resourceId, int length) +{ + if (InspectorController* ic = inspectorController()) + ic->didReceiveContentLength(resourceId, length); +} + +void WebDevToolsAgentImpl::didReceiveResponse(unsigned long resourceId, const WebURLResponse& response) +{ + if (InspectorController* ic = inspectorController()) + ic->didReceiveResponse(resourceId, response.toResourceResponse()); +} + +void WebDevToolsAgentImpl::didFinishLoading(unsigned long resourceId) +{ + if (InspectorController* ic = inspectorController()) + ic->didFinishLoading(resourceId); +} + +void WebDevToolsAgentImpl::didFailLoading(unsigned long resourceId, const WebURLError& error) +{ + ResourceError resourceError; + if (InspectorController* ic = inspectorController()) + ic->didFailLoading(resourceId, resourceError); +} + +void WebDevToolsAgentImpl::evaluateInWebInspector(long callId, const WebString& script) +{ + InspectorController* ic = inspectorController(); + ic->evaluateForTestInFrontend(callId, script); +} + +void WebDevToolsAgentImpl::setTimelineProfilingEnabled(bool enabled) +{ + InspectorController* ic = inspectorController(); + if (enabled) + ic->startTimelineProfiler(); + else + ic->stopTimelineProfiler(); +} + +WebDevToolsAgent* WebDevToolsAgent::create(WebView* webview, WebDevToolsAgentClient* client) +{ + return new WebDevToolsAgentImpl(static_cast<WebViewImpl*>(webview), client); +} + +void WebDevToolsAgent::executeDebuggerCommand(const WebString& command, int callerId) +{ + DebuggerAgentManager::executeDebuggerCommand(command, callerId); +} + +void WebDevToolsAgent::debuggerPauseScript() +{ + DebuggerAgentManager::pauseScript(); +} + +void WebDevToolsAgent::setMessageLoopDispatchHandler(MessageLoopDispatchHandler handler) +{ + DebuggerAgentManager::setMessageLoopDispatchHandler(handler); +} + +bool WebDevToolsAgent::dispatchMessageFromFrontendOnIOThread(const WebDevToolsMessageData& data) +{ + IORPCDelegate transport; + ProfilerAgentDelegateStub stub(&transport); + ProfilerAgentImpl agent(&stub); + return ProfilerAgentDispatch::dispatch(&agent, data); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebDevToolsAgentImpl.h b/WebKit/chromium/src/WebDevToolsAgentImpl.h new file mode 100644 index 0000000..1f81c6d --- /dev/null +++ b/WebKit/chromium/src/WebDevToolsAgentImpl.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010 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 WebDevToolsAgentImpl_h +#define WebDevToolsAgentImpl_h + +#include "APUAgentDelegate.h" +#include "DevToolsRPC.h" +#include "ToolsAgent.h" +#include "WebDevToolsAgentPrivate.h" + +#include <v8.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { +class Document; +class InspectorController; +class Node; +class String; +} + +namespace WebKit { + +class DebuggerAgentDelegateStub; +class DebuggerAgentImpl; +class WebDevToolsAgentClient; +class WebFrame; +class WebFrameImpl; +class WebString; +class WebURLRequest; +class WebURLResponse; +class WebViewImpl; +struct WebURLError; +struct WebDevToolsMessageData; + +class WebDevToolsAgentImpl : public WebDevToolsAgentPrivate, + public ToolsAgent, + public DevToolsRPC::Delegate { +public: + WebDevToolsAgentImpl(WebViewImpl* webViewImpl, WebDevToolsAgentClient* client); + virtual ~WebDevToolsAgentImpl(); + + // ToolsAgent implementation. + virtual void dispatchOnInspectorController(int callId, const WebCore::String& functionName, const WebCore::String& jsonArgs); + virtual void dispatchOnInjectedScript(int callId, int injectedScriptId, const WebCore::String& functionName, const WebCore::String& jsonArgs, bool async); + + // WebDevToolsAgentPrivate implementation. + virtual void didClearWindowObject(WebFrameImpl* frame); + virtual void didCommitProvisionalLoad(WebFrameImpl* frame, bool isNewNavigation); + + // WebDevToolsAgent implementation. + virtual void attach(); + virtual void detach(); + virtual void didNavigate(); + virtual void dispatchMessageFromFrontend(const WebDevToolsMessageData& data); + virtual void inspectElementAt(const WebPoint& point); + virtual void evaluateInWebInspector(long callId, const WebString& script); + virtual void setRuntimeFeatureEnabled(const WebString& feature, bool enabled); + virtual void setTimelineProfilingEnabled(bool enable); + + virtual void identifierForInitialRequest(unsigned long, WebFrame*, const WebURLRequest&); + virtual void willSendRequest(unsigned long, const WebURLRequest&); + virtual void didReceiveData(unsigned long, int length); + virtual void didReceiveResponse(unsigned long, const WebURLResponse&); + virtual void didFinishLoading(unsigned long); + virtual void didFailLoading(unsigned long, const WebURLError&); + + // DevToolsRPC::Delegate implementation. + virtual void sendRpcMessage(const WebDevToolsMessageData& data); + + void forceRepaint(); + + int hostId() { return m_hostId; } + +private: + static v8::Handle<v8::Value> jsDispatchOnClient(const v8::Arguments& args); + static v8::Handle<v8::Value> jsDispatchToApu(const v8::Arguments& args); + static v8::Handle<v8::Value> jsEvaluateOnSelf(const v8::Arguments& args); + static v8::Handle<v8::Value> jsOnRuntimeFeatureStateChanged(const v8::Arguments& args); + + void disposeUtilityContext(); + void unhideResourcesPanelIfNecessary(); + + void compileUtilityScripts(); + void initDevToolsAgentHost(); + void resetInspectorFrontendProxy(); + void setApuAgentEnabled(bool enabled); + + WebCore::InspectorController* inspectorController(); + + // Creates InspectorBackend v8 wrapper in the utility context so that it's + // methods prototype is Function.protoype object from the utility context. + // Otherwise some useful methods defined on Function.prototype(such as bind) + // are missing for InspectorController native methods. + v8::Local<v8::Object> createInspectorBackendV8Wrapper(); + + int m_hostId; + WebDevToolsAgentClient* m_client; + WebViewImpl* m_webViewImpl; + OwnPtr<DebuggerAgentDelegateStub> m_debuggerAgentDelegateStub; + OwnPtr<ToolsAgentDelegateStub> m_toolsAgentDelegateStub; + OwnPtr<DebuggerAgentImpl> m_debuggerAgentImpl; + OwnPtr<ApuAgentDelegateStub> m_apuAgentDelegateStub; + bool m_apuAgentEnabled; + bool m_resourceTrackingWasEnabled; + bool m_attached; + // TODO(pfeldman): This should not be needed once GC styles issue is fixed + // for matching rules. + v8::Persistent<v8::Context> m_utilityContext; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/WebDevToolsFrontendImpl.cpp b/WebKit/chromium/src/WebDevToolsFrontendImpl.cpp new file mode 100644 index 0000000..89fa6e7 --- /dev/null +++ b/WebKit/chromium/src/WebDevToolsFrontendImpl.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2010 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 "WebDevToolsFrontendImpl.h" + +#include "BoundObject.h" +#include "ContextMenuController.h" +#include "ContextMenuItem.h" +#include "DOMWindow.h" +#include "DebuggerAgent.h" +#include "DevToolsRPCJS.h" +#include "Document.h" +#include "Event.h" +#include "Frame.h" +#include "InspectorBackend.h" +#include "InspectorController.h" +#include "InspectorFrontendHost.h" +#include "Node.h" +#include "Page.h" +#include "Pasteboard.h" +#include "PlatformString.h" +#include "ProfilerAgent.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "ToolsAgent.h" +#include "V8Binding.h" +#include "V8DOMWrapper.h" +#include "V8InspectorFrontendHost.h" +#include "V8Node.h" +#include "V8Proxy.h" +#include "V8Utilities.h" +#include "WebDevToolsFrontendClient.h" +#include "WebFrameImpl.h" +#include "WebScriptSource.h" +#include "WebViewImpl.h" +#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> + +using namespace WebCore; + +namespace WebKit { + +static v8::Local<v8::String> ToV8String(const String& s) +{ + if (s.isNull()) + return v8::Local<v8::String>(); + + return v8::String::New(reinterpret_cast<const uint16_t*>(s.characters()), s.length()); +} + +DEFINE_RPC_JS_BOUND_OBJ(DebuggerAgent, DEBUGGER_AGENT_STRUCT, DebuggerAgentDelegate, DEBUGGER_AGENT_DELEGATE_STRUCT) +DEFINE_RPC_JS_BOUND_OBJ(ProfilerAgent, PROFILER_AGENT_STRUCT, ProfilerAgentDelegate, PROFILER_AGENT_DELEGATE_STRUCT) +DEFINE_RPC_JS_BOUND_OBJ(ToolsAgent, TOOLS_AGENT_STRUCT, ToolsAgentDelegate, TOOLS_AGENT_DELEGATE_STRUCT) + +WebDevToolsFrontend* WebDevToolsFrontend::create( + WebView* view, + WebDevToolsFrontendClient* client, + const WebString& applicationLocale) +{ + return new WebDevToolsFrontendImpl( + static_cast<WebViewImpl*>(view), + client, + applicationLocale); +} + +WebDevToolsFrontendImpl::WebDevToolsFrontendImpl( + WebViewImpl* webViewImpl, + WebDevToolsFrontendClient* client, + const String& applicationLocale) + : m_webViewImpl(webViewImpl) + , m_client(client) + , m_applicationLocale(applicationLocale) + , m_loaded(false) +{ + WebFrameImpl* frame = m_webViewImpl->mainFrameImpl(); + v8::HandleScope scope; + v8::Handle<v8::Context> frameContext = V8Proxy::context(frame->frame()); + + m_debuggerAgentObj.set(new JSDebuggerAgentBoundObj(this, frameContext, "RemoteDebuggerAgent")); + m_profilerAgentObj.set(new JSProfilerAgentBoundObj(this, frameContext, "RemoteProfilerAgent")); + m_toolsAgentObj.set(new JSToolsAgentBoundObj(this, frameContext, "RemoteToolsAgent")); + + // Debugger commands should be sent using special method. + BoundObject debuggerCommandExecutorObj(frameContext, this, "RemoteDebuggerCommandExecutor"); + debuggerCommandExecutorObj.addProtoFunction( + "DebuggerCommand", + WebDevToolsFrontendImpl::jsDebuggerCommand); + debuggerCommandExecutorObj.addProtoFunction( + "DebuggerPauseScript", + WebDevToolsFrontendImpl::jsDebuggerPauseScript); + debuggerCommandExecutorObj.build(); + + BoundObject devToolsHost(frameContext, this, "InspectorFrontendHost"); + devToolsHost.addProtoFunction( + "loaded", + WebDevToolsFrontendImpl::jsLoaded); + devToolsHost.addProtoFunction( + "platform", + WebDevToolsFrontendImpl::jsPlatform); + devToolsHost.addProtoFunction( + "port", + WebDevToolsFrontendImpl::jsPort); + devToolsHost.addProtoFunction( + "copyText", + WebDevToolsFrontendImpl::jsCopyText); + devToolsHost.addProtoFunction( + "activateWindow", + WebDevToolsFrontendImpl::jsActivateWindow); + devToolsHost.addProtoFunction( + "closeWindow", + WebDevToolsFrontendImpl::jsCloseWindow); + devToolsHost.addProtoFunction( + "attach", + WebDevToolsFrontendImpl::jsDockWindow); + devToolsHost.addProtoFunction( + "detach", + WebDevToolsFrontendImpl::jsUndockWindow); + devToolsHost.addProtoFunction( + "localizedStringsURL", + WebDevToolsFrontendImpl::jsLocalizedStringsURL); + devToolsHost.addProtoFunction( + "hiddenPanels", + WebDevToolsFrontendImpl::jsHiddenPanels); + devToolsHost.addProtoFunction( + "setting", + WebDevToolsFrontendImpl::jsSetting); + devToolsHost.addProtoFunction( + "setSetting", + WebDevToolsFrontendImpl::jsSetSetting); + devToolsHost.addProtoFunction( + "windowUnloading", + WebDevToolsFrontendImpl::jsWindowUnloading); + devToolsHost.addProtoFunction( + "showContextMenu", + WebDevToolsFrontendImpl::jsShowContextMenu); + devToolsHost.build(); +} + +WebDevToolsFrontendImpl::~WebDevToolsFrontendImpl() +{ + if (m_menuProvider) + m_menuProvider->disconnect(); +} + +void WebDevToolsFrontendImpl::dispatchMessageFromAgent(const WebDevToolsMessageData& data) +{ + Vector<String> v; + v.append(data.className); + v.append(data.methodName); + for (size_t i = 0; i < data.arguments.size(); i++) + v.append(data.arguments[i]); + if (!m_loaded) { + m_pendingIncomingMessages.append(v); + return; + } + executeScript(v); +} + +void WebDevToolsFrontendImpl::executeScript(const Vector<String>& v) +{ + WebFrameImpl* frame = m_webViewImpl->mainFrameImpl(); + v8::HandleScope scope; + v8::Handle<v8::Context> frameContext = V8Proxy::context(frame->frame()); + v8::Context::Scope contextScope(frameContext); + v8::Handle<v8::Value> dispatchFunction = frameContext->Global()->Get(v8::String::New("devtools$$dispatch")); + ASSERT(dispatchFunction->IsFunction()); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(dispatchFunction); + Vector< v8::Handle<v8::Value> > args; + for (size_t i = 0; i < v.size(); i++) + args.append(ToV8String(v.at(i))); + function->Call(frameContext->Global(), args.size(), args.data()); +} + +void WebDevToolsFrontendImpl::dispatchOnWebInspector(const String& methodName, const String& param) +{ + WebFrameImpl* frame = m_webViewImpl->mainFrameImpl(); + v8::HandleScope scope; + v8::Handle<v8::Context> frameContext = V8Proxy::context(frame->frame()); + v8::Context::Scope contextScope(frameContext); + + v8::Handle<v8::Value> webInspector = frameContext->Global()->Get(v8::String::New("WebInspector")); + ASSERT(webInspector->IsObject()); + v8::Handle<v8::Object> webInspectorObj = v8::Handle<v8::Object>::Cast(webInspector); + + v8::Handle<v8::Value> method = webInspectorObj->Get(ToV8String(methodName)); + ASSERT(method->IsFunction()); + v8::Handle<v8::Function> methodFunc = v8::Handle<v8::Function>::Cast(method); + v8::Handle<v8::Value> args[] = { + ToV8String(param) + }; + methodFunc->Call(frameContext->Global(), 1, args); +} + +void WebDevToolsFrontendImpl::sendRpcMessage(const WebDevToolsMessageData& data) +{ + m_client->sendMessageToAgent(data); +} + +void WebDevToolsFrontendImpl::contextMenuItemSelected(ContextMenuItem* item) +{ + int itemNumber = item->action() - ContextMenuItemBaseCustomTag; + dispatchOnWebInspector("contextMenuItemSelected", String::number(itemNumber)); +} + +void WebDevToolsFrontendImpl::contextMenuCleared() +{ + dispatchOnWebInspector("contextMenuCleared", ""); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsLoaded(const v8::Arguments& args) +{ + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + frontend->m_loaded = true; + + // Grant the devtools page the ability to have source view iframes. + Page* page = V8Proxy::retrieveFrameForEnteredContext()->page(); + SecurityOrigin* origin = page->mainFrame()->domWindow()->securityOrigin(); + origin->grantUniversalAccess(); + + for (Vector<Vector<String> >::iterator it = frontend->m_pendingIncomingMessages.begin(); + it != frontend->m_pendingIncomingMessages.end(); + ++it) { + frontend->executeScript(*it); + } + frontend->m_pendingIncomingMessages.clear(); + return v8::Undefined(); +} + +// static +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsPlatform(const v8::Arguments& args) +{ +#if defined(OS_MACOSX) + return v8String("mac"); +#elif defined(OS_LINUX) + return v8String("linux"); +#elif defined(OS_WIN) + return v8String("windows"); +#else + return v8String("unknown"); +#endif +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsPort(const v8::Arguments& args) +{ + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsCopyText(const v8::Arguments& args) +{ + String text = WebCore::toWebCoreStringWithNullCheck(args[0]); + Pasteboard::generalPasteboard()->writePlainText(text); + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsActivateWindow(const v8::Arguments& args) +{ + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + frontend->m_client->activateWindow(); + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsCloseWindow(const v8::Arguments& args) +{ + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + frontend->m_client->closeWindow(); + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsDockWindow(const v8::Arguments& args) +{ + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + frontend->m_client->dockWindow(); + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsUndockWindow(const v8::Arguments& args) +{ + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + frontend->m_client->undockWindow(); + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsLocalizedStringsURL(const v8::Arguments& args) +{ + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsHiddenPanels(const v8::Arguments& args) +{ + return v8String(""); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsDebuggerCommand(const v8::Arguments& args) +{ + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + WebString command = WebCore::toWebCoreStringWithNullCheck(args[0]); + frontend->m_client->sendDebuggerCommandToAgent(command); + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsSetting(const v8::Arguments& args) +{ + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsSetSetting(const v8::Arguments& args) +{ + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsDebuggerPauseScript(const v8::Arguments& args) +{ + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + frontend->m_client->sendDebuggerPauseScript(); + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsWindowUnloading(const v8::Arguments& args) +{ + // TODO(pfeldman): Implement this. + return v8::Undefined(); +} + +v8::Handle<v8::Value> WebDevToolsFrontendImpl::jsShowContextMenu(const v8::Arguments& args) +{ + if (args.Length() < 2) + return v8::Undefined(); + + v8::Local<v8::Object> eventWrapper = v8::Local<v8::Object>::Cast(args[0]); + if (V8DOMWrapper::domWrapperType(eventWrapper) != V8ClassIndex::MOUSEEVENT) + return v8::Undefined(); + + Event* event = V8Event::toNative(eventWrapper); + if (!args[1]->IsArray()) + return v8::Undefined(); + + v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(args[1]); + Vector<ContextMenuItem*> items; + + for (size_t i = 0; i < array->Length(); ++i) { + v8::Local<v8::Object> item = v8::Local<v8::Object>::Cast(array->Get(v8::Integer::New(i))); + v8::Local<v8::Value> label = item->Get(v8::String::New("label")); + v8::Local<v8::Value> id = item->Get(v8::String::New("id")); + if (label->IsUndefined() || id->IsUndefined()) { + items.append(new ContextMenuItem(SeparatorType, + ContextMenuItemTagNoAction, + String())); + } else { + ContextMenuAction typedId = static_cast<ContextMenuAction>( + ContextMenuItemBaseCustomTag + id->ToInt32()->Value()); + items.append(new ContextMenuItem(ActionType, + typedId, + toWebCoreStringWithNullCheck(label))); + } + } + + WebDevToolsFrontendImpl* frontend = static_cast<WebDevToolsFrontendImpl*>(v8::External::Cast(*args.Data())->Value()); + + frontend->m_menuProvider = MenuProvider::create(frontend, items); + + ContextMenuController* menuController = frontend->m_webViewImpl->page()->contextMenuController(); + menuController->showContextMenu(event, frontend->m_menuProvider); + + return v8::Undefined(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebDevToolsFrontendImpl.h b/WebKit/chromium/src/WebDevToolsFrontendImpl.h new file mode 100644 index 0000000..62b34da --- /dev/null +++ b/WebKit/chromium/src/WebDevToolsFrontendImpl.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2010 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 WebDevToolsFrontendImpl_h +#define WebDevToolsFrontendImpl_h + +#include "ContextMenu.h" +#include "ContextMenuProvider.h" +#include "DevToolsRPC.h" +#include "WebDevToolsFrontend.h" +#include <v8.h> +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { +class ContextMenuItem; +class Node; +class Page; +class String; +} + +namespace WebKit { + +class JSDebuggerAgentBoundObj; +class JSProfilerAgentBoundObj; +class JSToolsAgentBoundObj; +class WebDevToolsClientDelegate; +class WebViewImpl; +struct WebDevToolsMessageData; + +class WebDevToolsFrontendImpl : public WebKit::WebDevToolsFrontend + , public DevToolsRPC::Delegate + , public Noncopyable { +public: + WebDevToolsFrontendImpl( + WebKit::WebViewImpl* webViewImpl, + WebKit::WebDevToolsFrontendClient* client, + const String& applicationLocale); + virtual ~WebDevToolsFrontendImpl(); + + // DevToolsRPC::Delegate implementation. + virtual void sendRpcMessage(const WebKit::WebDevToolsMessageData& data); + + // WebDevToolsFrontend implementation. + virtual void dispatchMessageFromAgent(const WebKit::WebDevToolsMessageData& data); + +private: + class MenuProvider : public WebCore::ContextMenuProvider { + public: + static PassRefPtr<MenuProvider> create(WebDevToolsFrontendImpl* frontendHost, const Vector<WebCore::ContextMenuItem*>& items) + { + return adoptRef(new MenuProvider(frontendHost, items)); + } + + virtual ~MenuProvider() + { + contextMenuCleared(); + } + + void disconnect() + { + m_frontendHost = 0; + } + + virtual void populateContextMenu(WebCore::ContextMenu* menu) + { + for (size_t i = 0; i < m_items.size(); ++i) + menu->appendItem(*m_items[i]); + } + + virtual void contextMenuItemSelected(WebCore::ContextMenuItem* item) + { + if (m_frontendHost) + m_frontendHost->contextMenuItemSelected(item); + } + + virtual void contextMenuCleared() + { + if (m_frontendHost) + m_frontendHost->contextMenuCleared(); + deleteAllValues(m_items); + m_items.clear(); + } + + private: + MenuProvider(WebDevToolsFrontendImpl* frontendHost, const Vector<WebCore::ContextMenuItem*>& items) + : m_frontendHost(frontendHost) + , m_items(items) { } + WebDevToolsFrontendImpl* m_frontendHost; + Vector<WebCore::ContextMenuItem*> m_items; + }; + + void executeScript(const Vector<String>& v); + void dispatchOnWebInspector(const String& method, const String& param); + + // friend class MenuSelectionHandler; + void contextMenuItemSelected(WebCore::ContextMenuItem* menuItem); + void contextMenuCleared(); + + static v8::Handle<v8::Value> jsLoaded(const v8::Arguments& args); + static v8::Handle<v8::Value> jsPlatform(const v8::Arguments& args); + static v8::Handle<v8::Value> jsPort(const v8::Arguments& args); + static v8::Handle<v8::Value> jsCopyText(const v8::Arguments& args); + + static v8::Handle<v8::Value> jsActivateWindow(const v8::Arguments& args); + static v8::Handle<v8::Value> jsCloseWindow(const v8::Arguments& args); + static v8::Handle<v8::Value> jsDockWindow(const v8::Arguments& args); + static v8::Handle<v8::Value> jsUndockWindow(const v8::Arguments& args); + static v8::Handle<v8::Value> jsLocalizedStringsURL(const v8::Arguments& args); + static v8::Handle<v8::Value> jsHiddenPanels(const v8::Arguments& args); + static v8::Handle<v8::Value> jsDebuggerCommand(const v8::Arguments& args); + static v8::Handle<v8::Value> jsSetting(const v8::Arguments& args); + static v8::Handle<v8::Value> jsSetSetting(const v8::Arguments& args); + static v8::Handle<v8::Value> jsDebuggerPauseScript(const v8::Arguments& args); + static v8::Handle<v8::Value> jsWindowUnloading(const v8::Arguments& args); + static v8::Handle<v8::Value> jsShowContextMenu(const v8::Arguments& args); + + WebKit::WebViewImpl* m_webViewImpl; + WebKit::WebDevToolsFrontendClient* m_client; + String m_applicationLocale; + OwnPtr<JSDebuggerAgentBoundObj> m_debuggerAgentObj; + OwnPtr<JSProfilerAgentBoundObj> m_profilerAgentObj; + OwnPtr<JSToolsAgentBoundObj> m_toolsAgentObj; + bool m_loaded; + Vector<Vector<String> > m_pendingIncomingMessages; + RefPtr<MenuProvider> m_menuProvider; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/WebDocument.cpp b/WebKit/chromium/src/WebDocument.cpp new file mode 100644 index 0000000..84f3004 --- /dev/null +++ b/WebKit/chromium/src/WebDocument.cpp @@ -0,0 +1,150 @@ +/* + * 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 "WebDocument.h" + +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "HTMLAllCollection.h" +#include "HTMLBodyElement.h" +#include "HTMLCollection.h" +#include "HTMLElement.h" +#include "HTMLHeadElement.h" +#include "NodeList.h" + +#include "WebElement.h" +#include "WebFrameImpl.h" +#include "WebNodeCollection.h" +#include "WebURL.h" + +#include <wtf/PassRefPtr.h> + +using namespace WebCore; + +namespace WebKit { + +WebDocument::WebDocument(const PassRefPtr<Document>& elem) + : WebNode(elem.releaseRef()) +{ +} + +WebDocument& WebDocument::operator=(const PassRefPtr<Document>& elem) +{ + WebNode::assign(elem.releaseRef()); + return *this; +} + +WebDocument::operator PassRefPtr<Document>() const +{ + return PassRefPtr<Document>(static_cast<Document*>(m_private)); +} + +WebFrame* WebDocument::frame() const +{ + return WebFrameImpl::fromFrame(constUnwrap<Document>()->frame()); +} + +bool WebDocument::isHTMLDocument() const +{ + return constUnwrap<Document>()->isHTMLDocument(); +} + +WebURL WebDocument::baseURL() const +{ + return constUnwrap<Document>()->baseURL(); +} + +WebElement WebDocument::documentElement() const +{ + return WebElement(constUnwrap<Document>()->documentElement()); +} + +WebElement WebDocument::body() const +{ + return WebElement(constUnwrap<Document>()->body()); +} + +WebElement WebDocument::head() +{ + return WebElement(unwrap<Document>()->head()); +} + +WebNodeCollection WebDocument::all() +{ + return WebNodeCollection(unwrap<Document>()->all()); +} + +WebURL WebDocument::completeURL(const WebString& partialURL) const +{ + return constUnwrap<Document>()->completeURL(partialURL); +} + +WebElement WebDocument::getElementById(const WebString& id) const +{ + return WebElement(constUnwrap<Document>()->getElementById(id)); +} + +WebString WebDocument::applicationID() const +{ + const char* kChromeApplicationHeader = "x-chrome-application"; + + // First check if the document's response included a header indicating the + // application it should go with. + const Document* document = constUnwrap<Document>(); + Frame* frame = document->frame(); + if (!frame) + return WebString(); + + DocumentLoader* loader = frame->loader()->documentLoader(); + if (!loader) + return WebString(); + + WebString headerValue = + loader->response().httpHeaderField(kChromeApplicationHeader); + if (!headerValue.isEmpty()) + return headerValue; + + // Otherwise, fall back to looking for the meta tag. + RefPtr<NodeList> metaTags = + const_cast<Document*>(document)->getElementsByTagName("meta"); + for (unsigned i = 0; i < metaTags->length(); ++i) { + Element* element = static_cast<Element*>(metaTags->item(i)); + if (element->getAttribute("http-equiv").lower() == + kChromeApplicationHeader) { + return element->getAttribute("value"); + } + } + + return WebString(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebDragData.cpp b/WebKit/chromium/src/WebDragData.cpp index 4af1119..b18ab1b 100644 --- a/WebKit/chromium/src/WebDragData.cpp +++ b/WebKit/chromium/src/WebDragData.cpp @@ -88,6 +88,30 @@ void WebDragData::setURLTitle(const WebString& urlTitle) m_private->urlTitle = urlTitle; } +WebURL WebDragData::downloadURL() const +{ + ASSERT(!isNull()); + return m_private->downloadURL; +} + +void WebDragData::setDownloadURL(const WebURL& downloadURL) +{ + ensureMutable(); + m_private->downloadURL = downloadURL; +} + +WebString WebDragData::downloadMetadata() const +{ + ASSERT(!isNull()); + return m_private->downloadMetadata; +} + +void WebDragData::setDownloadMetadata(const WebString& downloadMetadata) +{ + ensureMutable(); + m_private->downloadMetadata = downloadMetadata; +} + WebString WebDragData::fileExtension() const { ASSERT(!isNull()); diff --git a/WebKit/chromium/src/WebElement.cpp b/WebKit/chromium/src/WebElement.cpp index 3f13ee1..d0a0862 100644 --- a/WebKit/chromium/src/WebElement.cpp +++ b/WebKit/chromium/src/WebElement.cpp @@ -54,5 +54,38 @@ WebElement::operator WTF::PassRefPtr<Element>() const return PassRefPtr<Element>(static_cast<Element*>(m_private)); } +WebString WebElement::tagName() const +{ + return constUnwrap<Element>()->tagName(); +} + +bool WebElement::hasTagName(const WebString& tagName) const +{ + return equalIgnoringCase(constUnwrap<Element>()->tagName(), + tagName.operator WebCore::String()); +} + +bool WebElement::hasAttribute(const WebString& attrName) const +{ + return constUnwrap<Element>()->hasAttribute(attrName); +} + +WebString WebElement::getAttribute(const WebString& attrName) const +{ + return constUnwrap<Element>()->getAttribute(attrName); +} + +bool WebElement::setAttribute(const WebString& attrName, const WebString& attrValue) +{ + ExceptionCode exceptionCode = 0; + unwrap<Element>()->setAttribute(attrName, attrValue, exceptionCode); + return !exceptionCode; +} + +WebString WebElement::innerText() const +{ + return constUnwrap<Element>()->innerText(); +} + } // namespace WebKit diff --git a/WebKit/chromium/src/WebEntities.cpp b/WebKit/chromium/src/WebEntities.cpp new file mode 100644 index 0000000..b9143d9 --- /dev/null +++ b/WebKit/chromium/src/WebEntities.cpp @@ -0,0 +1,133 @@ +/* + * 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 "WebEntities.h" + +#include <string.h> + +#include "PlatformString.h" +#include "StringBuilder.h" +#include <wtf/HashMap.h> + +#include "WebString.h" + +using namespace WebCore; + +namespace { +// Note that this file is also included by HTMLTokenizer.cpp so we are getting +// two copies of the data in memory. We can fix this by changing the script +// that generated the array to create a static const that is its length, but +// this is low priority since the data is less than 4K. We use anonymous +// namespace to prevent name collisions. +#include "HTMLEntityNames.c" // NOLINT +} + +namespace WebKit { + +void populateMap(WTF::HashMap<int, WebCore::String>& map, + const Entity* entities, + size_t entitiesCount, + bool standardHTML) +{ + ASSERT(map.isEmpty()); + const Entity* entity = &entities[0]; + for (size_t i = 0; i < entitiesCount; i++, entity++) { + int code = entity->code; + String name = entity->name; + // For consistency, use the lowe case for entities that have both. + if (map.contains(code) && map.get(code) == name.lower()) + continue; + // Don't register %, ⊅ and &supl;. + if (standardHTML && (code == '%' || code == 0x2285 || code == 0x00b9)) + continue; + map.set(code, name); + } + if (standardHTML) + map.set(static_cast<int>(0x0027), String("#39")); +} + +static const Entity xmlBuiltInEntityCodes[] = { + { "lt", 0x003c }, + { "gt", 0x003e }, + { "amp", 0x0026 }, + { "apos", 0x0027 }, + { "quot", 0x0022 } +}; + +WebEntities::WebEntities(bool xmlEntities) +{ + if (xmlEntities) + populateMap(m_entitiesMap, + xmlBuiltInEntityCodes, + sizeof(xmlBuiltInEntityCodes) / sizeof(Entity), + false); + else + populateMap(m_entitiesMap, + wordlist, + sizeof(wordlist) / sizeof(Entity), + true); +} + +String WebEntities::entityNameByCode(int code) const +{ + if (m_entitiesMap.contains(code)) + return m_entitiesMap.get(code); + return ""; +} + +String WebEntities::convertEntitiesInString(const String& value) const +{ + unsigned len = value.length(); + const UChar* startPos = value.characters(); + const UChar* curPos = startPos; + + // FIXME: Optimize - create StringBuilder only if value has any entities. + StringBuilder result; + while (len--) { + if (m_entitiesMap.contains(*curPos)) { + // Append content before entity code. + if (curPos > startPos) + result.append(String(startPos, curPos - startPos)); + result.append("&"); + result.append(m_entitiesMap.get(*curPos)); + result.append(";"); + startPos = ++curPos; + } else + curPos++; + } + // Append the remaining content. + if (curPos > startPos) + result.append(String(startPos, curPos - startPos)); + + return result.toString(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebEntities.h b/WebKit/chromium/src/WebEntities.h new file mode 100644 index 0000000..ad3c310 --- /dev/null +++ b/WebKit/chromium/src/WebEntities.h @@ -0,0 +1,75 @@ +/* + * 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 WebEntities_h +#define WebEntities_h + +#include "PlatformString.h" +#include <wtf/HashMap.h> + +namespace WebKit { + +class WebEntities { +public: + // ', %, ⊅, &supl; are not defined by the HTML standards. + // - IE does not support ' as an HTML entity (but support it as an XML + // entity.) + // - Firefox supports ' as an HTML entity. + // - Both of IE and Firefox don't support %, ⊅ and &supl;. + // + // A web page saved by Chromium should be able to be read by other browsers + // such as IE and Firefox. Chromium should produce only the standard entity + // references which other browsers can recognize. + // So if standard_html_entities_ is true, we will use a numeric character + // reference for ', and don't use entity references for %, ⊅ + // and &supl; for serialization. + // + // If xmlEntities is true, WebEntities will only contain standard XML + // entities. + explicit WebEntities(bool xmlEntities); + + // Check whether specified unicode has corresponding html or xml built-in + // entity name. If yes, return the entity notation. If not, returns an + // empty string. Parameter isHTML indicates check the code in html entity + // map or in xml entity map. + WebCore::String entityNameByCode(int code) const; + + // Returns a new string with corresponding entity names replaced. + WebCore::String convertEntitiesInString(const WebCore::String&) const; +private: + typedef HashMap<int, WebCore::String> EntitiesMapType; + // An internal object that maps the Unicode character to corresponding + // entity notation. + EntitiesMapType m_entitiesMap; +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/WebEvent.cpp b/WebKit/chromium/src/WebEvent.cpp new file mode 100644 index 0000000..8c68959 --- /dev/null +++ b/WebKit/chromium/src/WebEvent.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010 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 "WebEvent.h" + +#include "Event.h" +#include "Node.h" +#include <wtf/PassRefPtr.h> + +namespace WebKit { + +class WebEventPrivate : public WebCore::Event { +}; + +void WebEvent::reset() +{ + assign(0); +} + +void WebEvent::assign(const WebEvent& other) +{ + WebEventPrivate* p = const_cast<WebEventPrivate*>(other.m_private); + if (p) + p->ref(); + assign(p); +} + +void WebEvent::assign(WebEventPrivate* p) +{ + // p is already ref'd for us by the caller + if (m_private) + m_private->deref(); + m_private = p; +} + +WebEvent::WebEvent(const WTF::PassRefPtr<WebCore::Event>& event) + : m_private(static_cast<WebEventPrivate*>(event.releaseRef())) +{ +} + +WebString WebEvent::type() const +{ + ASSERT(m_private); + return m_private->type(); +} + +WebNode WebEvent::target() const +{ + ASSERT(m_private); + return WebNode(m_private->target()->toNode()); +} + +WebNode WebEvent::currentTarget() const +{ + ASSERT(m_private); + return WebNode(m_private->currentTarget()->toNode()); +} + +WebEvent::PhaseType WebEvent::eventPhase() const +{ + ASSERT(m_private); + return static_cast<WebEvent::PhaseType>(m_private->eventPhase()); +} + +bool WebEvent::bubbles() const +{ + ASSERT(m_private); + return m_private->bubbles(); +} + +bool WebEvent::cancelable() const +{ + ASSERT(m_private); + return m_private->cancelable(); +} + +bool WebEvent::isUIEvent() const +{ + ASSERT(m_private); + return m_private->isUIEvent(); +} + +bool WebEvent::isMouseEvent() const +{ + ASSERT(m_private); + return m_private->isMouseEvent(); +} + +bool WebEvent::isMutationEvent() const +{ + ASSERT(m_private); + return m_private->isMutationEvent(); +} + +bool WebEvent::isKeyboardEvent() const +{ + ASSERT(m_private); + return m_private->isKeyboardEvent(); +} + +bool WebEvent::isTextEvent() const +{ + ASSERT(m_private); + return m_private->isTextEvent(); +} + +bool WebEvent::isCompositionEvent() const +{ + ASSERT(m_private); + return m_private->isCompositionEvent(); +} + +bool WebEvent::isDragEvent() const +{ + ASSERT(m_private); + return m_private->isDragEvent(); +} + +bool WebEvent::isClipboardEvent() const +{ + ASSERT(m_private); + return m_private->isClipboardEvent(); +} + +bool WebEvent::isMessageEvent() const +{ + ASSERT(m_private); + return m_private->isMessageEvent(); +} + +bool WebEvent::isWheelEvent() const +{ + ASSERT(m_private); + return m_private->isWheelEvent(); +} + +bool WebEvent::isBeforeTextInsertedEvent() const +{ + ASSERT(m_private); + return m_private->isBeforeTextInsertedEvent(); +} + +bool WebEvent::isOverflowEvent() const +{ + ASSERT(m_private); + return m_private->isOverflowEvent(); +} + +bool WebEvent::isPageTransitionEvent() const +{ + ASSERT(m_private); + return m_private->isPageTransitionEvent(); +} + +bool WebEvent::isPopStateEvent() const +{ + ASSERT(m_private); + return m_private->isPopStateEvent(); +} + +bool WebEvent::isProgressEvent() const +{ + ASSERT(m_private); + return m_private->isProgressEvent(); +} + +bool WebEvent::isXMLHttpRequestProgressEvent() const +{ + ASSERT(m_private); + return m_private->isXMLHttpRequestProgressEvent(); +} + +bool WebEvent::isWebKitAnimationEvent() const +{ + ASSERT(m_private); + return m_private->isWebKitAnimationEvent(); +} + +bool WebEvent::isWebKitTransitionEvent() const +{ + ASSERT(m_private); + return m_private->isWebKitTransitionEvent(); +} + +bool WebEvent::isBeforeLoadEvent() const +{ + ASSERT(m_private); + return m_private->isBeforeLoadEvent(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebEventListener.cpp b/WebKit/chromium/src/WebEventListener.cpp new file mode 100644 index 0000000..8d9a887 --- /dev/null +++ b/WebKit/chromium/src/WebEventListener.cpp @@ -0,0 +1,64 @@ +/*
+ * Copyright (C) 2010 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 "WebEventListener.h"
+
+#include "WebEventListenerPrivate.h"
+
+namespace WebKit {
+
+WebEventListener::WebEventListener()
+ : m_private(new WebEventListenerPrivate(this))
+{
+}
+
+WebEventListener::~WebEventListener()
+{
+ m_private->webEventListenerDeleted();
+ delete m_private;
+}
+
+void WebEventListener::notifyEventListenerDeleted(EventListenerWrapper* wrapper)
+{
+ m_private->eventListenerDeleted(wrapper);
+}
+
+EventListenerWrapper* WebEventListener::createEventListenerWrapper(const WebString& eventType, bool useCapture, Node* node)
+{
+ return m_private->createEventListenerWrapper(eventType, useCapture, node);
+}
+
+EventListenerWrapper* WebEventListener::getEventListenerWrapper(const WebString& eventType, bool useCapture, Node* node)
+{
+ return m_private->getEventListenerWrapper(eventType, useCapture, node);
+}
+
+} // namespace WebKit
diff --git a/WebKit/chromium/src/WebEventListenerPrivate.cpp b/WebKit/chromium/src/WebEventListenerPrivate.cpp new file mode 100644 index 0000000..bd14baf --- /dev/null +++ b/WebKit/chromium/src/WebEventListenerPrivate.cpp @@ -0,0 +1,87 @@ +/*
+ * Copyright (C) 2010 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 "WebEventListenerPrivate.h"
+
+#include "EventListenerWrapper.h"
+#include "WebEventListener.h"
+
+namespace WebKit {
+
+WebEventListenerPrivate::WebEventListenerPrivate(WebEventListener* webEventListener)
+ : m_webEventListener(webEventListener)
+{
+}
+
+WebEventListenerPrivate::~WebEventListenerPrivate()
+{
+}
+
+EventListenerWrapper* WebEventListenerPrivate::createEventListenerWrapper(const WebString& eventType, bool useCapture, Node* node)
+{
+ EventListenerWrapper* listenerWrapper = new EventListenerWrapper(m_webEventListener);
+ WebEventListenerPrivate::ListenerInfo listenerInfo(eventType, useCapture, listenerWrapper, node);
+ m_listenerWrappers.append(listenerInfo);
+ return listenerWrapper;
+}
+
+EventListenerWrapper* WebEventListenerPrivate::getEventListenerWrapper(const WebString& eventType, bool useCapture, Node* node)
+{
+ Vector<WebEventListenerPrivate::ListenerInfo>::const_iterator iter;
+ for (iter = m_listenerWrappers.begin(); iter != m_listenerWrappers.end(); ++iter) {
+ if (iter->node == node)
+ return iter->eventListenerWrapper;
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void WebEventListenerPrivate::webEventListenerDeleted()
+{
+ // Notifies all WebEventListenerWrappers that we are going away so they can
+ // invalidate their pointer to us.
+ Vector<WebEventListenerPrivate::ListenerInfo>::const_iterator iter;
+ for (iter = m_listenerWrappers.begin(); iter != m_listenerWrappers.end(); ++iter)
+ iter->eventListenerWrapper->webEventListenerDeleted();
+}
+
+void WebEventListenerPrivate::eventListenerDeleted(EventListenerWrapper* eventListener)
+{
+ for (size_t i = 0; i < m_listenerWrappers.size(); ++i) {
+ if (m_listenerWrappers[i].eventListenerWrapper == eventListener) {
+ m_listenerWrappers.remove(i);
+ return;
+ }
+ }
+ ASSERT_NOT_REACHED();
+}
+
+} // namespace WebKit
diff --git a/WebKit/chromium/src/WebEventListenerPrivate.h b/WebKit/chromium/src/WebEventListenerPrivate.h new file mode 100644 index 0000000..0ba2b5d --- /dev/null +++ b/WebKit/chromium/src/WebEventListenerPrivate.h @@ -0,0 +1,95 @@ +/*
+ * Copyright (C) 2010 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 WebEventListenerPrivate_h
+#define WebEventListenerPrivate_h
+
+#include "WebString.h"
+
+#include <wtf/Vector.h>
+
+namespace WebCore {
+class Node;
+}
+
+using namespace WebCore;
+
+namespace WebKit {
+
+class EventListenerWrapper;
+class WebEventListener;
+
+class WebEventListenerPrivate {
+public:
+ WebEventListenerPrivate(WebEventListener* webEventListener);
+ ~WebEventListenerPrivate();
+
+ EventListenerWrapper* createEventListenerWrapper(
+ const WebString& eventType, bool useCapture, Node* node);
+
+ // Gets the ListenerEventWrapper for a specific node.
+ // Used by WebNode::removeEventListener().
+ EventListenerWrapper* getEventListenerWrapper(
+ const WebString& eventType, bool useCapture, Node* node);
+
+ // Called by the WebEventListener when it is about to be deleted.
+ void webEventListenerDeleted();
+
+ // Called by the EventListenerWrapper when it is about to be deleted.
+ void eventListenerDeleted(EventListenerWrapper* eventListener);
+
+ struct ListenerInfo {
+ ListenerInfo(const WebString& eventType, bool useCapture,
+ EventListenerWrapper* eventListenerWrapper,
+ Node* node)
+ : eventType(eventType)
+ , useCapture(useCapture)
+ , eventListenerWrapper(eventListenerWrapper)
+ , node(node)
+ {
+ }
+
+ WebString eventType;
+ bool useCapture;
+ EventListenerWrapper* eventListenerWrapper;
+ Node* node;
+ };
+
+private:
+ WebEventListener* m_webEventListener;
+
+ // We keep a list of the wrapper for the WebKit EventListener, it is needed
+ // to implement WebNode::removeEventListener().
+ Vector<ListenerInfo> m_listenerWrappers;
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/WebKit/chromium/src/WebFrameImpl.cpp b/WebKit/chromium/src/WebFrameImpl.cpp index ab6769f..2f911f1 100644 --- a/WebKit/chromium/src/WebFrameImpl.cpp +++ b/WebKit/chromium/src/WebFrameImpl.cpp @@ -84,7 +84,6 @@ #include "Editor.h" #include "EventHandler.h" #include "FormState.h" -#include "FrameChromium.h" #include "FrameLoader.h" #include "FrameLoadRequest.h" #include "FrameTree.h" @@ -104,6 +103,7 @@ #include "PlatformContextSkia.h" #include "PrintContext.h" #include "RenderFrame.h" +#include "RenderTreeAsText.h" #include "RenderView.h" #include "RenderWidget.h" #include "ReplaceSelectionCommand.h" @@ -120,8 +120,10 @@ #include "SubstituteData.h" #include "TextAffinity.h" #include "TextIterator.h" +#include "WebAnimationControllerImpl.h" #include "WebConsoleMessage.h" #include "WebDataSourceImpl.h" +#include "WebDocument.h" #include "WebFindOptions.h" #include "WebFormElement.h" #include "WebFrameClient.h" @@ -142,11 +144,11 @@ #include <wtf/CurrentTime.h> -#if PLATFORM(DARWIN) +#if OS(DARWIN) #include "LocalCurrentGraphicsContext.h" #endif -#if PLATFORM(LINUX) +#if OS(LINUX) #include <gdk/gdk.h> #endif @@ -154,6 +156,8 @@ using namespace WebCore; namespace WebKit { +static int frameCount = 0; + // Key for a StatsCounter tracking how many WebFrames are active. static const char* const webFrameActiveCount = "WebFrameActiveCount"; @@ -329,6 +333,11 @@ private: // WebFrame ------------------------------------------------------------------- +int WebFrame::instanceCount() +{ + return frameCount; +} + WebFrame* WebFrame::frameForEnteredContext() { Frame* frame = @@ -343,11 +352,22 @@ WebFrame* WebFrame::frameForCurrentContext() return WebFrameImpl::fromFrame(frame); } +WebFrame* WebFrame::fromFrameOwnerElement(const WebElement& element) +{ + return WebFrameImpl::fromFrameOwnerElement( + PassRefPtr<Element>(element).get()); +} + WebString WebFrameImpl::name() const { return m_frame->tree()->name(); } +void WebFrameImpl::clearName() +{ + m_frame->tree()->clearName(); +} + WebURL WebFrameImpl::url() const { const WebDataSource* ds = dataSource(); @@ -391,6 +411,11 @@ WebURL WebFrameImpl::openSearchDescriptionURL() const return WebURL(); } +WebString WebFrameImpl::encoding() const +{ + return frame()->loader()->encoding(); +} + WebSize WebFrameImpl::scrollOffset() const { FrameView* view = frameView(); @@ -515,6 +540,13 @@ WebFrame* WebFrameImpl::findChildByExpression(const WebString& xpath) const return fromFrame(frameElement->contentFrame()); } +WebDocument WebFrameImpl::document() const +{ + if (!m_frame || !m_frame->document()) + return WebDocument(); + return WebDocument(m_frame->document()); +} + void WebFrameImpl::forms(WebVector<WebFormElement>& results) const { if (!m_frame) @@ -533,6 +565,11 @@ void WebFrameImpl::forms(WebVector<WebFormElement>& results) const results.swap(temp); } +WebAnimationController* WebFrameImpl::animationController() +{ + return &m_animationController; +} + WebSecurityOrigin WebFrameImpl::securityOrigin() const { if (!m_frame || !m_frame->document()) @@ -559,7 +596,7 @@ NPObject* WebFrameImpl::windowObject() const void WebFrameImpl::bindToWindowObject(const WebString& name, NPObject* object) { ASSERT(m_frame); - if (!m_frame || !m_frame->script()->isEnabled()) + if (!m_frame || !m_frame->script()->canExecuteScripts()) return; String key = name; @@ -576,19 +613,6 @@ void WebFrameImpl::executeScript(const WebScriptSource& source) 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) @@ -1037,7 +1061,7 @@ WebString WebFrameImpl::selectionAsText() const return WebString(); String text = range->text(); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) replaceNewlinesWithWindowsStyleNewlines(text); #endif replaceNBSPWithSpace(text); @@ -1053,6 +1077,28 @@ WebString WebFrameImpl::selectionAsMarkup() const return createMarkup(range.get(), 0); } +void WebFrameImpl::selectWordAroundPosition(Frame* frame, VisiblePosition pos) +{ + VisibleSelection selection(pos); + selection.expandUsingGranularity(WordGranularity); + + if (selection.isRange()) + frame->setSelectionGranularity(WordGranularity); + + if (frame->shouldChangeSelection(selection)) + frame->selection()->setSelection(selection); +} + +bool WebFrameImpl::selectWordAroundCaret() +{ + SelectionController* controller = frame()->selection(); + ASSERT(!controller->isNone()); + if (controller->isNone() || controller->isRange()) + return false; + selectWordAroundPosition(frame(), controller->selection().visibleStart()); + return true; +} + int WebFrameImpl::printBegin(const WebSize& pageSize) { ASSERT(!frame()->document()->isFrameSet()); @@ -1087,10 +1133,10 @@ float WebFrameImpl::printPage(int page, WebCanvas* canvas) return 0; } -#if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD) +#if OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) PlatformContextSkia context(canvas); GraphicsContext spool(&context); -#elif PLATFORM(DARWIN) +#elif OS(DARWIN) GraphicsContext spool(canvas); LocalCurrentGraphicsContext localContext(&spool); #endif @@ -1212,8 +1258,7 @@ void WebFrameImpl::stopFinding(bool clearSelection) cancelPendingScopingEffort(); // Remove all markers for matches found and turn off the highlighting. - if (!parent()) - frame()->document()->removeMarkers(DocumentMarker::TextMatch); + frame()->document()->removeMarkers(DocumentMarker::TextMatch); frame()->setMarkedTextMatchesAreHighlighted(false); // Let the frame know that we don't want tickmarks or highlighting anymore. @@ -1456,9 +1501,39 @@ WebString WebFrameImpl::contentAsMarkup() const return createFullMarkup(m_frame->document()); } -// WebFrameImpl public --------------------------------------------------------- +WebString WebFrameImpl::renderTreeAsText() const +{ + return externalRepresentation(m_frame); +} + +WebString WebFrameImpl::counterValueForElementById(const WebString& id) const +{ + if (!m_frame) + return WebString(); + + Element* element = m_frame->document()->getElementById(id); + if (!element) + return WebString(); + + return counterValueForElement(element); +} + +int WebFrameImpl::pageNumberForElementById(const WebString& id, + float pageWidthInPixels, + float pageHeightInPixels) const +{ + if (!m_frame) + return -1; + + Element* element = m_frame->document()->getElementById(id); + if (!element) + return -1; + + FloatSize pageSize(pageWidthInPixels, pageHeightInPixels); + return PrintContext::pageNumberForElement(element, pageSize); +} -int WebFrameImpl::m_liveObjectCount = 0; +// WebFrameImpl public --------------------------------------------------------- PassRefPtr<WebFrameImpl> WebFrameImpl::create(WebFrameClient* client) { @@ -1477,15 +1552,16 @@ WebFrameImpl::WebFrameImpl(WebFrameClient* client) , m_framesScopingCount(-1) , m_scopingComplete(false) , m_nextInvalidateAfter(0) + , m_animationController(this) { ChromiumBridge::incrementStatsCounter(webFrameActiveCount); - m_liveObjectCount++; + frameCount++; } WebFrameImpl::~WebFrameImpl() { ChromiumBridge::decrementStatsCounter(webFrameActiveCount); - m_liveObjectCount--; + frameCount--; cancelPendingScopingEffort(); clearPasswordListeners(); @@ -1631,6 +1707,19 @@ WebFrameImpl* WebFrameImpl::fromFrame(Frame* frame) return static_cast<FrameLoaderClientImpl*>(frame->loader()->client())->webFrame(); } +WebFrameImpl* WebFrameImpl::fromFrameOwnerElement(Element* element) +{ + if (!element + || !element->isFrameOwnerElement() + || (!element->hasTagName(HTMLNames::iframeTag) + && !element->hasTagName(HTMLNames::frameTag))) + return 0; + + HTMLFrameOwnerElement* frameElement = + static_cast<HTMLFrameOwnerElement*>(element); + return fromFrame(frameElement->contentFrame()); +} + WebViewImpl* WebFrameImpl::viewImpl() const { if (!m_frame) diff --git a/WebKit/chromium/src/WebFrameImpl.h b/WebKit/chromium/src/WebFrameImpl.h index 2cd332a..ccba6d4 100644 --- a/WebKit/chromium/src/WebFrameImpl.h +++ b/WebKit/chromium/src/WebFrameImpl.h @@ -39,6 +39,8 @@ #include <wtf/OwnPtr.h> #include <wtf/RefCounted.h> +#include "WebAnimationControllerImpl.h" + namespace WebCore { class HistoryItem; class KURL; @@ -62,9 +64,11 @@ class WebFrameImpl : public WebFrame, public RefCounted<WebFrameImpl> { public: // WebFrame methods: virtual WebString name() const; + virtual void clearName(); virtual WebURL url() const; virtual WebURL favIconURL() const; virtual WebURL openSearchDescriptionURL() const; + virtual WebString encoding() const; virtual WebSize scrollOffset() const; virtual WebSize contentsSize() const; virtual int contentsPreferredWidth() const; @@ -82,14 +86,14 @@ public: virtual WebFrame* traversePrevious(bool wrap) const; virtual WebFrame* findChildByName(const WebString&) const; virtual WebFrame* findChildByExpression(const WebString&) const; + virtual WebDocument document() const; virtual void forms(WebVector<WebFormElement>&) const; + virtual WebAnimationController* animationController(); 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); @@ -137,6 +141,7 @@ public: virtual WebRange selectionRange() const; virtual WebString selectionAsText() const; virtual WebString selectionAsMarkup() const; + virtual bool selectWordAroundCaret(); virtual int printBegin(const WebSize& pageSize); virtual float printPage(int pageToPrint, WebCanvas*); virtual float getPrintPageShrink(int page); @@ -157,12 +162,15 @@ public: virtual WebURL completeURL(const WebString& url) const; virtual WebString contentAsText(size_t maxChars) const; virtual WebString contentAsMarkup() const; + virtual WebString renderTreeAsText() const; + virtual WebString counterValueForElementById(const WebString& id) const; + virtual int pageNumberForElementById(const WebString& id, + float pageWidthInPixels, + float pageHeightInPixels) 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*); @@ -174,6 +182,7 @@ public: void createFrameView(); static WebFrameImpl* fromFrame(WebCore::Frame* frame); + static WebFrameImpl* fromFrameOwnerElement(WebCore::Element* element); WebViewImpl* viewImpl() const; @@ -214,6 +223,8 @@ public: WebFrameClient* client() const { return m_client; } void dropClient() { m_client = 0; } + static void selectWordAroundPosition(WebCore::Frame*, WebCore::VisiblePosition); + private: class DeferredScopeStringMatches; friend class DeferredScopeStringMatches; @@ -276,9 +287,6 @@ private: void loadJavaScriptURL(const WebCore::KURL&); - // Used to check for leaks of this object. - static int m_liveObjectCount; - FrameLoaderClientImpl m_frameLoaderClient; WebFrameClient* m_client; @@ -348,6 +356,9 @@ private: typedef HashMap<RefPtr<WebCore::HTMLInputElement>, WebPasswordAutocompleteListener*> PasswordListenerMap; PasswordListenerMap m_passwordListeners; + + // Keeps a reference to the frame's WebAnimationController. + WebAnimationControllerImpl m_animationController; }; } // namespace WebKit diff --git a/WebKit/chromium/src/WebGlyphCache.cpp b/WebKit/chromium/src/WebGlyphCache.cpp new file mode 100644 index 0000000..272c6cd --- /dev/null +++ b/WebKit/chromium/src/WebGlyphCache.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 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 "WebGlyphCache.h" + +#include "GlyphPageTreeNode.h" + +using namespace WebCore; + +namespace WebKit { + +size_t WebGlyphCache::pageCount() +{ + return GlyphPageTreeNode::treeGlyphPageCount(); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebHTTPBody.cpp b/WebKit/chromium/src/WebHTTPBody.cpp index 335ed5c..3d40869 100644 --- a/WebKit/chromium/src/WebHTTPBody.cpp +++ b/WebKit/chromium/src/WebHTTPBody.cpp @@ -32,6 +32,7 @@ #include "WebHTTPBody.h" #include "FormData.h" +#include "WebFileInfo.h" using namespace WebCore; @@ -78,11 +79,17 @@ bool WebHTTPBody::elementAt(size_t index, Element& result) const result.type = Element::TypeData; result.data.assign(element.m_data.data(), element.m_data.size()); result.filePath.reset(); + result.fileStart = 0; + result.fileLength = 0; + result.fileInfo.modificationTime = 0.0; break; case FormDataElement::encodedFile: result.type = Element::TypeFile; result.data.reset(); result.filePath = element.m_filename; + result.fileStart = 0; // FIXME: to be set from FormData. + result.fileLength = -1; // FIXME: to be set from FormData. + result.fileInfo.modificationTime = 0.0; // FIXME: to be set from FormData. break; default: ASSERT_NOT_REACHED(); @@ -106,6 +113,11 @@ void WebHTTPBody::appendFile(const WebString& filePath) m_private->appendFile(filePath); } +void WebHTTPBody::appendFile(const WebString& filePath, long long fileStart, long long fileLength, const WebFileInfo& fileInfo) +{ + // FIXME: to be implemented. +} + long long WebHTTPBody::identifier() const { ASSERT(!isNull()); diff --git a/WebKit/chromium/src/WebHistoryItem.cpp b/WebKit/chromium/src/WebHistoryItem.cpp index d4ee50a..4ca8cc7 100644 --- a/WebKit/chromium/src/WebHistoryItem.cpp +++ b/WebKit/chromium/src/WebHistoryItem.cpp @@ -218,6 +218,18 @@ void WebHistoryItem::setDocumentState(const WebVector<WebString>& state) m_private->setDocumentState(ds); } +long long WebHistoryItem::documentSequenceNumber() const +{ + ASSERT(!isNull()); + return m_private->documentSequenceNumber(); +} + +void WebHistoryItem::setDocumentSequenceNumber(long long documentSequenceNumber) +{ + ensureMutable(); + m_private->setDocumentSequenceNumber(documentSequenceNumber); +} + WebString WebHistoryItem::httpContentType() const { ASSERT(!isNull()); diff --git a/WebKit/chromium/src/WebInputElement.cpp b/WebKit/chromium/src/WebInputElement.cpp index d403120..9fd317f 100644 --- a/WebKit/chromium/src/WebInputElement.cpp +++ b/WebKit/chromium/src/WebInputElement.cpp @@ -56,6 +56,11 @@ WebInputElement::operator WTF::PassRefPtr<HTMLInputElement>() const return PassRefPtr<HTMLInputElement>(static_cast<HTMLInputElement*>(m_private)); } +bool WebInputElement::autoComplete() const +{ + return constUnwrap<HTMLInputElement>()->autoComplete(); +} + bool WebInputElement::isEnabledFormControl() const { return constUnwrap<HTMLInputElement>()->isEnabledFormControl(); @@ -70,7 +75,12 @@ WebString WebInputElement::formControlType() const { return constUnwrap<HTMLInputElement>()->formControlType(); } - + +bool WebInputElement::isActivatedSubmit() const +{ + return constUnwrap<HTMLInputElement>()->isActivatedSubmit(); +} + void WebInputElement::setActivatedSubmit(bool activated) { unwrap<HTMLInputElement>()->setActivatedSubmit(activated); diff --git a/WebKit/chromium/src/WebKit.cpp b/WebKit/chromium/src/WebKit.cpp index a129f85..a8e1851 100644 --- a/WebKit/chromium/src/WebKit.cpp +++ b/WebKit/chromium/src/WebKit.cpp @@ -33,6 +33,7 @@ #include "AtomicString.h" #include "DOMTimer.h" +#include "Logging.h" #include "Page.h" #include "RuntimeEnabledFeatures.h" #include "TextEncoding.h" @@ -94,6 +95,13 @@ bool layoutTestMode() return s_layoutTestMode; } +void enableLogChannel(const char* name) +{ + WTFLogChannel* channel = WebCore::getChannelFromName(name); + if (channel) + channel->state = WTFLogChannelOn; +} + void resetPluginCache(bool reloadPages) { WebCore::Page::refreshPlugins(reloadPages); diff --git a/WebKit/chromium/src/WebMediaPlayerClientImpl.cpp b/WebKit/chromium/src/WebMediaPlayerClientImpl.cpp index c2a3535..b1f1f03 100644 --- a/WebKit/chromium/src/WebMediaPlayerClientImpl.cpp +++ b/WebKit/chromium/src/WebMediaPlayerClientImpl.cpp @@ -85,10 +85,16 @@ void WebMediaPlayerClientImpl::readyStateChanged() m_mediaPlayer->readyStateChanged(); } -void WebMediaPlayerClientImpl::volumeChanged() +void WebMediaPlayerClientImpl::volumeChanged(float newVolume) { ASSERT(m_mediaPlayer); - m_mediaPlayer->volumeChanged(); + m_mediaPlayer->volumeChanged(newVolume); +} + +void WebMediaPlayerClientImpl::muteChanged(bool newMute) +{ + ASSERT(m_mediaPlayer); + m_mediaPlayer->muteChanged(newMute); } void WebMediaPlayerClientImpl::timeChanged() diff --git a/WebKit/chromium/src/WebMediaPlayerClientImpl.h b/WebKit/chromium/src/WebMediaPlayerClientImpl.h index 7f087d0..4adbed2 100644 --- a/WebKit/chromium/src/WebMediaPlayerClientImpl.h +++ b/WebKit/chromium/src/WebMediaPlayerClientImpl.h @@ -53,7 +53,8 @@ public: // WebMediaPlayerClient methods: virtual void networkStateChanged(); virtual void readyStateChanged(); - virtual void volumeChanged(); + virtual void volumeChanged(float); + virtual void muteChanged(bool); virtual void timeChanged(); virtual void repaint(); virtual void durationChanged(); diff --git a/WebKit/chromium/src/WebMutationEvent.cpp b/WebKit/chromium/src/WebMutationEvent.cpp new file mode 100644 index 0000000..511b615 --- /dev/null +++ b/WebKit/chromium/src/WebMutationEvent.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 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 "WebMutationEvent.h" + +#include "MutationEvent.h" + +using namespace WebCore; + +namespace WebKit { + +WebNode WebMutationEvent::relatedNode() const +{ + return WebNode(constUnwrap<MutationEvent>()->relatedNode()); +} + +WebString WebMutationEvent::prevValue() const +{ + return WebString(constUnwrap<MutationEvent>()->prevValue()); +} + +WebString WebMutationEvent::newValue() const +{ + return WebString(constUnwrap<MutationEvent>()->newValue()); +} + +WebString WebMutationEvent::attrName() const +{ + return WebString(constUnwrap<MutationEvent>()->attrName()); +} + +WebMutationEvent::AttrChangeType WebMutationEvent::attrChange() const +{ + return static_cast<AttrChangeType>(constUnwrap<MutationEvent>()->attrChange()); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebNode.cpp b/WebKit/chromium/src/WebNode.cpp index 965c412..9fbf573 100644 --- a/WebKit/chromium/src/WebNode.cpp +++ b/WebKit/chromium/src/WebNode.cpp @@ -35,9 +35,18 @@ #include "Frame.h" #include "FrameLoaderClientImpl.h" #include "Node.h" +#include "NodeList.h" +#include "EventListenerWrapper.h" +#include "WebDocument.h" +#include "WebEvent.h" +#include "WebEventListener.h" #include "WebFrameImpl.h" +#include "WebNodeList.h" #include "WebString.h" +#include "WebVector.h" + +#include "markup.h" #include <wtf/PassRefPtr.h> @@ -61,6 +70,11 @@ void WebNode::assign(const WebNode& other) assign(p); } +WebNode::NodeType WebNode::nodeType() const +{ + return static_cast<NodeType>(m_private->nodeType()); +} + WebNode WebNode::parentNode() const { return PassRefPtr<Node>(const_cast<Node*>(m_private->parentNode())); @@ -71,18 +85,30 @@ WebString WebNode::nodeName() const return m_private->nodeName(); } -WebNode::WebNode(const WTF::PassRefPtr<WebCore::Node>& node) +WebString WebNode::nodeValue() const +{ + return m_private->nodeValue(); +} + +bool WebNode::setNodeValue(const WebString& value) +{ + ExceptionCode exceptionCode = 0; + m_private->setNodeValue(value, exceptionCode); + return !exceptionCode; +} + +WebNode::WebNode(const PassRefPtr<Node>& node) : m_private(static_cast<WebNodePrivate*>(node.releaseRef())) { } -WebNode& WebNode::operator=(const WTF::PassRefPtr<WebCore::Node>& node) +WebNode& WebNode::operator=(const PassRefPtr<Node>& node) { assign(static_cast<WebNodePrivate*>(node.releaseRef())); return *this; } -WebNode::operator WTF::PassRefPtr<WebCore::Node>() const +WebNode::operator PassRefPtr<Node>() const { return PassRefPtr<Node>(const_cast<WebNodePrivate*>(m_private)); } @@ -100,4 +126,72 @@ WebFrame* WebNode::frame() const return WebFrameImpl::fromFrame(m_private->document()->frame()); } +WebDocument WebNode::document() const +{ + return WebDocument(m_private->document()); +} + +WebNode WebNode::firstChild() const +{ + return WebNode(m_private->firstChild()); +} + +WebNode WebNode::lastChild() const +{ + return WebNode(m_private->lastChild()); +} + +WebNode WebNode::previousSibling() const +{ + return WebNode(m_private->previousSibling()); +} + +WebNode WebNode::nextSibling() const +{ + return WebNode(m_private->nextSibling()); +} + +bool WebNode::hasChildNodes() const +{ + return m_private->hasChildNodes(); +} + +WebNodeList WebNode::childNodes() +{ + return WebNodeList(m_private->childNodes()); +} + +WebString WebNode::createMarkup() const +{ + return WebCore::createMarkup(m_private); +} + +bool WebNode::isTextNode() const +{ + return m_private->isTextNode(); +} + +bool WebNode::isElementNode() const +{ + return m_private->isElementNode(); +} + +void WebNode::addEventListener(const WebString& eventType, WebEventListener* listener, bool useCapture) +{ + EventListenerWrapper* listenerWrapper = + listener->createEventListenerWrapper(eventType, useCapture, m_private); + // The listenerWrapper is only referenced by the actual Node. Once it goes + // away, the wrapper notifies the WebEventListener so it can clear its + // pointer to it. + m_private->addEventListener(eventType, adoptRef(listenerWrapper), useCapture); +} + +void WebNode::removeEventListener(const WebString& eventType, WebEventListener* listener, bool useCapture) +{ + EventListenerWrapper* listenerWrapper = + listener->getEventListenerWrapper(eventType, useCapture, m_private); + m_private->removeEventListener(eventType, listenerWrapper, useCapture); + // listenerWrapper is now deleted. +} + } // namespace WebKit diff --git a/WebKit/chromium/src/WebNodeCollection.cpp b/WebKit/chromium/src/WebNodeCollection.cpp new file mode 100644 index 0000000..a9e532f --- /dev/null +++ b/WebKit/chromium/src/WebNodeCollection.cpp @@ -0,0 +1,85 @@ +/* + * 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 "WebNodeCollection.h" + +#include "HTMLCollection.h" +#include "Node.h" +#include <wtf/PassRefPtr.h> + +#include "WebNode.h" + +using namespace WebCore; + +namespace WebKit { + +void WebNodeCollection::reset() +{ + assign(0); +} + +void WebNodeCollection::assign(const WebNodeCollection& other) +{ + HTMLCollection* p = const_cast<HTMLCollection*>(other.m_private); + if (p) + p->ref(); + assign(p); +} + +WebNodeCollection::WebNodeCollection(const PassRefPtr<HTMLCollection>& col) + : m_private(static_cast<HTMLCollection*>(col.releaseRef())) +{ +} + +void WebNodeCollection::assign(HTMLCollection* p) +{ + // p is already ref'd for us by the caller + if (m_private) + m_private->deref(); + m_private = p; +} + +unsigned WebNodeCollection::length() const +{ + return m_private->length(); +} + +WebNode WebNodeCollection::nextItem() const +{ + return WebNode(m_private->nextItem()); +} + +WebNode WebNodeCollection::firstItem() const +{ + return WebNode(m_private->firstItem()); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebNodeList.cpp b/WebKit/chromium/src/WebNodeList.cpp new file mode 100644 index 0000000..f68f961 --- /dev/null +++ b/WebKit/chromium/src/WebNodeList.cpp @@ -0,0 +1,80 @@ +/* + * 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 "WebNodeList.h" + +#include "Node.h" +#include "NodeList.h" +#include <wtf/PassRefPtr.h> + +#include "WebNode.h" + +using namespace WebCore; + +namespace WebKit { + +void WebNodeList::reset() +{ + assign(0); +} + +void WebNodeList::assign(const WebNodeList& other) +{ + NodeList* p = const_cast<NodeList*>(other.m_private); + if (p) + p->ref(); + assign(p); +} + +WebNodeList::WebNodeList(const PassRefPtr<NodeList>& col) + : m_private(static_cast<NodeList*>(col.releaseRef())) +{ +} + +void WebNodeList::assign(NodeList* p) +{ + // p is already ref'd for us by the caller + if (m_private) + m_private->deref(); + m_private = p; +} + +unsigned WebNodeList::length() const +{ + return m_private->length(); +} + +WebNode WebNodeList::item(size_t index) const +{ + return WebNode(m_private->item(index)); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebPageSerializer.cpp b/WebKit/chromium/src/WebPageSerializer.cpp new file mode 100644 index 0000000..1010285 --- /dev/null +++ b/WebKit/chromium/src/WebPageSerializer.cpp @@ -0,0 +1,81 @@ +/* + * 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 "WebPageSerializer.h" + +#include "KURL.h" +#include "PlatformString.h" + +#include "WebFrame.h" +#include "WebPageSerializerClient.h" +#include "WebPageSerializerImpl.h" +#include "WebString.h" +#include "WebURL.h" +#include "WebVector.h" + +using namespace WebCore; + +namespace WebKit { + +bool WebPageSerializer::serialize(WebFrame* frame, + bool recursive, + WebPageSerializerClient* client, + const WebVector<WebURL>& links, + const WebVector<WebString>& localPaths, + const WebString& localDirectoryName) +{ + WebPageSerializerImpl serializerImpl( + frame, recursive, client, links, localPaths, localDirectoryName); + return serializerImpl.serialize(); +} + +WebString WebPageSerializer::generateMetaCharsetDeclaration(const WebString& charset) +{ + return String::format("<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">", + charset.utf8().data()); +} + +WebString WebPageSerializer::generateMarkOfTheWebDeclaration(const WebURL& url) +{ + return String::format("\n<!-- saved from url=(%04d)%s -->\n", + static_cast<int>(url.spec().length()), + url.spec().data()); +} + +WebString WebPageSerializer::generateBaseTagDeclaration(const WebString& baseTarget) +{ + String targetDeclaration; + if (!baseTarget.isEmpty()) + targetDeclaration = String::format(" target=\"%s\"", baseTarget.utf8().data()); + return String::format("<BASE href=\".\"%s>", targetDeclaration.utf8().data()); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebPageSerializerImpl.cpp b/WebKit/chromium/src/WebPageSerializerImpl.cpp new file mode 100644 index 0000000..d5b2b7f --- /dev/null +++ b/WebKit/chromium/src/WebPageSerializerImpl.cpp @@ -0,0 +1,547 @@ +/* + * 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 we handle the base tag better. +// Current status: +// At now the normal way we use to handling base tag is +// a) For those links which have corresponding local saved files, such as +// savable CSS, JavaScript files, they will be written to relative URLs which +// point to local saved file. Why those links can not be resolved as absolute +// file URLs, because if they are resolved as absolute URLs, after moving the +// file location from one directory to another directory, the file URLs will +// be dead links. +// b) For those links which have not corresponding local saved files, such as +// links in A, AREA tags, they will be resolved as absolute URLs. +// c) We comment all base tags when serialzing DOM for the page. +// FireFox also uses above way to handle base tag. +// +// Problem: +// This way can not handle the following situation: +// the base tag is written by JavaScript. +// For example. The page "www.yahoo.com" use +// "document.write('<base href="http://www.yahoo.com/"...');" to setup base URL +// of page when loading page. So when saving page as completed-HTML, we assume +// that we save "www.yahoo.com" to "c:\yahoo.htm". After then we load the saved +// completed-HTML page, then the JavaScript will insert a base tag +// <base href="http://www.yahoo.com/"...> to DOM, so all URLs which point to +// local saved resource files will be resolved as +// "http://www.yahoo.com/yahoo_files/...", which will cause all saved resource +// files can not be loaded correctly. Also the page will be rendered ugly since +// all saved sub-resource files (such as CSS, JavaScript files) and sub-frame +// files can not be fetched. +// Now FireFox, IE and WebKit based Browser all have this problem. +// +// Solution: +// My solution is that we comment old base tag and write new base tag: +// <base href="." ...> after the previous commented base tag. In WebKit, it +// always uses the latest "href" attribute of base tag to set document's base +// URL. Based on this behavior, when we encounter a base tag, we comment it and +// write a new base tag <base href="."> after the previous commented base tag. +// The new added base tag can help engine to locate correct base URL for +// correctly loading local saved resource files. Also I think we need to inherit +// the base target value from document object when appending new base tag. +// If there are multiple base tags in original document, we will comment all old +// base tags and append new base tag after each old base tag because we do not +// know those old base tags are original content or added by JavaScript. If +// they are added by JavaScript, it means when loading saved page, the script(s) +// will still insert base tag(s) to DOM, so the new added base tag(s) can +// override the incorrect base URL and make sure we alway load correct local +// saved resource files. + +#include "config.h" +#include "WebPageSerializerImpl.h" + +#include "Document.h" +#include "DocumentType.h" +#include "Element.h" +#include "FrameLoader.h" +#include "HTMLAllCollection.h" +#include "HTMLElement.h" +#include "HTMLFormElement.h" +#include "HTMLMetaElement.h" +#include "HTMLNames.h" +#include "KURL.h" +#include "PlatformString.h" +#include "StringBuilder.h" +#include "TextEncoding.h" +#include "markup.h" + +#include "DOMUtilitiesPrivate.h" +#include "WebFrameImpl.h" +#include "WebURL.h" +#include "WebVector.h" + +using namespace WebCore; + +namespace WebKit { + +// Maximum length of data buffer which is used to temporary save generated +// html content data. This is a soft limit which might be passed if a very large +// contegious string is found in the page. +static const unsigned dataBufferCapacity = 65536; + +WebPageSerializerImpl::SerializeDomParam::SerializeDomParam(const KURL& currentFrameURL, + const TextEncoding& textEncoding, + Document* doc, + const String& directoryName) + : currentFrameURL(currentFrameURL) + , textEncoding(textEncoding) + , doc(doc) + , directoryName(directoryName) + , hasDoctype(false) + , hasCheckedMeta(false) + , skipMetaElement(0) + , isInScriptOrStyleTag(false) + , hasDocDeclaration(false) +{ + // Cache the value since we check it lots of times. + isHTMLDocument = doc->isHTMLDocument(); +} + +String WebPageSerializerImpl::preActionBeforeSerializeOpenTag( + const Element* element, SerializeDomParam* param, bool* needSkip) +{ + StringBuilder result; + + *needSkip = false; + if (param->isHTMLDocument) { + // Skip the open tag of original META tag which declare charset since we + // have overrided the META which have correct charset declaration after + // serializing open tag of HEAD element. + if (element->hasTagName(HTMLNames::metaTag)) { + const HTMLMetaElement* meta = static_cast<const HTMLMetaElement*>(element); + // Check whether the META tag has declared charset or not. + String equiv = meta->httpEquiv(); + if (equalIgnoringCase(equiv, "content-type")) { + String content = meta->content(); + if (content.length() && content.contains("charset", false)) { + // Find META tag declared charset, we need to skip it when + // serializing DOM. + param->skipMetaElement = element; + *needSkip = true; + } + } + } else if (element->hasTagName(HTMLNames::htmlTag)) { + // Check something before processing the open tag of HEAD element. + // First we add doc type declaration if original doc has it. + if (!param->hasDoctype) { + param->hasDoctype = true; + result.append(createMarkup(param->doc->doctype())); + } + + // Add MOTW declaration before html tag. + // See http://msdn2.microsoft.com/en-us/library/ms537628(VS.85).aspx. + result.append(WebPageSerializer::generateMarkOfTheWebDeclaration(param->currentFrameURL)); + } else if (element->hasTagName(HTMLNames::baseTag)) { + // Comment the BASE tag when serializing dom. + result.append("<!--"); + } + } else { + // Write XML declaration. + if (!param->hasDocDeclaration) { + param->hasDocDeclaration = true; + // Get encoding info. + String xmlEncoding = param->doc->xmlEncoding(); + if (xmlEncoding.isEmpty()) + xmlEncoding = param->doc->frame()->loader()->encoding(); + if (xmlEncoding.isEmpty()) + xmlEncoding = UTF8Encoding().name(); + result.append("<?xml version=\""); + result.append(param->doc->xmlVersion()); + result.append("\" encoding=\""); + result.append(xmlEncoding); + if (param->doc->xmlStandalone()) + result.append("\" standalone=\"yes"); + result.append("\"?>\n"); + } + // Add doc type declaration if original doc has it. + if (!param->hasDoctype) { + param->hasDoctype = true; + result.append(createMarkup(param->doc->doctype())); + } + } + return result.toString(); +} + +String WebPageSerializerImpl::postActionAfterSerializeOpenTag( + const Element* element, SerializeDomParam* param) +{ + StringBuilder result; + + param->hasAddedContentsBeforeEnd = false; + if (!param->isHTMLDocument) + return result.toString(); + // Check after processing the open tag of HEAD element + if (!param->hasCheckedMeta + && element->hasTagName(HTMLNames::headTag)) { + param->hasCheckedMeta = true; + // Check meta element. WebKit only pre-parse the first 512 bytes + // of the document. If the whole <HEAD> is larger and meta is the + // end of head part, then this kind of pages aren't decoded correctly + // because of this issue. So when we serialize the DOM, we need to + // make sure the meta will in first child of head tag. + // See http://bugs.webkit.org/show_bug.cgi?id=16621. + // First we generate new content for writing correct META element. + result.append(WebPageSerializer::generateMetaCharsetDeclaration( + String(param->textEncoding.name()))); + + param->hasAddedContentsBeforeEnd = true; + // Will search each META which has charset declaration, and skip them all + // in PreActionBeforeSerializeOpenTag. + } else if (element->hasTagName(HTMLNames::scriptTag) + || element->hasTagName(HTMLNames::styleTag)) { + param->isInScriptOrStyleTag = true; + } + + return result.toString(); +} + +String WebPageSerializerImpl::preActionBeforeSerializeEndTag( + const Element* element, SerializeDomParam* param, bool* needSkip) +{ + String result; + + *needSkip = false; + if (!param->isHTMLDocument) + return result; + // Skip the end tag of original META tag which declare charset. + // Need not to check whether it's META tag since we guarantee + // skipMetaElement is definitely META tag if it's not 0. + if (param->skipMetaElement == element) + *needSkip = true; + else if (element->hasTagName(HTMLNames::scriptTag) + || element->hasTagName(HTMLNames::styleTag)) { + ASSERT(param->isInScriptOrStyleTag); + param->isInScriptOrStyleTag = false; + } + + return result; +} + +// After we finish serializing end tag of a element, we give the target +// element a chance to do some post work to add some additional data. +String WebPageSerializerImpl::postActionAfterSerializeEndTag( + const Element* element, SerializeDomParam* param) +{ + StringBuilder result; + + if (!param->isHTMLDocument) + return result.toString(); + // Comment the BASE tag when serializing DOM. + if (element->hasTagName(HTMLNames::baseTag)) { + result.append("-->"); + // Append a new base tag declaration. + result.append(WebPageSerializer::generateBaseTagDeclaration( + param->doc->baseTarget())); + } + + return result.toString(); +} + +void WebPageSerializerImpl::saveHTMLContentToBuffer( + const String& result, SerializeDomParam* param) +{ + m_dataBuffer.append(result); + encodeAndFlushBuffer(WebPageSerializerClient::CurrentFrameIsNotFinished, + param, + 0); +} + +void WebPageSerializerImpl::encodeAndFlushBuffer( + WebPageSerializerClient::PageSerializationStatus status, + SerializeDomParam* param, + bool force) +{ + // Data buffer is not full nor do we want to force flush. + if (!force && m_dataBuffer.length() <= dataBufferCapacity) + return; + + String content = m_dataBuffer.toString(); + m_dataBuffer.clear(); + + // Convert the unicode content to target encoding + CString encodedContent = param->textEncoding.encode( + content.characters(), content.length(), EntitiesForUnencodables); + + // Send result to the client. + m_client->didSerializeDataForFrame(param->currentFrameURL, + WebCString(encodedContent.data(), encodedContent.length()), + status); +} + +void WebPageSerializerImpl::openTagToString(const Element* element, + SerializeDomParam* param) +{ + // FIXME: use StringBuilder instead of String. + bool needSkip; + // Do pre action for open tag. + String result = preActionBeforeSerializeOpenTag(element, param, &needSkip); + if (needSkip) + return; + // Add open tag + result += "<" + element->nodeName(); + // Go through all attributes and serialize them. + const NamedNodeMap *attrMap = element->attributes(true); + if (attrMap) { + unsigned numAttrs = attrMap->length(); + for (unsigned i = 0; i < numAttrs; i++) { + result += " "; + // Add attribute pair + const Attribute *attribute = attrMap->attributeItem(i); + result += attribute->name().toString(); + result += "=\""; + if (!attribute->value().isEmpty()) { + const String& attrValue = attribute->value(); + + // Check whether we need to replace some resource links + // with local resource paths. + const QualifiedName& attrName = attribute->name(); + if (elementHasLegalLinkAttribute(element, attrName)) { + // For links start with "javascript:", we do not change it. + if (attrValue.startsWith("javascript:", false)) + result += attrValue; + else { + // Get the absolute link + String completeURL = param->doc->completeURL(attrValue); + // Check whether we have local files for those link. + if (m_localLinks.contains(completeURL)) { + if (!m_localDirectoryName.isEmpty()) + result += "./" + m_localDirectoryName + "/"; + result += m_localLinks.get(completeURL); + } else + result += completeURL; + } + } else { + if (param->isHTMLDocument) + result += m_htmlEntities.convertEntitiesInString(attrValue); + else + result += m_xmlEntities.convertEntitiesInString(attrValue); + } + } + result += "\""; + } + } + + // Do post action for open tag. + String addedContents = postActionAfterSerializeOpenTag(element, param); + // Complete the open tag for element when it has child/children. + if (element->hasChildNodes() || param->hasAddedContentsBeforeEnd) + result += ">"; + // Append the added contents generate in post action of open tag. + result += addedContents; + // Save the result to data buffer. + saveHTMLContentToBuffer(result, param); +} + +// Serialize end tag of an specified element. +void WebPageSerializerImpl::endTagToString(const Element* element, + SerializeDomParam* param) +{ + bool needSkip; + // Do pre action for end tag. + String result = preActionBeforeSerializeEndTag(element, + param, + &needSkip); + if (needSkip) + return; + // Write end tag when element has child/children. + if (element->hasChildNodes() || param->hasAddedContentsBeforeEnd) { + result += "</"; + result += element->nodeName(); + result += ">"; + } else { + // Check whether we have to write end tag for empty element. + if (param->isHTMLDocument) { + result += ">"; + const HTMLElement* htmlElement = + static_cast<const HTMLElement*>(element); + if (htmlElement->endTagRequirement() == TagStatusRequired) { + // We need to write end tag when it is required. + result += "</"; + result += element->nodeName(); + result += ">"; + } + } else { + // For xml base document. + result += " />"; + } + } + // Do post action for end tag. + result += postActionAfterSerializeEndTag(element, param); + // Save the result to data buffer. + saveHTMLContentToBuffer(result, param); +} + +void WebPageSerializerImpl::buildContentForNode(const Node* node, + SerializeDomParam* param) +{ + switch (node->nodeType()) { + case Node::ELEMENT_NODE: + // Process open tag of element. + openTagToString(static_cast<const Element*>(node), param); + // Walk through the children nodes and process it. + for (const Node *child = node->firstChild(); child; child = child->nextSibling()) + buildContentForNode(child, param); + // Process end tag of element. + endTagToString(static_cast<const Element*>(node), param); + break; + case Node::TEXT_NODE: + saveHTMLContentToBuffer(createMarkup(node), param); + break; + case Node::ATTRIBUTE_NODE: + case Node::DOCUMENT_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + // Should not exist. + ASSERT_NOT_REACHED(); + break; + // Document type node can be in DOM? + case Node::DOCUMENT_TYPE_NODE: + param->hasDoctype = true; + default: + // For other type node, call default action. + saveHTMLContentToBuffer(createMarkup(node), param); + break; + } +} + +WebPageSerializerImpl::WebPageSerializerImpl(WebFrame* frame, + bool recursiveSerialization, + WebPageSerializerClient* client, + const WebVector<WebURL>& links, + const WebVector<WebString>& localPaths, + const WebString& localDirectoryName) + : m_client(client) + , m_recursiveSerialization(recursiveSerialization) + , m_framesCollected(false) + , m_localDirectoryName(localDirectoryName) + , m_htmlEntities(false) + , m_xmlEntities(true) +{ + // Must specify available webframe. + ASSERT(frame); + m_specifiedWebFrameImpl = static_cast<WebFrameImpl*>(frame); + // Make sure we have non 0 client. + ASSERT(client); + // Build local resources map. + ASSERT(links.size() == localPaths.size()); + for (size_t i = 0; i < links.size(); i++) { + KURL url = links[i]; + ASSERT(!m_localLinks.contains(url.string())); + m_localLinks.set(url.string(), localPaths[i]); + } + + ASSERT(!m_dataBuffer.length()); +} + +void WebPageSerializerImpl::collectTargetFrames() +{ + ASSERT(!m_framesCollected); + m_framesCollected = true; + + // First, process main frame. + m_frames.append(m_specifiedWebFrameImpl); + // Return now if user only needs to serialize specified frame, not including + // all sub-frames. + if (!m_recursiveSerialization) + return; + // Collect all frames inside the specified frame. + for (int i = 0; i < static_cast<int>(m_frames.size()); ++i) { + WebFrameImpl* currentFrame = m_frames[i]; + // Get current using document. + Document* currentDoc = currentFrame->frame()->document(); + // Go through sub-frames. + RefPtr<HTMLAllCollection> all = currentDoc->all(); + for (Node* node = all->firstItem(); node; node = all->nextItem()) { + if (!node->isHTMLElement()) + continue; + Element* element = static_cast<Element*>(node); + WebFrameImpl* webFrame = + WebFrameImpl::fromFrameOwnerElement(element); + if (webFrame) + m_frames.append(webFrame); + } + } +} + +bool WebPageSerializerImpl::serialize() +{ + // Collect target frames. + if (!m_framesCollected) + collectTargetFrames(); + bool didSerialization = false; + // Get KURL for main frame. + KURL mainPageURL = m_specifiedWebFrameImpl->frame()->loader()->url(); + + // Go through all frames for serializing DOM for whole page, include + // sub-frames. + for (int i = 0; i < static_cast<int>(m_frames.size()); ++i) { + // Get current serializing frame. + WebFrameImpl* currentFrame = m_frames[i]; + // Get current using document. + Document* currentDoc = currentFrame->frame()->document(); + // Get current frame's URL. + const KURL& currentFrameURL = currentFrame->frame()->loader()->url(); + + // Check whether we have done this document. + if (m_localLinks.contains(currentFrameURL.string())) { + // A new document, we will serialize it. + didSerialization = true; + // Get target encoding for current document. + String encoding = currentFrame->frame()->loader()->encoding(); + // Create the text encoding object with target encoding. + TextEncoding textEncoding(encoding); + // Construct serialize parameter for late processing document. + SerializeDomParam param(currentFrameURL, + encoding.length() ? textEncoding : UTF8Encoding(), + currentDoc, + currentFrameURL == mainPageURL ? m_localDirectoryName : ""); + + // Process current document. + Element* rootElement = currentDoc->documentElement(); + if (rootElement) + buildContentForNode(rootElement, ¶m); + + // Flush the remainder data and finish serializing current frame. + encodeAndFlushBuffer(WebPageSerializerClient::CurrentFrameIsFinished, + ¶m, + 1); + } + } + + // We have done call frames, so we send message to embedder to tell it that + // frames are finished serializing. + ASSERT(!m_dataBuffer.length()); + m_client->didSerializeDataForFrame(KURL(), + WebCString("", 0), + WebPageSerializerClient::AllFramesAreFinished); + return didSerialization; +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/WebPageSerializerImpl.h b/WebKit/chromium/src/WebPageSerializerImpl.h new file mode 100644 index 0000000..8f6a99f --- /dev/null +++ b/WebKit/chromium/src/WebPageSerializerImpl.h @@ -0,0 +1,199 @@ +/* + * 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 WebPageSerializerImpl_h +#define WebPageSerializerImpl_h + +#include "PlatformString.h" +#include "StringBuilder.h" +#include "StringHash.h" +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +#include "WebEntities.h" +#include "WebPageSerializer.h" +#include "WebPageSerializerClient.h" +#include "WebString.h" +#include "WebURL.h" + +namespace WebCore { +class Document; +class Element; +class Node; +class String; +class TextEncoding; +} + +namespace WebKit { +class WebFrameImpl; + +// Get html data by serializing all frames of current page with lists +// which contain all resource links that have local copy. +// contain all saved auxiliary files included all sub frames and resources. +// This function will find out all frames and serialize them to HTML data. +// We have a data buffer to temporary saving generated html data. We will +// sequentially call WebViewDelegate::SendSerializedHtmlData once the data +// buffer is full. See comments of WebViewDelegate::SendSerializedHtmlData +// for getting more information. +class WebPageSerializerImpl { +public: + // Do serialization action. Return false means no available frame has been + // serialized, otherwise return true. + bool serialize(); + + // The parameter specifies which frame need to be serialized. + // The parameter recursive_serialization specifies whether we need to + // serialize all sub frames of the specified frame or not. + // The parameter delegate specifies the pointer of interface + // DomSerializerDelegate provide sink interface which can receive the + // individual chunks of data to be saved. + // The parameter links contain original URLs of all saved links. + // The parameter local_paths contain corresponding local file paths of all + // saved links, which matched with vector:links one by one. + // The parameter local_directory_name is relative path of directory which + // contain all saved auxiliary files included all sub frames and resources. + WebPageSerializerImpl(WebFrame* frame, + bool recursive, + WebPageSerializerClient* client, + const WebVector<WebURL>& links, + const WebVector<WebString>& localPaths, + const WebString& localDirectoryName); + +private: + // Specified frame which need to be serialized; + WebFrameImpl* m_specifiedWebFrameImpl; + // Pointer of WebPageSerializerClient + WebPageSerializerClient* m_client; + // This hash map is used to map resource URL of original link to its local + // file path. + typedef HashMap<WebCore::String, WebCore::String> LinkLocalPathMap; + // local_links_ include all pair of local resource path and corresponding + // original link. + LinkLocalPathMap m_localLinks; + // Data buffer for saving result of serialized DOM data. + WebCore::StringBuilder m_dataBuffer; + // Passing true to recursive_serialization_ indicates we will serialize not + // only the specified frame but also all sub-frames in the specific frame. + // Otherwise we only serialize the specified frame excluded all sub-frames. + bool m_recursiveSerialization; + // Flag indicates whether we have collected all frames which need to be + // serialized or not; + bool m_framesCollected; + // Local directory name of all local resource files. + WebCore::String m_localDirectoryName; + // Vector for saving all frames which need to be serialized. + Vector<WebFrameImpl*> m_frames; + + // Web entities conversion maps. + WebEntities m_htmlEntities; + WebEntities m_xmlEntities; + + struct SerializeDomParam { + // Frame URL of current processing document presented by GURL + const WebCore::KURL& currentFrameURL; + // Current using text encoding object. + const WebCore::TextEncoding& textEncoding; + + // Document object of current frame. + WebCore::Document* doc; + // Local directory name of all local resource files. + const WebCore::String& directoryName; + + // Flag indicates current doc is html document or not. It's a cache value + // of Document.isHTMLDocument(). + bool isHTMLDocument; + // Flag which indicate whether we have met document type declaration. + bool hasDoctype; + // Flag which indicate whether will process meta issue. + bool hasCheckedMeta; + // This meta element need to be skipped when serializing DOM. + const WebCore::Element* skipMetaElement; + // Flag indicates we are in script or style tag. + bool isInScriptOrStyleTag; + // Flag indicates whether we have written xml document declaration. + // It is only used in xml document + bool hasDocDeclaration; + // Flag indicates whether we have added additional contents before end tag. + // This flag will be re-assigned in each call of function + // PostActionAfterSerializeOpenTag and it could be changed in function + // PreActionBeforeSerializeEndTag if the function adds new contents into + // serialization stream. + bool hasAddedContentsBeforeEnd; + + // Constructor. + SerializeDomParam(const WebCore::KURL& currentFrameURL, + const WebCore::TextEncoding& textEncoding, + WebCore::Document* doc, + const WebCore::String& directoryName); + }; + + // Collect all target frames which need to be serialized. + void collectTargetFrames(); + // Before we begin serializing open tag of a element, we give the target + // element a chance to do some work prior to add some additional data. + WebCore::String preActionBeforeSerializeOpenTag(const WebCore::Element* element, + SerializeDomParam* param, + bool* needSkip); + // After we finish serializing open tag of a element, we give the target + // element a chance to do some post work to add some additional data. + WebCore::String postActionAfterSerializeOpenTag(const WebCore::Element* element, + SerializeDomParam* param); + // Before we begin serializing end tag of a element, we give the target + // element a chance to do some work prior to add some additional data. + WebCore::String preActionBeforeSerializeEndTag(const WebCore::Element* element, + SerializeDomParam* param, + bool* needSkip); + // After we finish serializing end tag of a element, we give the target + // element a chance to do some post work to add some additional data. + WebCore::String postActionAfterSerializeEndTag(const WebCore::Element* element, + SerializeDomParam* param); + // Save generated html content to data buffer. + void saveHTMLContentToBuffer(const WebCore::String& content, + SerializeDomParam* param); + // Flushes the content buffer by encoding and sending the content to the + // WebPageSerializerClient. Content is not flushed if the buffer is not full + // unless force is 1. + void encodeAndFlushBuffer(WebPageSerializerClient::PageSerializationStatus status, + SerializeDomParam* param, + bool force); + // Serialize open tag of an specified element. + void openTagToString(const WebCore::Element* element, + SerializeDomParam* param); + // Serialize end tag of an specified element. + void endTagToString(const WebCore::Element* element, + SerializeDomParam* param); + // Build content for a specified node + void buildContentForNode(const WebCore::Node* node, + SerializeDomParam* param); +}; + +} // namespace WebKit + +#endif diff --git a/WebKit/chromium/src/WebPluginContainerImpl.cpp b/WebKit/chromium/src/WebPluginContainerImpl.cpp index 43f3cef..86cac26 100644 --- a/WebKit/chromium/src/WebPluginContainerImpl.cpp +++ b/WebKit/chromium/src/WebPluginContainerImpl.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "WebPluginContainerImpl.h" +#include "Chrome.h" #include "ChromeClientImpl.h" #include "WebCursorInfo.h" #include "WebDataSourceImpl.h" @@ -353,7 +354,7 @@ void WebPluginContainerImpl::handleMouseEvent(MouseEvent* event) WebCursorInfo cursorInfo; bool handled = m_webPlugin->handleInputEvent(webEvent, cursorInfo); -#if !PLATFORM(DARWIN) +#if !OS(DARWIN) // TODO(pkasting): http://b/1119691 This conditional seems exactly // backwards, but if I reverse it, giving focus to a transparent // (windowless) plugin fails. @@ -389,7 +390,7 @@ void WebPluginContainerImpl::handleKeyboardEvent(KeyboardEvent* event) WebCursorInfo cursor_info; bool handled = m_webPlugin->handleInputEvent(webEvent, cursor_info); -#if !PLATFORM(DARWIN) +#if !OS(DARWIN) // TODO(pkasting): http://b/1119691 See above. handled = !handled; #endif diff --git a/WebKit/chromium/src/WebRuntimeFeatures.cpp b/WebKit/chromium/src/WebRuntimeFeatures.cpp index b630a09..ad84764 100644 --- a/WebKit/chromium/src/WebRuntimeFeatures.cpp +++ b/WebKit/chromium/src/WebRuntimeFeatures.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "WebRuntimeFeatures.h" +#include "Database.h" #include "RuntimeEnabledFeatures.h" #include "WebMediaPlayerClientImpl.h" #include "WebSocket.h" @@ -42,14 +43,14 @@ namespace WebKit { void WebRuntimeFeatures::enableDatabase(bool enable) { #if ENABLE(DATABASE) - RuntimeEnabledFeatures::setDatabaseEnabled(enable); + Database::setIsAvailable(enable); #endif } bool WebRuntimeFeatures::isDatabaseEnabled() { #if ENABLE(DATABASE) - return RuntimeEnabledFeatures::databaseEnabled(); + return Database::isAvailable(); #else return false; #endif @@ -122,14 +123,14 @@ bool WebRuntimeFeatures::isSocketsEnabled() void WebRuntimeFeatures::enableNotifications(bool enable) { #if ENABLE(NOTIFICATIONS) - RuntimeEnabledFeatures::setNotificationsEnabled(enable); + RuntimeEnabledFeatures::setWebkitNotificationsEnabled(enable); #endif } bool WebRuntimeFeatures::isNotificationsEnabled() { #if ENABLE(NOTIFICATIONS) - return RuntimeEnabledFeatures::notificationsEnabled(); + return RuntimeEnabledFeatures::webkitNotificationsEnabled(); #else return false; #endif @@ -151,4 +152,36 @@ bool WebRuntimeFeatures::isApplicationCacheEnabled() #endif } +void WebRuntimeFeatures::enableGeolocation(bool enable) +{ +#if ENABLE(GEOLOCATION) + RuntimeEnabledFeatures::setGeolocationEnabled(enable); +#endif +} + +bool WebRuntimeFeatures::isGeolocationEnabled() +{ +#if ENABLE(GEOLOCATION) + return RuntimeEnabledFeatures::geolocationEnabled(); +#else + return false; +#endif +} + +void WebRuntimeFeatures::enableIndexedDatabase(bool enable) +{ +#if ENABLE(INDEXED_DATABASE) + RuntimeEnabledFeatures::setIndexedDBEnabled(enable); +#endif +} + +bool WebRuntimeFeatures::isIndexedDatabaseEnabled() +{ +#if ENABLE(INDEXED_DATABASE) + return RuntimeEnabledFeatures::indexedDBEnabled(); +#else + return false; +#endif +} + } // namespace WebKit diff --git a/WebKit/chromium/src/WebSecurityOrigin.cpp b/WebKit/chromium/src/WebSecurityOrigin.cpp index 3cf7364..81546da 100644 --- a/WebKit/chromium/src/WebSecurityOrigin.cpp +++ b/WebKit/chromium/src/WebSecurityOrigin.cpp @@ -42,6 +42,16 @@ namespace WebKit { class WebSecurityOriginPrivate : public SecurityOrigin { }; +WebSecurityOrigin* WebSecurityOrigin::createFromDatabaseIdentifier(const WebString& databaseIdentifier) +{ + return new WebSecurityOrigin(SecurityOrigin::createFromDatabaseIdentifier(databaseIdentifier)); +} + +WebSecurityOrigin WebSecurityOrigin::createFromString(const WebString& origin) +{ + return WebSecurityOrigin(SecurityOrigin::createFromString(origin)); +} + void WebSecurityOrigin::reset() { assign(0); diff --git a/WebKit/chromium/src/WebSecurityPolicy.cpp b/WebKit/chromium/src/WebSecurityPolicy.cpp index c66c805..48b445c 100644 --- a/WebKit/chromium/src/WebSecurityPolicy.cpp +++ b/WebKit/chromium/src/WebSecurityPolicy.cpp @@ -66,4 +66,9 @@ void WebSecurityPolicy::resetOriginAccessWhiteLists() SecurityOrigin::resetOriginAccessWhiteLists(); } +bool WebSecurityPolicy::shouldHideReferrer(const WebURL& url, const WebString& referrer) +{ + return SecurityOrigin::shouldHideReferrer(url, referrer); +} + } // namespace WebKit diff --git a/WebKit/chromium/src/WebSettingsImpl.cpp b/WebKit/chromium/src/WebSettingsImpl.cpp index e019653..5cfbd4f 100644 --- a/WebKit/chromium/src/WebSettingsImpl.cpp +++ b/WebKit/chromium/src/WebSettingsImpl.cpp @@ -130,6 +130,11 @@ void WebSettingsImpl::setLoadsImagesAutomatically(bool loadsImagesAutomatically) m_settings->setLoadsImagesAutomatically(loadsImagesAutomatically); } +void WebSettingsImpl::setImagesEnabled(bool enabled) +{ + m_settings->setImagesEnabled(enabled); +} + void WebSettingsImpl::setPluginsEnabled(bool enabled) { m_settings->setPluginsEnabled(enabled); diff --git a/WebKit/chromium/src/WebSettingsImpl.h b/WebKit/chromium/src/WebSettingsImpl.h index 9c0f9f4..3b69fe6 100644 --- a/WebKit/chromium/src/WebSettingsImpl.h +++ b/WebKit/chromium/src/WebSettingsImpl.h @@ -60,6 +60,7 @@ public: virtual void setWebSecurityEnabled(bool); virtual void setJavaScriptCanOpenWindowsAutomatically(bool); virtual void setLoadsImagesAutomatically(bool); + virtual void setImagesEnabled(bool); virtual void setPluginsEnabled(bool); virtual void setDOMPasteAllowed(bool); virtual void setDeveloperExtrasEnabled(bool); diff --git a/WebKit/chromium/src/WebStorageAreaImpl.cpp b/WebKit/chromium/src/WebStorageAreaImpl.cpp index f24bee3..9a7fd5c 100644 --- a/WebKit/chromium/src/WebStorageAreaImpl.cpp +++ b/WebKit/chromium/src/WebStorageAreaImpl.cpp @@ -66,30 +66,30 @@ WebString WebStorageAreaImpl::getItem(const WebString& key) return m_storageArea->getItem(key); } -void WebStorageAreaImpl::setItem(const WebString& key, const WebString& value, const WebURL& url, bool& quotaException) +void WebStorageAreaImpl::setItem(const WebString& key, const WebString& value, const WebURL& url, Result& result, WebString& oldValue) { int exceptionCode = 0; ScopedStorageEventURL scope(url); - m_storageArea->setItem(key, value, exceptionCode, 0); + oldValue = m_storageArea->setItem(key, value, exceptionCode, 0); if (exceptionCode) { ASSERT(exceptionCode == WebCore::QUOTA_EXCEEDED_ERR); - quotaException = true; + result = ResultBlockedByQuota; } else - quotaException = false; + result = ResultOK; } -void WebStorageAreaImpl::removeItem(const WebString& key, const WebURL& url) +void WebStorageAreaImpl::removeItem(const WebString& key, const WebURL& url, WebString& oldValue) { ScopedStorageEventURL scope(url); - m_storageArea->removeItem(key, 0); + oldValue = m_storageArea->removeItem(key, 0); } -void WebStorageAreaImpl::clear(const WebURL& url) +void WebStorageAreaImpl::clear(const WebURL& url, bool& somethingCleared) { ScopedStorageEventURL scope(url); - m_storageArea->clear(0); + somethingCleared = m_storageArea->clear(0); } } // namespace WebKit diff --git a/WebKit/chromium/src/WebStorageAreaImpl.h b/WebKit/chromium/src/WebStorageAreaImpl.h index e1f74e0..e9a11c2 100644 --- a/WebKit/chromium/src/WebStorageAreaImpl.h +++ b/WebKit/chromium/src/WebStorageAreaImpl.h @@ -45,9 +45,9 @@ public: virtual unsigned length(); virtual WebString key(unsigned index); virtual WebString getItem(const WebString& key); - virtual void setItem(const WebString& key, const WebString& value, const WebURL& url, bool& quotaException); - virtual void removeItem(const WebString& key, const WebURL& url); - virtual void clear(const WebURL& url); + virtual void setItem(const WebString& key, const WebString& value, const WebURL& url, Result& result, WebString& oldValue); + virtual void removeItem(const WebString& key, const WebURL& url, WebString& oldValue); + virtual void clear(const WebURL& url, bool& somethingCleared); // For storage events in single-process mode and test shell. static const WebURL* currentStorageEventURL() { return storageEventURL; } diff --git a/WebKit/chromium/src/WebString.cpp b/WebKit/chromium/src/WebString.cpp index f45e05f..36d5f86 100644 --- a/WebKit/chromium/src/WebString.cpp +++ b/WebKit/chromium/src/WebString.cpp @@ -86,6 +86,11 @@ WebString WebString::fromUTF8(const char* data) return WebCore::String::fromUTF8(data); } +bool WebString::equals(const WebString& s) const +{ + return equal(m_private, s.m_private); +} + WebString::WebString(const WebCore::String& s) : m_private(static_cast<WebStringPrivate*>(s.impl())) { diff --git a/WebKit/chromium/src/WebURLResponse.cpp b/WebKit/chromium/src/WebURLResponse.cpp index 49f07f9..95e0be2 100644 --- a/WebKit/chromium/src/WebURLResponse.cpp +++ b/WebKit/chromium/src/WebURLResponse.cpp @@ -255,6 +255,16 @@ const ResourceResponse& WebURLResponse::toResourceResponse() const return *m_private->m_resourceResponse; } +bool WebURLResponse::wasFetchedViaSPDY() const +{ + return m_private->m_resourceResponse->wasFetchedViaSPDY(); +} + +void WebURLResponse::setWasFetchedViaSPDY(bool value) +{ + m_private->m_resourceResponse->setWasFetchedViaSPDY(value); +} + void WebURLResponse::assign(WebURLResponsePrivate* p) { // Subclasses may call this directly so a self-assignment check is needed diff --git a/WebKit/chromium/src/WebViewImpl.cpp b/WebKit/chromium/src/WebViewImpl.cpp index 357cdca..ce03523 100644 --- a/WebKit/chromium/src/WebViewImpl.cpp +++ b/WebKit/chromium/src/WebViewImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 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 @@ -31,8 +31,10 @@ #include "config.h" #include "WebViewImpl.h" +#include "AutoFillPopupMenuClient.h" #include "AutocompletePopupMenuClient.h" #include "AXObjectCache.h" +#include "Chrome.h" #include "ContextMenu.h" #include "ContextMenuController.h" #include "ContextMenuItem.h" @@ -65,6 +67,7 @@ #include "NodeRenderStyle.h" #include "Page.h" #include "PageGroup.h" +#include "PageGroupLoadDeferrer.h" #include "Pasteboard.h" #include "PlatformContextSkia.h" #include "PlatformKeyboardEvent.h" @@ -96,11 +99,11 @@ #include "WebVector.h" #include "WebViewClient.h" -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) #include "KeyboardCodesWin.h" #include "RenderThemeChromiumWin.h" #else -#if PLATFORM(LINUX) +#if OS(LINUX) #include "RenderThemeChromiumLinux.h" #endif #include "KeyboardCodesPosix.h" @@ -129,6 +132,10 @@ static const double maxTextSizeMultiplier = 3.0; // one page group. const char* pageGroupName = "default"; +// Used to defer all page activity in cases where the embedder wishes to run +// a nested event loop. +static PageGroupLoadDeferrer* pageGroupLoadDeferrer; + // Ensure that the WebDragOperation enum values stay in sync with the original // DragOperation constants. #define COMPILE_ASSERT_MATCHING_ENUM(coreName) \ @@ -142,16 +149,16 @@ 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 +// Note that focusOnShow is false so that the suggestions 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 = { +static const PopupContainerSettings suggestionsPopupSettings = { 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 + // For suggestions, 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, @@ -174,6 +181,28 @@ void WebView::resetVisitedLinkState() Page::allVisitedStateChanged(PageGroup::pageGroup(pageGroupName)); } +void WebView::willEnterModalLoop() +{ + // It is not valid to nest more than once. + ASSERT(!pageGroupLoadDeferrer); + + PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); + ASSERT(pageGroup); + ASSERT(!pageGroup->pages().isEmpty()); + + // Pick any page in the page group since we are deferring all pages. + pageGroupLoadDeferrer = new PageGroupLoadDeferrer(*pageGroup->pages().begin(), true); +} + +void WebView::didExitModalLoop() +{ + // The embedder must have called willEnterNestedEventLoop. + ASSERT(pageGroupLoadDeferrer); + + delete pageGroupLoadDeferrer; + pageGroupLoadDeferrer = 0; +} + void WebViewImpl::initializeMainFrame(WebFrameClient* frameClient) { // NOTE: The WebFrameImpl takes a reference to itself within InitMainFrame @@ -211,7 +240,9 @@ WebViewImpl::WebViewImpl(WebViewClient* client) , m_dropEffect(DropEffectDefault) , m_operationsAllowed(WebDragOperationNone) , m_dragOperation(WebDragOperationNone) - , m_autocompletePopupShowing(false) + , m_suggestionsPopupShowing(false) + , m_suggestionsPopupClient(0) + , m_suggestionsPopup(0) , m_isTransparent(false) , m_tabsToLinks(false) { @@ -229,6 +260,7 @@ WebViewImpl::WebViewImpl(WebViewClient* client) &m_editorClientImpl, &m_dragClientImpl, &m_inspectorClientImpl, + 0, 0)); m_page->backForwardList()->setClient(&m_backForwardListClientImpl); @@ -297,7 +329,7 @@ void WebViewImpl::mouseDown(const WebMouseEvent& event) m_lastMouseDownPoint = WebPoint(event.x, event.y); // If a text field that has focus is clicked again, we should display the - // autocomplete popup. + // suggestions popup. RefPtr<Node> clickedNode; if (event.button == WebMouseEvent::ButtonLeft) { RefPtr<Node> focusedNode = focusedWebCoreNode(); @@ -319,19 +351,19 @@ void WebViewImpl::mouseDown(const WebMouseEvent& event) PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); if (clickedNode.get() && clickedNode == focusedWebCoreNode()) { - // Focus has not changed, show the autocomplete popup. + // Focus has not changed, show the suggestions 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 OS(DARWIN) if (event.button == WebMouseEvent::ButtonRight || (event.button == WebMouseEvent::ButtonLeft && event.modifiers & WebMouseEvent::ControlKey)) mouseContextMenu(event); -#elif PLATFORM(LINUX) +#elif OS(LINUX) if (event.button == WebMouseEvent::ButtonRight) mouseContextMenu(event); #endif @@ -354,7 +386,7 @@ void WebViewImpl::mouseContextMenu(const WebMouseEvent& event) else targetFrame = m_page->focusController()->focusedOrMainFrame(); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) targetFrame->view()->setCursor(pointerCursor()); #endif @@ -370,7 +402,7 @@ void WebViewImpl::mouseUp(const WebMouseEvent& event) if (!mainFrameImpl() || !mainFrameImpl()->frameView()) return; -#if PLATFORM(LINUX) +#if OS(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. @@ -389,14 +421,14 @@ void WebViewImpl::mouseUp(const WebMouseEvent& event) // handleMouseReleaseEvent() earlier in this function if (event.button == WebMouseEvent::ButtonMiddle) { Frame* focused = focusedWebCoreFrame(); + FrameView* view = m_page->mainFrame()->view(); IntPoint clickPoint(m_lastMouseDownPoint.x, m_lastMouseDownPoint.y); - clickPoint = m_page->mainFrame()->view()->windowToContents(clickPoint); - HitTestResult hitTestResult = - focused->eventHandler()->hitTestResultAtPoint(clickPoint, false, false, - ShouldHitTestScrollbars); + IntPoint contentPoint = view->windowToContents(clickPoint); + HitTestResult hitTestResult = focused->eventHandler()->hitTestResultAtPoint(contentPoint, 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) { + // link (which will navigate later in the code). The main scrollbars + // have to be handled separately. + if (!hitTestResult.scrollbar() && !hitTestResult.isLiveLink() && focused && !view->scrollbarAtPoint(clickPoint)) { Editor* editor = focused->editor(); Pasteboard* pasteboard = Pasteboard::generalPasteboard(); bool oldSelectionMode = pasteboard->isSelectionMode(); @@ -411,7 +443,7 @@ void WebViewImpl::mouseUp(const WebMouseEvent& event) mainFrameImpl()->frame()->eventHandler()->handleMouseReleaseEvent( PlatformMouseEventBuilder(mainFrameImpl()->frameView(), event)); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) // 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) @@ -439,7 +471,7 @@ bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) // event. m_suppressNextKeypressEvent = false; - // Give autocomplete a chance to consume the key events it is interested in. + // Give Autocomplete a chance to consume the key events it is interested in. if (autocompleteHandleKeyEvent(event)) return true; @@ -451,9 +483,17 @@ bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) if (!handler) return keyEventDefault(event); -#if PLATFORM(WIN_OS) || PLATFORM(LINUX) - if ((!event.modifiers && (event.windowsKeyCode == VKEY_APPS)) - || ((event.modifiers == WebInputEvent::ShiftKey) && (event.windowsKeyCode == VKEY_F10))) { +#if OS(WINDOWS) || OS(LINUX) + const WebInputEvent::Type contextMenuTriggeringEventType = +#if OS(WINDOWS) + WebInputEvent::KeyUp; +#elif OS(LINUX) + WebInputEvent::RawKeyDown; +#endif + + if (((!event.modifiers && (event.windowsKeyCode == VKEY_APPS)) + || ((event.modifiers == WebInputEvent::ShiftKey) && (event.windowsKeyCode == VKEY_F10))) + && event.type == contextMenuTriggeringEventType) { sendContextMenuEvent(event); return true; } @@ -477,7 +517,7 @@ bool WebViewImpl::keyEvent(const WebKeyboardEvent& event) bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) { - if (!m_autocompletePopupShowing + if (!m_suggestionsPopupShowing // Home and End should be left to the text field to process. || event.windowsKeyCode == VKEY_HOME || event.windowsKeyCode == VKEY_END) @@ -485,7 +525,7 @@ bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) // Pressing delete triggers the removal of the selected suggestion from the DB. if (event.windowsKeyCode == VKEY_DELETE - && m_autocompletePopup->selectedIndex() != -1) { + && m_suggestionsPopup->selectedIndex() != -1) { Node* node = focusedWebCoreNode(); if (!node || (node->nodeType() != Node::ELEMENT_NODE)) { ASSERT_NOT_REACHED(); @@ -497,22 +537,22 @@ bool WebViewImpl::autocompleteHandleKeyEvent(const WebKeyboardEvent& event) return false; } - int selectedIndex = m_autocompletePopup->selectedIndex(); + int selectedIndex = m_suggestionsPopup->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(); + m_autocompletePopupClient->removeSuggestionAtIndex(selectedIndex); + refreshSuggestionsPopup(); return false; } - if (!m_autocompletePopup->isInterestedInEventForKey(event.windowsKeyCode)) + if (!m_suggestionsPopup->isInterestedInEventForKey(event.windowsKeyCode)) return false; - if (m_autocompletePopup->handleKeyEvent(PlatformKeyboardEventBuilder(event))) { + if (m_suggestionsPopup->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) @@ -573,7 +613,7 @@ bool WebViewImpl::charEvent(const WebKeyboardEvent& 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) +#if OS(WINDOWS) || OS(LINUX) // FIXME: implement on Mac bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event) { @@ -584,22 +624,19 @@ bool WebViewImpl::sendContextMenuEvent(const WebKeyboardEvent& event) return false; IntPoint coords(-1, -1); -#if PLATFORM(WIN_OS) +#if OS(WINDOWS) 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(); Frame* focusedFrame = page()->focusController()->focusedOrMainFrame(); Node* focusedNode = focusedFrame->document()->focusedNode(); + Position start = mainFrameImpl->selection()->selection().start(); - if (start.node() && end.node()) { + if (focusedFrame->editor() && focusedFrame->editor()->canEdit() && start.node()) { RenderObject* renderer = start.node()->renderer(); if (!renderer) return false; @@ -663,6 +700,7 @@ bool WebViewImpl::keyEventDefault(const WebKeyboardEvent& event) case WebInputEvent::RawKeyDown: if (event.modifiers == WebInputEvent::ControlKey) { switch (event.windowsKeyCode) { +#if !OS(DARWIN) case 'A': focusedFrame()->executeCommand(WebString::fromUTF8("SelectAll")); return true; @@ -670,6 +708,7 @@ bool WebViewImpl::keyEventDefault(const WebKeyboardEvent& event) case 'C': focusedFrame()->executeCommand(WebString::fromUTF8("Copy")); return true; +#endif // 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 @@ -942,7 +981,7 @@ void WebViewImpl::setFocus(bool enable) } m_imeAcceptEvents = true; } else { - hideAutoCompletePopup(); + hideSuggestionsPopup(); // Clear focus on the currently focused frame if any. if (!m_page.get()) @@ -1148,7 +1187,7 @@ 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(); + Frame* frame = m_page->mainFrame(); if (!frame) return true; @@ -1282,9 +1321,9 @@ void WebViewImpl::performMediaPlayerAction(const WebMediaPlayerAction& action, switch (action.type) { case WebMediaPlayerAction::Play: if (action.enable) - mediaElement->play(); + mediaElement->play(mediaElement->processingUserGesture()); else - mediaElement->pause(); + mediaElement->pause(mediaElement->processingUserGesture()); break; case WebMediaPlayerAction::Mute: mediaElement->setMuted(action.enable); @@ -1524,64 +1563,132 @@ void WebViewImpl::applyAutofillSuggestions( const WebVector<WebString>& suggestions, int defaultSuggestionIndex) { - if (!m_page.get() || suggestions.isEmpty()) { - hideAutoCompletePopup(); + applyAutocompleteSuggestions(node, suggestions, defaultSuggestionIndex); +} + +void WebViewImpl::applyAutoFillSuggestions( + const WebNode& node, + const WebVector<WebString>& names, + const WebVector<WebString>& labels, + int defaultSuggestionIndex) +{ + ASSERT(names.size() == labels.size()); + ASSERT(defaultSuggestionIndex < static_cast<int>(names.size())); + + if (names.isEmpty()) { + hideSuggestionsPopup(); + return; + } + + RefPtr<Node> focusedNode = focusedWebCoreNode(); + // 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 + // caret is at the end and that the text has not changed. + if (!focusedNode || focusedNode != PassRefPtr<Node>(node)) { + hideSuggestionsPopup(); return; } + HTMLInputElement* inputElem = + static_cast<HTMLInputElement*>(focusedNode.get()); + + // The first time the AutoFill popup is shown we'll create the client and + // the popup. + if (!m_autoFillPopupClient.get()) + m_autoFillPopupClient.set(new AutoFillPopupMenuClient); + + m_autoFillPopupClient->initialize(inputElem, names, labels, + defaultSuggestionIndex); + + if (m_suggestionsPopupClient != m_autoFillPopupClient.get()) { + hideSuggestionsPopup(); + m_suggestionsPopupClient = m_autoFillPopupClient.get(); + } + + if (!m_autoFillPopup.get()) { + m_autoFillPopup = PopupContainer::create(m_suggestionsPopupClient, + suggestionsPopupSettings); + } + + if (m_suggestionsPopup != m_autoFillPopup.get()) + m_suggestionsPopup = m_autoFillPopup.get(); + + if (m_suggestionsPopupShowing) { + m_autoFillPopupClient->setSuggestions(names, labels); + refreshSuggestionsPopup(); + } else { + m_suggestionsPopup->show(focusedNode->getRect(), + focusedNode->ownerDocument()->view(), 0); + m_suggestionsPopupShowing = true; + } +} + +void WebViewImpl::applyAutocompleteSuggestions( + const WebNode& node, + const WebVector<WebString>& suggestions, + int defaultSuggestionIndex) +{ 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; - } + if (!m_page.get() || suggestions.isEmpty()) { + hideSuggestionsPopup(); + 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; - } + RefPtr<Node> focusedNode = focusedWebCoreNode(); + // If the node for which we queried the Autocomplete suggestions is not the + // focused node, then we have nothing to do. FIXME: also check the + // caret is at the end and that the text has not changed. + if (!focusedNode || focusedNode != PassRefPtr<Node>(node)) { + hideSuggestionsPopup(); + return; + } - if (!focusedNode->hasTagName(HTMLNames::inputTag)) { - ASSERT_NOT_REACHED(); - return; - } + HTMLInputElement* inputElem = + static_cast<HTMLInputElement*>(focusedNode.get()); - 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); - } + // The first time the Autocomplete is shown we'll create the client and the + // popup. + if (!m_autocompletePopupClient.get()) + m_autocompletePopupClient.set(new AutocompletePopupMenuClient); - if (m_autocompletePopupShowing) { - m_autocompletePopupClient->setSuggestions(suggestions); - refreshAutofillPopup(); - } else { - m_autocompletePopup->show(focusedNode->getRect(), - focusedNode->ownerDocument()->view(), 0); - m_autocompletePopupShowing = true; - } + m_autocompletePopupClient->initialize(inputElem, suggestions, + defaultSuggestionIndex); + + if (m_suggestionsPopupClient != m_autocompletePopupClient.get()) { + hideSuggestionsPopup(); + m_suggestionsPopupClient = m_autocompletePopupClient.get(); + } + + if (!m_autocompletePopup.get()) { + m_autocompletePopup = PopupContainer::create(m_suggestionsPopupClient, + suggestionsPopupSettings); + } + + if (m_suggestionsPopup != m_autocompletePopup.get()) + m_suggestionsPopup = m_autocompletePopup.get(); + + if (m_suggestionsPopupShowing) { + m_autocompletePopupClient->setSuggestions(suggestions); + refreshSuggestionsPopup(); + } else { + m_suggestionsPopup->show(focusedNode->getRect(), + focusedNode->ownerDocument()->view(), 0); + m_suggestionsPopupShowing = true; } } void WebViewImpl::hideAutofillPopup() { - hideAutoCompletePopup(); + hideSuggestionsPopup(); +} + +void WebViewImpl::hideSuggestionsPopup() +{ + if (m_suggestionsPopupShowing) { + m_suggestionsPopup->hidePopup(); + m_suggestionsPopupShowing = false; + } } void WebViewImpl::performCustomContextMenuAction(unsigned action) @@ -1640,13 +1747,40 @@ bool WebViewImpl::isActive() const void WebViewImpl::setScrollbarColors(unsigned inactiveColor, unsigned activeColor, unsigned trackColor) { -#if PLATFORM(LINUX) +#if OS(LINUX) RenderThemeChromiumLinux::setScrollbarColors(inactiveColor, activeColor, trackColor); #endif } +void WebViewImpl::setSelectionColors(unsigned activeBackgroundColor, + unsigned activeForegroundColor, + unsigned inactiveBackgroundColor, + unsigned inactiveForegroundColor) { +#if OS(LINUX) + RenderThemeChromiumLinux::setSelectionColors(activeBackgroundColor, + activeForegroundColor, + inactiveBackgroundColor, + inactiveForegroundColor); + theme()->platformColorsDidChange(); +#endif +} + +void WebViewImpl::addUserScript(const WebString& sourceCode, bool runAtStart) +{ + PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); + RefPtr<DOMWrapperWorld> world(DOMWrapperWorld::create()); + pageGroup->addUserScriptToWorld(world.get(), sourceCode, WebURL(), 0, 0, + runAtStart ? InjectAtDocumentStart : InjectAtDocumentEnd); +} + +void WebViewImpl::removeAllUserContent() +{ + PageGroup* pageGroup = PageGroup::pageGroup(pageGroupName); + pageGroup->removeAllUserContent(); +} + void WebViewImpl::didCommitLoad(bool* isNewNavigation) { if (isNewNavigation) @@ -1665,9 +1799,9 @@ bool WebViewImpl::navigationPolicyFromMouseEvent(unsigned short button, bool alt, bool meta, WebNavigationPolicy* policy) { -#if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(FREEBSD) +#if OS(WINDOWS) || OS(LINUX) || OS(FREEBSD) const bool newTabModifier = (button == 1) || ctrl; -#elif PLATFORM(DARWIN) +#elif OS(DARWIN) const bool newTabModifier = (button == 1) || meta; #endif if (!newTabModifier && !shift && !alt) @@ -1717,19 +1851,6 @@ void WebViewImpl::observeNewNavigation() #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); @@ -1745,23 +1866,23 @@ NotificationPresenterImpl* WebViewImpl::notificationPresenterImpl() } #endif -void WebViewImpl::refreshAutofillPopup() +void WebViewImpl::refreshSuggestionsPopup() { - ASSERT(m_autocompletePopupShowing); + ASSERT(m_suggestionsPopupShowing); // Hide the popup if it has become empty. if (!m_autocompletePopupClient->listSize()) { - hideAutoCompletePopup(); + hideSuggestionsPopup(); return; } - IntRect oldBounds = m_autocompletePopup->boundsRect(); - m_autocompletePopup->refresh(); - IntRect newBounds = m_autocompletePopup->boundsRect(); + IntRect oldBounds = m_suggestionsPopup->boundsRect(); + m_suggestionsPopup->refresh(); + IntRect newBounds = m_suggestionsPopup->boundsRect(); // Let's resize the backing window if necessary. if (oldBounds != newBounds) { WebPopupMenuImpl* popupMenu = - static_cast<WebPopupMenuImpl*>(m_autocompletePopup->client()); + static_cast<WebPopupMenuImpl*>(m_suggestionsPopup->client()); popupMenu->client()->setWindowRect(newBounds); } } diff --git a/WebKit/chromium/src/WebViewImpl.h b/WebKit/chromium/src/WebViewImpl.h index dd5191e..286ac43 100644 --- a/WebKit/chromium/src/WebViewImpl.h +++ b/WebKit/chromium/src/WebViewImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 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 @@ -58,6 +58,7 @@ class KeyboardEvent; class Page; class PlatformKeyboardEvent; class PopupContainer; +class PopupMenuClient; class Range; class RenderTheme; class Widget; @@ -65,7 +66,9 @@ class Widget; namespace WebKit { class AutocompletePopupMenuClient; +class AutoFillPopupMenuClient; class ContextMenuClientImpl; +class SuggestionsPopupMenuClient; class WebAccessibilityObject; class WebDevToolsAgentPrivate; class WebFrameImpl; @@ -153,11 +156,28 @@ public: const WebNode&, const WebVector<WebString>& suggestions, int defaultSuggestionIndex); + virtual void applyAutoFillSuggestions( + const WebNode&, + const WebVector<WebString>& names, + const WebVector<WebString>& labels, + int defaultSuggestionIndex); + virtual void applyAutocompleteSuggestions( + const WebNode&, + const WebVector<WebString>& suggestions, + int defaultSuggestionIndex); virtual void hideAutofillPopup(); + virtual void hideSuggestionsPopup(); virtual void setScrollbarColors(unsigned inactiveColor, unsigned activeColor, unsigned trackColor); + virtual void setSelectionColors(unsigned activeBackgroundColor, + unsigned activeForegroundColor, + unsigned inactiveBackgroundColor, + unsigned inactiveForegroundColor); virtual void performCustomContextMenuAction(unsigned action); + virtual void addUserScript(const WebString& sourceCode, + bool runAtStart); + virtual void removeAllUserContent(); // WebViewImpl @@ -254,9 +274,10 @@ public: const WebDragData& dragData, WebDragOperationsMask dragSourceOperationMask); - // Hides the autocomplete popup if it is showing. - void hideAutoCompletePopup(); - void autoCompletePopupDidHide(); + void suggestionsPopupDidHide() + { + m_suggestionsPopupShowing = false; + } #if ENABLE(NOTIFICATIONS) // Returns the provider of desktop notifications. @@ -288,10 +309,10 @@ private: // 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(); + // Repaints the suggestions popup. Should be called when the suggestions + // have changed. Note that this should only be called when the suggestions + // popup is showing. + void refreshSuggestionsPopup(); // Returns true if the view was scrolled. bool scrollViewWithKeyboard(int keyCode, int modifiers); @@ -386,16 +407,29 @@ private: // 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 a suggestions popup is currently showing. + bool m_suggestionsPopupShowing; + + // A pointer to the current suggestions popup menu client. This can be + // either an AutoFillPopupMenuClient or an AutocompletePopupMenuClient. We + // do not own this pointer. + SuggestionsPopupMenuClient* m_suggestionsPopupClient; - // Whether the autocomplete popup is currently showing. - bool m_autocompletePopupShowing; + // The AutoFill popup client. + OwnPtr<AutoFillPopupMenuClient> m_autoFillPopupClient; - // The autocomplete client. + // The Autocomplete popup client. OwnPtr<AutocompletePopupMenuClient> m_autocompletePopupClient; + // A pointer to the current suggestions popup. We do not own this pointer. + WebCore::PopupContainer* m_suggestionsPopup; + + // The AutoFill suggestions popup. + RefPtr<WebCore::PopupContainer> m_autoFillPopup; + + // The AutoComplete suggestions popup. + RefPtr<WebCore::PopupContainer> m_autocompletePopup; + OwnPtr<WebDevToolsAgentPrivate> m_devToolsAgent; // Whether the webview is rendering transparently. diff --git a/WebKit/chromium/src/WebWorkerBase.cpp b/WebKit/chromium/src/WebWorkerBase.cpp index 85a263b..40019e8 100644 --- a/WebKit/chromium/src/WebWorkerBase.cpp +++ b/WebKit/chromium/src/WebWorkerBase.cpp @@ -132,8 +132,7 @@ void WebWorkerBase::initializeLoader(const WebURL& url) int len = static_cast<int>(content.length()); RefPtr<SharedBuffer> buf(SharedBuffer::create(content.data(), len)); SubstituteData substData(buf, String("text/html"), String("UTF-8"), KURL()); - ResourceRequest request(url, CString()); - webFrame->frame()->loader()->load(request, substData, false); + webFrame->frame()->loader()->load(ResourceRequest(url), substData, false); // This document will be used as 'loading context' for the worker. m_loadingDocument = webFrame->frame()->document(); @@ -158,7 +157,7 @@ void WebWorkerBase::postMessageToWorkerObject(PassRefPtr<SerializedScriptValue> PassOwnPtr<MessagePortChannelArray> channels) { dispatchTaskToMainThread(createCallbackTask(&postMessageTask, this, - message->toString(), channels)); + message->toWireString(), channels)); } void WebWorkerBase::postMessageTask(ScriptExecutionContext* context, @@ -272,6 +271,8 @@ void WebWorkerBase::workerContextClosedTask(ScriptExecutionContext* context, { if (thisPtr->commonClient()) thisPtr->commonClient()->workerContextClosed(); + + thisPtr->stopWorkerThread(); } void WebWorkerBase::workerContextDestroyed() diff --git a/WebKit/chromium/src/WebWorkerClientImpl.cpp b/WebKit/chromium/src/WebWorkerClientImpl.cpp index 6be03a7..598a078 100644 --- a/WebKit/chromium/src/WebWorkerClientImpl.cpp +++ b/WebKit/chromium/src/WebWorkerClientImpl.cpp @@ -173,7 +173,7 @@ void WebWorkerClientImpl::postMessageToWorkerContext( if (!isMainThread()) { WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&postMessageToWorkerContextTask, this, - message->toString(), + message->toWireString(), channels)); return; } @@ -184,7 +184,7 @@ void WebWorkerClientImpl::postMessageToWorkerContext( webchannel->setClient(0); webChannels[i] = webchannel; } - m_webWorker->postMessageToWorkerContext(message->toString(), webChannels); + m_webWorker->postMessageToWorkerContext(message->toWireString(), webChannels); } bool WebWorkerClientImpl::hasPendingActivity() const @@ -356,7 +356,7 @@ void WebWorkerClientImpl::postMessageToWorkerObjectTask( OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*context, channels.release()); RefPtr<SerializedScriptValue> serializedMessage = - SerializedScriptValue::create(message); + SerializedScriptValue::createFromWire(message); thisPtr->m_worker->dispatchEvent(MessageEvent::create(ports.release(), serializedMessage.release())); } diff --git a/WebKit/chromium/src/WebWorkerImpl.cpp b/WebKit/chromium/src/WebWorkerImpl.cpp index 744be30..5b5e053 100644 --- a/WebKit/chromium/src/WebWorkerImpl.cpp +++ b/WebKit/chromium/src/WebWorkerImpl.cpp @@ -88,7 +88,7 @@ void WebWorkerImpl::postMessageToWorkerContextTask(WebCore::ScriptExecutionConte OwnPtr<MessagePortArray> ports = MessagePort::entanglePorts(*context, channels.release()); RefPtr<SerializedScriptValue> serializedMessage = - SerializedScriptValue::create(message); + SerializedScriptValue::createFromWire(message); workerContext->dispatchEvent(MessageEvent::create( ports.release(), serializedMessage.release())); thisPtr->confirmMessageFromWorkerObject(workerContext->hasPendingActivity()); diff --git a/WebKit/chromium/src/js/DebuggerAgent.js b/WebKit/chromium/src/js/DebuggerAgent.js new file mode 100644 index 0000000..301620a --- /dev/null +++ b/WebKit/chromium/src/js/DebuggerAgent.js @@ -0,0 +1,1528 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * @fileoverview Provides communication interface to remote v8 debugger. See + * protocol decription at http://code.google.com/p/v8/wiki/DebuggerProtocol + */ + +/** + * FIXME: change field naming style to use trailing underscore. + * @constructor + */ +devtools.DebuggerAgent = function() +{ + RemoteDebuggerAgent.debuggerOutput = this.handleDebuggerOutput_.bind(this); + RemoteDebuggerAgent.setContextId = this.setContextId_.bind(this); + + /** + * Id of the inspected page global context. It is used for filtering scripts. + * @type {number} + */ + this.contextId_ = null; + + /** + * Mapping from script id to script info. + * @type {Object} + */ + this.parsedScripts_ = null; + + /** + * Mapping from the request id to the devtools.BreakpointInfo for the + * breakpoints whose v8 ids are not set yet. These breakpoints are waiting for + * "setbreakpoint" responses to learn their ids in the v8 debugger. + * @see #handleSetBreakpointResponse_ + * @type {Object} + */ + this.requestNumberToBreakpointInfo_ = null; + + /** + * Information on current stack frames. + * @type {Array.<devtools.CallFrame>} + */ + this.callFrames_ = []; + + /** + * Whether to stop in the debugger on the exceptions. + * @type {boolean} + */ + this.pauseOnExceptions_ = false; + + /** + * Mapping: request sequence number->callback. + * @type {Object} + */ + this.requestSeqToCallback_ = null; + + /** + * Whether the scripts panel has been shown and initialilzed. + * @type {boolean} + */ + this.scriptsPanelInitialized_ = false; + + /** + * Whether the scripts list should be requested next time when context id is + * set. + * @type {boolean} + */ + this.requestScriptsWhenContextIdSet_ = false; + + /** + * Whether the agent is waiting for initial scripts response. + * @type {boolean} + */ + this.waitingForInitialScriptsResponse_ = false; + + /** + * If backtrace response is received when initial scripts response + * is not yet processed the backtrace handling will be postponed until + * after the scripts response processing. The handler bound to its arguments + * and this agent will be stored in this field then. + * @type {?function()} + */ + this.pendingBacktraceResponseHandler_ = null; + + /** + * Container of all breakpoints set using resource URL. These breakpoints + * survive page reload. Breakpoints set by script id(for scripts that don't + * have URLs) are stored in ScriptInfo objects. + * @type {Object} + */ + this.urlToBreakpoints_ = {}; + + + /** + * Exception message that is shown to user while on exception break. + * @type {WebInspector.ConsoleMessage} + */ + this.currentExceptionMessage_ = null; +}; + + +/** + * A copy of the scope types from v8/src/mirror-delay.js + * @enum {number} + */ +devtools.DebuggerAgent.ScopeType = { + Global: 0, + Local: 1, + With: 2, + Closure: 3, + Catch: 4 +}; + + +/** + * Resets debugger agent to its initial state. + */ +devtools.DebuggerAgent.prototype.reset = function() +{ + this.contextId_ = null; + // No need to request scripts since they all will be pushed in AfterCompile + // events. + this.requestScriptsWhenContextIdSet_ = false; + this.waitingForInitialScriptsResponse_ = false; + + this.parsedScripts_ = {}; + this.requestNumberToBreakpointInfo_ = {}; + this.callFrames_ = []; + this.requestSeqToCallback_ = {}; +}; + + +/** + * Initializes scripts UI. This method is called every time Scripts panel + * is shown. It will send request for context id if it's not set yet. + */ +devtools.DebuggerAgent.prototype.initUI = function() +{ + // Initialize scripts cache when Scripts panel is shown first time. + if (this.scriptsPanelInitialized_) + return; + this.scriptsPanelInitialized_ = true; + if (this.contextId_) { + // We already have context id. This means that we are here from the + // very beginning of the page load cycle and hence will get all scripts + // via after-compile events. No need to request scripts for this session. + // + // There can be a number of scripts from after-compile events that are + // pending addition into the UI. + for (var scriptId in this.parsedScripts_) { + var script = this.parsedScripts_[scriptId]; + WebInspector.parsedScriptSource(scriptId, script.getUrl(), undefined /* script source */, script.getLineOffset()); + } + return; + } + this.waitingForInitialScriptsResponse_ = true; + // Script list should be requested only when current context id is known. + RemoteDebuggerAgent.getContextId(); + this.requestScriptsWhenContextIdSet_ = true; +}; + + +/** + * Asynchronously requests the debugger for the script source. + * @param {number} scriptId Id of the script whose source should be resolved. + * @param {function(source:?string):void} callback Function that will be called + * when the source resolution is completed. "source" parameter will be null + * if the resolution fails. + */ +devtools.DebuggerAgent.prototype.resolveScriptSource = function(scriptId, callback) +{ + var script = this.parsedScripts_[scriptId]; + if (!script || script.isUnresolved()) { + callback(null); + return; + } + + var cmd = new devtools.DebugCommand("scripts", { + "ids": [scriptId], + "includeSource": true + }); + devtools.DebuggerAgent.sendCommand_(cmd); + // Force v8 execution so that it gets to processing the requested command. + RemoteDebuggerAgent.processDebugCommands(); + + this.requestSeqToCallback_[cmd.getSequenceNumber()] = function(msg) { + if (msg.isSuccess()) { + var scriptJson = msg.getBody()[0]; + if (scriptJson) + callback(scriptJson.source); + else + callback(null); + } else + callback(null); + }; +}; + + +/** + * Tells the v8 debugger to stop on as soon as possible. + */ +devtools.DebuggerAgent.prototype.pauseExecution = function() +{ + RemoteDebuggerCommandExecutor.DebuggerPauseScript(); +}; + + +/** + * @param {number} sourceId Id of the script fot the breakpoint. + * @param {number} line Number of the line for the breakpoint. + * @param {?string} condition The breakpoint condition. + */ +devtools.DebuggerAgent.prototype.addBreakpoint = function(sourceId, line, condition) +{ + var script = this.parsedScripts_[sourceId]; + if (!script) + return; + + line = devtools.DebuggerAgent.webkitToV8LineNumber_(line); + + var commandArguments; + if (script.getUrl()) { + var breakpoints = this.urlToBreakpoints_[script.getUrl()]; + if (breakpoints && breakpoints[line]) + return; + if (!breakpoints) { + breakpoints = {}; + this.urlToBreakpoints_[script.getUrl()] = breakpoints; + } + + var breakpointInfo = new devtools.BreakpointInfo(line); + breakpoints[line] = breakpointInfo; + + commandArguments = { + "groupId": this.contextId_, + "type": "script", + "target": script.getUrl(), + "line": line, + "condition": condition + }; + } else { + var breakpointInfo = script.getBreakpointInfo(line); + if (breakpointInfo) + return; + + breakpointInfo = new devtools.BreakpointInfo(line); + script.addBreakpointInfo(breakpointInfo); + + commandArguments = { + "groupId": this.contextId_, + "type": "scriptId", + "target": sourceId, + "line": line, + "condition": condition + }; + } + + var cmd = new devtools.DebugCommand("setbreakpoint", commandArguments); + + this.requestNumberToBreakpointInfo_[cmd.getSequenceNumber()] = breakpointInfo; + + devtools.DebuggerAgent.sendCommand_(cmd); + // Force v8 execution so that it gets to processing the requested command. + // It is necessary for being able to change a breakpoint just after it + // has been created (since we need an existing breakpoint id for that). + RemoteDebuggerAgent.processDebugCommands(); +}; + + +/** + * @param {number} sourceId Id of the script for the breakpoint. + * @param {number} line Number of the line for the breakpoint. + */ +devtools.DebuggerAgent.prototype.removeBreakpoint = function(sourceId, line) +{ + var script = this.parsedScripts_[sourceId]; + if (!script) + return; + + line = devtools.DebuggerAgent.webkitToV8LineNumber_(line); + + var breakpointInfo; + if (script.getUrl()) { + var breakpoints = this.urlToBreakpoints_[script.getUrl()]; + breakpointInfo = breakpoints[line]; + delete breakpoints[line]; + } else { + breakpointInfo = script.getBreakpointInfo(line); + if (breakpointInfo) + script.removeBreakpointInfo(breakpointInfo); + } + + if (!breakpointInfo) + return; + + breakpointInfo.markAsRemoved(); + + var id = breakpointInfo.getV8Id(); + + // If we don't know id of this breakpoint in the v8 debugger we cannot send + // "clearbreakpoint" request. In that case it will be removed in + // "setbreakpoint" response handler when we learn the id. + if (id !== -1) { + this.requestClearBreakpoint_(id); + } +}; + + +/** + * @param {number} sourceId Id of the script for the breakpoint. + * @param {number} line Number of the line for the breakpoint. + * @param {?string} condition New breakpoint condition. + */ +devtools.DebuggerAgent.prototype.updateBreakpoint = function(sourceId, line, condition) +{ + var script = this.parsedScripts_[sourceId]; + if (!script) + return; + + line = devtools.DebuggerAgent.webkitToV8LineNumber_(line); + + var breakpointInfo; + if (script.getUrl()) { + var breakpoints = this.urlToBreakpoints_[script.getUrl()]; + breakpointInfo = breakpoints[line]; + } else + breakpointInfo = script.getBreakpointInfo(line); + + var id = breakpointInfo.getV8Id(); + + // If we don't know id of this breakpoint in the v8 debugger we cannot send + // the "changebreakpoint" request. + if (id !== -1) { + // TODO(apavlov): make use of the real values for "enabled" and + // "ignoreCount" when appropriate. + this.requestChangeBreakpoint_(id, true, condition, null); + } +}; + + +/** + * Tells the v8 debugger to step into the next statement. + */ +devtools.DebuggerAgent.prototype.stepIntoStatement = function() +{ + this.stepCommand_("in"); +}; + + +/** + * Tells the v8 debugger to step out of current function. + */ +devtools.DebuggerAgent.prototype.stepOutOfFunction = function() +{ + this.stepCommand_("out"); +}; + + +/** + * Tells the v8 debugger to step over the next statement. + */ +devtools.DebuggerAgent.prototype.stepOverStatement = function() +{ + this.stepCommand_("next"); +}; + + +/** + * Tells the v8 debugger to continue execution after it has been stopped on a + * breakpoint or an exception. + */ +devtools.DebuggerAgent.prototype.resumeExecution = function() +{ + this.clearExceptionMessage_(); + var cmd = new devtools.DebugCommand("continue"); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Creates exception message and schedules it for addition to the resource upon + * backtrace availability. + * @param {string} url Resource url. + * @param {number} line Resource line number. + * @param {string} message Exception text. + */ +devtools.DebuggerAgent.prototype.createExceptionMessage_ = function(url, line, message) +{ + this.currentExceptionMessage_ = new WebInspector.ConsoleMessage( + WebInspector.ConsoleMessage.MessageSource.JS, + WebInspector.ConsoleMessage.MessageType.Log, + WebInspector.ConsoleMessage.MessageLevel.Error, + line, + url, + 0 /* group level */, + 1 /* repeat count */, + "[Exception] " + message); +}; + + +/** + * Shows pending exception message that is created with createExceptionMessage_ + * earlier. + */ +devtools.DebuggerAgent.prototype.showPendingExceptionMessage_ = function() +{ + if (!this.currentExceptionMessage_) + return; + var msg = this.currentExceptionMessage_; + var resource = WebInspector.resourceURLMap[msg.url]; + if (resource) { + msg.resource = resource; + WebInspector.panels.resources.addMessageToResource(resource, msg); + } else + this.currentExceptionMessage_ = null; +}; + + +/** + * Clears exception message from the resource. + */ +devtools.DebuggerAgent.prototype.clearExceptionMessage_ = function() +{ + if (this.currentExceptionMessage_) { + var messageElement = this.currentExceptionMessage_._resourceMessageLineElement; + var bubble = messageElement.parentElement; + bubble.removeChild(messageElement); + if (!bubble.firstChild) { + // Last message in bubble removed. + bubble.parentElement.removeChild(bubble); + } + this.currentExceptionMessage_ = null; + } +}; + + +/** + * @return {boolean} True iff the debugger will pause execution on the + * exceptions. + */ +devtools.DebuggerAgent.prototype.pauseOnExceptions = function() +{ + return this.pauseOnExceptions_; +}; + + +/** + * Tells whether to pause in the debugger on the exceptions or not. + * @param {boolean} value True iff execution should be stopped in the debugger + * on the exceptions. + */ +devtools.DebuggerAgent.prototype.setPauseOnExceptions = function(value) +{ + this.pauseOnExceptions_ = value; +}; + + +/** + * Sends "evaluate" request to the debugger. + * @param {Object} arguments Request arguments map. + * @param {function(devtools.DebuggerMessage)} callback Callback to be called + * when response is received. + */ +devtools.DebuggerAgent.prototype.requestEvaluate = function(arguments, callback) +{ + var cmd = new devtools.DebugCommand("evaluate", arguments); + devtools.DebuggerAgent.sendCommand_(cmd); + this.requestSeqToCallback_[cmd.getSequenceNumber()] = callback; +}; + + +/** + * Sends "lookup" request for each unresolved property of the object. When + * response is received the properties will be changed with their resolved + * values. + * @param {Object} object Object whose properties should be resolved. + * @param {function(devtools.DebuggerMessage)} Callback to be called when all + * children are resolved. + * @param {boolean} noIntrinsic Whether intrinsic properties should be included. + */ +devtools.DebuggerAgent.prototype.resolveChildren = function(object, callback, noIntrinsic) +{ + if ("handle" in object) { + var result = []; + devtools.DebuggerAgent.formatObjectProperties_(object, result, noIntrinsic); + callback(result); + } else { + this.requestLookup_([object.ref], function(msg) { + var result = []; + if (msg.isSuccess()) { + var handleToObject = msg.getBody(); + var resolved = handleToObject[object.ref]; + devtools.DebuggerAgent.formatObjectProperties_(resolved, result, noIntrinsic); + callback(result); + } else + callback([]); + }); + } +}; + + +/** + * Sends "scope" request for the scope object to resolve its variables. + * @param {Object} scope Scope to be resolved. + * @param {function(Array.<WebInspector.ObjectPropertyProxy>)} callback + * Callback to be called when all scope variables are resolved. + */ +devtools.DebuggerAgent.prototype.resolveScope = function(scope, callback) +{ + var cmd = new devtools.DebugCommand("scope", { + "frameNumber": scope.frameNumber, + "number": scope.index, + "compactFormat": true + }); + devtools.DebuggerAgent.sendCommand_(cmd); + this.requestSeqToCallback_[cmd.getSequenceNumber()] = function(msg) { + var result = []; + if (msg.isSuccess()) { + var scopeObjectJson = msg.getBody().object; + devtools.DebuggerAgent.formatObjectProperties_(scopeObjectJson, result, true /* no intrinsic */); + } + callback(result); + }; +}; + + +/** + * Sends "scopes" request for the frame object to resolve all variables + * available in the frame. + * @param {number} callFrameId Id of call frame whose variables need to + * be resolved. + * @param {function(Object)} callback Callback to be called when all frame + * variables are resolved. + */ +devtools.DebuggerAgent.prototype.resolveFrameVariables_ = function(callFrameId, callback) +{ + var result = {}; + + var frame = this.callFrames_[callFrameId]; + if (!frame) { + callback(result); + return; + } + + var waitingResponses = 0; + function scopeResponseHandler(msg) { + waitingResponses--; + + if (msg.isSuccess()) { + var properties = msg.getBody().object.properties; + for (var j = 0; j < properties.length; j++) + result[properties[j].name] = true; + } + + // When all scopes are resolved invoke the callback. + if (waitingResponses === 0) + callback(result); + }; + + for (var i = 0; i < frame.scopeChain.length; i++) { + var scope = frame.scopeChain[i].objectId; + if (scope.type === devtools.DebuggerAgent.ScopeType.Global) { + // Do not resolve global scope since it takes for too long. + // TODO(yurys): allow to send only property names in the response. + continue; + } + var cmd = new devtools.DebugCommand("scope", { + "frameNumber": scope.frameNumber, + "number": scope.index, + "compactFormat": true + }); + devtools.DebuggerAgent.sendCommand_(cmd); + this.requestSeqToCallback_[cmd.getSequenceNumber()] = scopeResponseHandler; + waitingResponses++; + } +}; + +/** + * Evaluates the expressionString to an object in the call frame and reports + * all its properties. + * @param{string} expressionString Expression whose properties should be + * collected. + * @param{number} callFrameId The frame id. + * @param{function(Object result,bool isException)} reportCompletions Callback + * function. + */ +devtools.DebuggerAgent.prototype.resolveCompletionsOnFrame = function(expressionString, callFrameId, reportCompletions) +{ + if (expressionString) { + expressionString = "var obj = " + expressionString + + "; var names = {}; for (var n in obj) { names[n] = true; };" + + "names;"; + this.evaluateInCallFrame( + callFrameId, + expressionString, + function(result) { + var names = {}; + if (!result.isException) { + var props = result.value.objectId.properties; + // Put all object properties into the map. + for (var i = 0; i < props.length; i++) + names[props[i].name] = true; + } + reportCompletions(names, result.isException); + }); + } else { + this.resolveFrameVariables_(callFrameId, + function(result) { + reportCompletions(result, false /* isException */); + }); + } +}; + + +/** + * @param{number} scriptId + * @return {string} Type of the context of the script with specified id. + */ +devtools.DebuggerAgent.prototype.getScriptContextType = function(scriptId) +{ + return this.parsedScripts_[scriptId].getContextType(); +}; + + +/** + * Removes specified breakpoint from the v8 debugger. + * @param {number} breakpointId Id of the breakpoint in the v8 debugger. + */ +devtools.DebuggerAgent.prototype.requestClearBreakpoint_ = function(breakpointId) +{ + var cmd = new devtools.DebugCommand("clearbreakpoint", { + "breakpoint": breakpointId + }); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Changes breakpoint parameters in the v8 debugger. + * @param {number} breakpointId Id of the breakpoint in the v8 debugger. + * @param {boolean} enabled Whether to enable the breakpoint. + * @param {?string} condition New breakpoint condition. + * @param {number} ignoreCount New ignore count for the breakpoint. + */ +devtools.DebuggerAgent.prototype.requestChangeBreakpoint_ = function(breakpointId, enabled, condition, ignoreCount) +{ + var cmd = new devtools.DebugCommand("changebreakpoint", { + "breakpoint": breakpointId, + "enabled": enabled, + "condition": condition, + "ignoreCount": ignoreCount + }); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Sends "backtrace" request to v8. + */ +devtools.DebuggerAgent.prototype.requestBacktrace_ = function() +{ + var cmd = new devtools.DebugCommand("backtrace", { + "compactFormat":true + }); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Sends command to v8 debugger. + * @param {devtools.DebugCommand} cmd Command to execute. + */ +devtools.DebuggerAgent.sendCommand_ = function(cmd) +{ + RemoteDebuggerCommandExecutor.DebuggerCommand(cmd.toJSONProtocol()); +}; + + +/** + * Tells the v8 debugger to make the next execution step. + * @param {string} action "in", "out" or "next" action. + */ +devtools.DebuggerAgent.prototype.stepCommand_ = function(action) +{ + this.clearExceptionMessage_(); + var cmd = new devtools.DebugCommand("continue", { + "stepaction": action, + "stepcount": 1 + }); + devtools.DebuggerAgent.sendCommand_(cmd); +}; + + +/** + * Sends "lookup" request to v8. + * @param {number} handle Handle to the object to lookup. + */ +devtools.DebuggerAgent.prototype.requestLookup_ = function(handles, callback) +{ + var cmd = new devtools.DebugCommand("lookup", { + "compactFormat":true, + "handles": handles + }); + devtools.DebuggerAgent.sendCommand_(cmd); + this.requestSeqToCallback_[cmd.getSequenceNumber()] = callback; +}; + + +/** + * Sets debugger context id for scripts filtering. + * @param {number} contextId Id of the inspected page global context. + */ +devtools.DebuggerAgent.prototype.setContextId_ = function(contextId) +{ + this.contextId_ = contextId; + + // If it's the first time context id is set request scripts list. + if (this.requestScriptsWhenContextIdSet_) { + this.requestScriptsWhenContextIdSet_ = false; + var cmd = new devtools.DebugCommand("scripts", { + "includeSource": false + }); + devtools.DebuggerAgent.sendCommand_(cmd); + // Force v8 execution so that it gets to processing the requested command. + RemoteDebuggerAgent.processDebugCommands(); + + var debuggerAgent = this; + this.requestSeqToCallback_[cmd.getSequenceNumber()] = function(msg) { + // Handle the response iff the context id hasn't changed since the request + // was issued. Otherwise if the context id did change all up-to-date + // scripts will be pushed in after compile events and there is no need to + // handle the response. + if (contextId === debuggerAgent.contextId_) + debuggerAgent.handleScriptsResponse_(msg); + + // We received initial scripts response so flush the flag and + // see if there is an unhandled backtrace response. + debuggerAgent.waitingForInitialScriptsResponse_ = false; + if (debuggerAgent.pendingBacktraceResponseHandler_) { + debuggerAgent.pendingBacktraceResponseHandler_(); + debuggerAgent.pendingBacktraceResponseHandler_ = null; + } + }; + } +}; + + +/** + * Handles output sent by v8 debugger. The output is either asynchronous event + * or response to a previously sent request. See protocol definitioun for more + * details on the output format. + * @param {string} output + */ +devtools.DebuggerAgent.prototype.handleDebuggerOutput_ = function(output) +{ + var msg; + try { + msg = new devtools.DebuggerMessage(output); + } catch(e) { + debugPrint("Failed to handle debugger response:\n" + e); + throw e; + } + + if (msg.getType() === "event") { + if (msg.getEvent() === "break") + this.handleBreakEvent_(msg); + else if (msg.getEvent() === "exception") + this.handleExceptionEvent_(msg); + else if (msg.getEvent() === "afterCompile") + this.handleAfterCompileEvent_(msg); + } else if (msg.getType() === "response") { + if (msg.getCommand() === "scripts") + this.invokeCallbackForResponse_(msg); + else if (msg.getCommand() === "setbreakpoint") + this.handleSetBreakpointResponse_(msg); + else if (msg.getCommand() === "clearbreakpoint") + this.handleClearBreakpointResponse_(msg); + else if (msg.getCommand() === "backtrace") + this.handleBacktraceResponse_(msg); + else if (msg.getCommand() === "lookup") + this.invokeCallbackForResponse_(msg); + else if (msg.getCommand() === "evaluate") + this.invokeCallbackForResponse_(msg); + else if (msg.getCommand() === "scope") + this.invokeCallbackForResponse_(msg); + } +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleBreakEvent_ = function(msg) +{ + // Force scrips panel to be shown first. + WebInspector.currentPanel = WebInspector.panels.scripts; + + var body = msg.getBody(); + + var line = devtools.DebuggerAgent.v8ToWwebkitLineNumber_(body.sourceLine); + this.requestBacktrace_(); +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleExceptionEvent_ = function(msg) +{ + // Force scrips panel to be shown first. + WebInspector.currentPanel = WebInspector.panels.scripts; + + var body = msg.getBody(); + // No script field in the body means that v8 failed to parse the script. We + // resume execution on parser errors automatically. + if (this.pauseOnExceptions_ && body.script) { + var line = devtools.DebuggerAgent.v8ToWwebkitLineNumber_(body.sourceLine); + this.createExceptionMessage_(body.script.name, line, body.exception.text); + this.requestBacktrace_(); + } else + this.resumeExecution(); +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleScriptsResponse_ = function(msg) +{ + var scripts = msg.getBody(); + for (var i = 0; i < scripts.length; i++) { + var script = scripts[i]; + + // Skip scripts from other tabs. + if (!this.isScriptFromInspectedContext_(script, msg)) + continue; + + // We may already have received the info in an afterCompile event. + if (script.id in this.parsedScripts_) + continue; + this.addScriptInfo_(script, msg); + } +}; + + +/** + * @param {Object} script Json object representing script. + * @param {devtools.DebuggerMessage} msg Debugger response. + */ +devtools.DebuggerAgent.prototype.isScriptFromInspectedContext_ = function(script, msg) +{ + if (!script.context) { + // Always ignore scripts from the utility context. + return false; + } + var context = msg.lookup(script.context.ref); + var scriptContextId = context.data; + if (typeof scriptContextId === "undefined") + return false; // Always ignore scripts from the utility context. + if (this.contextId_ === null) + return true; + // Find the id from context data. The context data has the format "type,id". + var comma = context.data.indexOf(","); + if (comma < 0) + return false; + return (context.data.substring(comma + 1) == this.contextId_); +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleSetBreakpointResponse_ = function(msg) +{ + var requestSeq = msg.getRequestSeq(); + var breakpointInfo = this.requestNumberToBreakpointInfo_[requestSeq]; + if (!breakpointInfo) { + // TODO(yurys): handle this case + return; + } + delete this.requestNumberToBreakpointInfo_[requestSeq]; + if (!msg.isSuccess()) { + // TODO(yurys): handle this case + return; + } + var idInV8 = msg.getBody().breakpoint; + breakpointInfo.setV8Id(idInV8); + + if (breakpointInfo.isRemoved()) + this.requestClearBreakpoint_(idInV8); +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleAfterCompileEvent_ = function(msg) +{ + if (!this.contextId_) { + // Ignore scripts delta if main request has not been issued yet. + return; + } + var script = msg.getBody().script; + + // Ignore scripts from other tabs. + if (!this.isScriptFromInspectedContext_(script, msg)) + return; + this.addScriptInfo_(script, msg); +}; + + +/** + * Adds the script info to the local cache. This method assumes that the script + * is not in the cache yet. + * @param {Object} script Script json object from the debugger message. + * @param {devtools.DebuggerMessage} msg Debugger message containing the script + * data. + */ +devtools.DebuggerAgent.prototype.addScriptInfo_ = function(script, msg) +{ + var context = msg.lookup(script.context.ref); + var contextType; + // Find the type from context data. The context data has the format + // "type,id". + var comma = context.data.indexOf(","); + if (comma < 0) + return + contextType = context.data.substring(0, comma); + this.parsedScripts_[script.id] = new devtools.ScriptInfo(script.id, script.name, script.lineOffset, contextType); + if (this.scriptsPanelInitialized_) { + // Only report script as parsed after scripts panel has been shown. + WebInspector.parsedScriptSource(script.id, script.name, script.source, script.lineOffset); + } +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleClearBreakpointResponse_ = function(msg) +{ + // Do nothing. +}; + + +/** + * Handles response to "backtrace" command. + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.handleBacktraceResponse_ = function(msg) +{ + if (this.waitingForInitialScriptsResponse_) + this.pendingBacktraceResponseHandler_ = this.doHandleBacktraceResponse_.bind(this, msg); + else + this.doHandleBacktraceResponse_(msg); +}; + + +/** + * @param {devtools.DebuggerMessage} msg + */ +devtools.DebuggerAgent.prototype.doHandleBacktraceResponse_ = function(msg) +{ + var frames = msg.getBody().frames; + this.callFrames_ = []; + for (var i = 0; i < frames.length; ++i) + this.callFrames_.push(this.formatCallFrame_(frames[i])); + WebInspector.pausedScript(this.callFrames_); + this.showPendingExceptionMessage_(); + InspectorFrontendHost.activateWindow(); +}; + + +/** + * Evaluates code on given callframe. + */ +devtools.DebuggerAgent.prototype.evaluateInCallFrame = function(callFrameId, code, callback) +{ + var callFrame = this.callFrames_[callFrameId]; + callFrame.evaluate_(code, callback); +}; + + +/** + * Handles response to a command by invoking its callback (if any). + * @param {devtools.DebuggerMessage} msg + * @return {boolean} Whether a callback for the given message was found and + * excuted. + */ +devtools.DebuggerAgent.prototype.invokeCallbackForResponse_ = function(msg) +{ + var callback = this.requestSeqToCallback_[msg.getRequestSeq()]; + if (!callback) { + // It may happend if reset was called. + return false; + } + delete this.requestSeqToCallback_[msg.getRequestSeq()]; + callback(msg); + return true; +}; + + +/** + * @param {Object} stackFrame Frame json object from "backtrace" response. + * @return {!devtools.CallFrame} Object containing information related to the + * call frame in the format expected by ScriptsPanel and its panes. + */ +devtools.DebuggerAgent.prototype.formatCallFrame_ = function(stackFrame) +{ + var func = stackFrame.func; + var sourceId = func.scriptId; + + // Add service script if it does not exist. + var existingScript = this.parsedScripts_[sourceId]; + if (!existingScript) { + this.parsedScripts_[sourceId] = new devtools.ScriptInfo(sourceId, null /* name */, 0 /* line */, "unknown" /* type */, true /* unresolved */); + WebInspector.parsedScriptSource(sourceId, null, null, 0); + } + + var funcName = func.name || func.inferredName || "(anonymous function)"; + var line = devtools.DebuggerAgent.v8ToWwebkitLineNumber_(stackFrame.line); + + // Add basic scope chain info with scope variables. + var scopeChain = []; + var ScopeType = devtools.DebuggerAgent.ScopeType; + for (var i = 0; i < stackFrame.scopes.length; i++) { + var scope = stackFrame.scopes[i]; + scope.frameNumber = stackFrame.index; + var scopeObjectProxy = new WebInspector.ObjectProxy(0, scope, [], 0, "", true); + scopeObjectProxy.isScope = true; + switch(scope.type) { + case ScopeType.Global: + scopeObjectProxy.isDocument = true; + break; + case ScopeType.Local: + scopeObjectProxy.isLocal = true; + scopeObjectProxy.thisObject = devtools.DebuggerAgent.formatObjectProxy_(stackFrame.receiver); + break; + case ScopeType.With: + // Catch scope is treated as a regular with scope by WebKit so we + // also treat it this way. + case ScopeType.Catch: + scopeObjectProxy.isWithBlock = true; + break; + case ScopeType.Closure: + scopeObjectProxy.isClosure = true; + break; + } + scopeChain.push(scopeObjectProxy); + } + return new devtools.CallFrame(stackFrame.index, "function", funcName, sourceId, line, scopeChain); +}; + + +/** + * Collects properties for an object from the debugger response. + * @param {Object} object An object from the debugger protocol response. + * @param {Array.<WebInspector.ObjectPropertyProxy>} result An array to put the + * properties into. + * @param {boolean} noIntrinsic Whether intrinsic properties should be + * included. + */ +devtools.DebuggerAgent.formatObjectProperties_ = function(object, result, noIntrinsic) +{ + devtools.DebuggerAgent.propertiesToProxies_(object.properties, result); + if (noIntrinsic) + return; + + result.push(new WebInspector.ObjectPropertyProxy("__proto__", devtools.DebuggerAgent.formatObjectProxy_(object.protoObject))); + result.push(new WebInspector.ObjectPropertyProxy("constructor", devtools.DebuggerAgent.formatObjectProxy_(object.constructorFunction))); + // Don't add 'prototype' property since it is one of the regualar properties. +}; + + +/** + * For each property in "properties" creates its proxy representative. + * @param {Array.<Object>} properties Receiver properties or locals array from + * "backtrace" response. + * @param {Array.<WebInspector.ObjectPropertyProxy>} Results holder. + */ +devtools.DebuggerAgent.propertiesToProxies_ = function(properties, result) +{ + var map = {}; + for (var i = 0; i < properties.length; ++i) { + var property = properties[i]; + var name = String(property.name); + if (name in map) + continue; + map[name] = true; + var value = devtools.DebuggerAgent.formatObjectProxy_(property.value); + var propertyProxy = new WebInspector.ObjectPropertyProxy(name, value); + result.push(propertyProxy); + } +}; + + +/** + * @param {Object} v An object reference from the debugger response. + * @return {*} The value representation expected by ScriptsPanel. + */ +devtools.DebuggerAgent.formatObjectProxy_ = function(v) +{ + var description; + var hasChildren = false; + if (v.type === "object") { + description = v.className; + hasChildren = true; + } else if (v.type === "function") { + if (v.source) + description = v.source; + else + description = "function " + v.name + "()"; + hasChildren = true; + } else if (v.type === "undefined") + description = "undefined"; + else if (v.type === "null") + description = "null"; + else if (typeof v.value !== "undefined") { + // Check for undefined and null types before checking the value, otherwise + // null/undefined may have blank value. + description = v.value; + } else + description = "<unresolved ref: " + v.ref + ", type: " + v.type + ">"; + + var proxy = new WebInspector.ObjectProxy(0, v, [], 0, description, hasChildren); + proxy.type = v.type; + proxy.isV8Ref = true; + return proxy; +}; + + +/** + * Converts line number from Web Inspector UI(1-based) to v8(0-based). + * @param {number} line Resource line number in Web Inspector UI. + * @return {number} The line number in v8. + */ +devtools.DebuggerAgent.webkitToV8LineNumber_ = function(line) +{ + return line - 1; +}; + + +/** + * Converts line number from v8(0-based) to Web Inspector UI(1-based). + * @param {number} line Resource line number in v8. + * @return {number} The line number in Web Inspector. + */ +devtools.DebuggerAgent.v8ToWwebkitLineNumber_ = function(line) +{ + return line + 1; +}; + + +/** + * @param {number} scriptId Id of the script. + * @param {?string} url Script resource URL if any. + * @param {number} lineOffset First line 0-based offset in the containing + * document. + * @param {string} contextType Type of the script's context: + * "page" - regular script from html page + * "injected" - extension content script + * @param {bool} opt_isUnresolved If true, script will not be resolved. + * @constructor + */ +devtools.ScriptInfo = function(scriptId, url, lineOffset, contextType, opt_isUnresolved) +{ + this.scriptId_ = scriptId; + this.lineOffset_ = lineOffset; + this.contextType_ = contextType; + this.url_ = url; + this.isUnresolved_ = opt_isUnresolved; + + this.lineToBreakpointInfo_ = {}; +}; + + +/** + * @return {number} + */ +devtools.ScriptInfo.prototype.getLineOffset = function() +{ + return this.lineOffset_; +}; + + +/** + * @return {string} + */ +devtools.ScriptInfo.prototype.getContextType = function() +{ + return this.contextType_; +}; + + +/** + * @return {?string} + */ +devtools.ScriptInfo.prototype.getUrl = function() +{ + return this.url_; +}; + + +/** + * @return {?bool} + */ +devtools.ScriptInfo.prototype.isUnresolved = function() +{ + return this.isUnresolved_; +}; + + +/** + * @param {number} line 0-based line number in the script. + * @return {?devtools.BreakpointInfo} Information on a breakpoint at the + * specified line in the script or undefined if there is no breakpoint at + * that line. + */ +devtools.ScriptInfo.prototype.getBreakpointInfo = function(line) +{ + return this.lineToBreakpointInfo_[line]; +}; + + +/** + * Adds breakpoint info to the script. + * @param {devtools.BreakpointInfo} breakpoint + */ +devtools.ScriptInfo.prototype.addBreakpointInfo = function(breakpoint) +{ + this.lineToBreakpointInfo_[breakpoint.getLine()] = breakpoint; +}; + + +/** + * @param {devtools.BreakpointInfo} breakpoint Breakpoint info to be removed. + */ +devtools.ScriptInfo.prototype.removeBreakpointInfo = function(breakpoint) +{ + var line = breakpoint.getLine(); + delete this.lineToBreakpointInfo_[line]; +}; + + + +/** + * @param {number} line Breakpoint 0-based line number in the containing script. + * @constructor + */ +devtools.BreakpointInfo = function(line) +{ + this.line_ = line; + this.v8id_ = -1; + this.removed_ = false; +}; + + +/** + * @return {number} + */ +devtools.BreakpointInfo.prototype.getLine = function(n) +{ + return this.line_; +}; + + +/** + * @return {number} Unique identifier of this breakpoint in the v8 debugger. + */ +devtools.BreakpointInfo.prototype.getV8Id = function(n) +{ + return this.v8id_; +}; + + +/** + * Sets id of this breakpoint in the v8 debugger. + * @param {number} id + */ +devtools.BreakpointInfo.prototype.setV8Id = function(id) +{ + this.v8id_ = id; +}; + + +/** + * Marks this breakpoint as removed from the front-end. + */ +devtools.BreakpointInfo.prototype.markAsRemoved = function() +{ + this.removed_ = true; +}; + + +/** + * @return {boolean} Whether this breakpoint has been removed from the + * front-end. + */ +devtools.BreakpointInfo.prototype.isRemoved = function() +{ + return this.removed_; +}; + + +/** + * Call stack frame data. + * @param {string} id CallFrame id. + * @param {string} type CallFrame type. + * @param {string} functionName CallFrame type. + * @param {string} sourceID Source id. + * @param {number} line Source line. + * @param {Array.<Object>} scopeChain Array of scoped objects. + * @construnctor + */ +devtools.CallFrame = function(id, type, functionName, sourceID, line, scopeChain) +{ + this.id = id; + this.type = type; + this.functionName = functionName; + this.sourceID = sourceID; + this.line = line; + this.scopeChain = scopeChain; +}; + + +/** + * This method issues asynchronous evaluate request, reports result to the + * callback. + * @param {string} expression An expression to be evaluated in the context of + * this call frame. + * @param {function(Object):undefined} callback Callback to report result to. + */ +devtools.CallFrame.prototype.evaluate_ = function(expression, callback) +{ + devtools.tools.getDebuggerAgent().requestEvaluate({ + "expression": expression, + "frame": this.id, + "global": false, + "disable_break": false, + "compactFormat": true + }, + function(response) { + var result = {}; + if (response.isSuccess()) + result.value = devtools.DebuggerAgent.formatObjectProxy_(response.getBody()); + else { + result.value = response.getMessage(); + result.isException = true; + } + callback(result); + }); +}; + + +/** + * JSON based commands sent to v8 debugger. + * @param {string} command Name of the command to execute. + * @param {Object} opt_arguments Command-specific arguments map. + * @constructor + */ +devtools.DebugCommand = function(command, opt_arguments) +{ + this.command_ = command; + this.type_ = "request"; + this.seq_ = ++devtools.DebugCommand.nextSeq_; + if (opt_arguments) + this.arguments_ = opt_arguments; +}; + + +/** + * Next unique number to be used as debugger request sequence number. + * @type {number} + */ +devtools.DebugCommand.nextSeq_ = 1; + + +/** + * @return {number} + */ +devtools.DebugCommand.prototype.getSequenceNumber = function() +{ + return this.seq_; +}; + + +/** + * @return {string} + */ +devtools.DebugCommand.prototype.toJSONProtocol = function() +{ + var json = { + "seq": this.seq_, + "type": this.type_, + "command": this.command_ + } + if (this.arguments_) + json.arguments = this.arguments_; + return JSON.stringify(json); +}; + + +/** + * JSON messages sent from v8 debugger. See protocol definition for more + * details: http://code.google.com/p/v8/wiki/DebuggerProtocol + * @param {string} msg Raw protocol packet as JSON string. + * @constructor + */ +devtools.DebuggerMessage = function(msg) +{ + this.packet_ = JSON.parse(msg); + this.refs_ = []; + if (this.packet_.refs) { + for (var i = 0; i < this.packet_.refs.length; i++) + this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i]; + } +}; + + +/** + * @return {string} The packet type. + */ +devtools.DebuggerMessage.prototype.getType = function() +{ + return this.packet_.type; +}; + + +/** + * @return {?string} The packet event if the message is an event. + */ +devtools.DebuggerMessage.prototype.getEvent = function() +{ + return this.packet_.event; +}; + + +/** + * @return {?string} The packet command if the message is a response to a + * command. + */ +devtools.DebuggerMessage.prototype.getCommand = function() +{ + return this.packet_.command; +}; + + +/** + * @return {number} The packet request sequence. + */ +devtools.DebuggerMessage.prototype.getRequestSeq = function() +{ + return this.packet_.request_seq; +}; + + +/** + * @return {number} Whether the v8 is running after processing the request. + */ +devtools.DebuggerMessage.prototype.isRunning = function() +{ + return this.packet_.running ? true : false; +}; + + +/** + * @return {boolean} Whether the request succeeded. + */ +devtools.DebuggerMessage.prototype.isSuccess = function() +{ + return this.packet_.success ? true : false; +}; + + +/** + * @return {string} + */ +devtools.DebuggerMessage.prototype.getMessage = function() +{ + return this.packet_.message; +}; + + +/** + * @return {Object} Parsed message body json. + */ +devtools.DebuggerMessage.prototype.getBody = function() +{ + return this.packet_.body; +}; + + +/** + * @param {number} handle Object handle. + * @return {?Object} Returns the object with the handle if it was sent in this + * message(some objects referenced by handles may be missing in the message). + */ +devtools.DebuggerMessage.prototype.lookup = function(handle) +{ + return this.refs_[handle]; +}; diff --git a/WebKit/chromium/src/js/DevTools.js b/WebKit/chromium/src/js/DevTools.js new file mode 100644 index 0000000..dcb181b --- /dev/null +++ b/WebKit/chromium/src/js/DevTools.js @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * FIXME: change field naming style to use trailing underscore. + * @fileoverview Tools is a main class that wires all components of the + * DevTools frontend together. It is also responsible for overriding existing + * WebInspector functionality while it is getting upstreamed into WebCore. + */ + +/** + * Dispatches raw message from the host. + * @param {string} remoteName + * @prama {string} methodName + * @param {string} param1, param2, param3 Arguments to dispatch. + */ +devtools$$dispatch = function(remoteName, methodName, param1, param2, param3) +{ + remoteName = "Remote" + remoteName.substring(0, remoteName.length - 8); + var agent = window[remoteName]; + if (!agent) { + debugPrint("No remote agent '" + remoteName + "' found."); + return; + } + var method = agent[methodName]; + if (!method) { + debugPrint("No method '" + remoteName + "." + methodName + "' found."); + return; + } + method.call(this, param1, param2, param3); +}; + + +devtools.ToolsAgent = function() +{ + RemoteToolsAgent.didDispatchOn = WebInspector.Callback.processCallback; + RemoteToolsAgent.frameNavigate = this.frameNavigate_.bind(this); + RemoteToolsAgent.dispatchOnClient = this.dispatchOnClient_.bind(this); + this.debuggerAgent_ = new devtools.DebuggerAgent(); + this.profilerAgent_ = new devtools.ProfilerAgent(); +}; + + +/** + * Resets tools agent to its initial state. + */ +devtools.ToolsAgent.prototype.reset = function() +{ + this.debuggerAgent_.reset(); +}; + + +/** + * @param {string} script Script exression to be evaluated in the context of the + * inspected page. + * @param {function(Object|string, boolean):undefined} opt_callback Function to + * call with the result. + */ +devtools.ToolsAgent.prototype.evaluateJavaScript = function(script, opt_callback) +{ + InspectorBackend.evaluate(script, opt_callback || function() {}); +}; + + +/** + * @return {devtools.DebuggerAgent} Debugger agent instance. + */ +devtools.ToolsAgent.prototype.getDebuggerAgent = function() +{ + return this.debuggerAgent_; +}; + + +/** + * @return {devtools.ProfilerAgent} Profiler agent instance. + */ +devtools.ToolsAgent.prototype.getProfilerAgent = function() +{ + return this.profilerAgent_; +}; + + +/** + * @param {string} url Url frame navigated to. + * @see tools_agent.h + * @private + */ +devtools.ToolsAgent.prototype.frameNavigate_ = function(url) +{ + this.reset(); + // Do not reset Profiles panel. + var profiles = null; + if ("profiles" in WebInspector.panels) { + profiles = WebInspector.panels["profiles"]; + delete WebInspector.panels["profiles"]; + } + WebInspector.reset(); + if (profiles !== null) + WebInspector.panels["profiles"] = profiles; +}; + + +/** + * @param {string} message Serialized call to be dispatched on WebInspector. + * @private + */ +devtools.ToolsAgent.prototype.dispatchOnClient_ = function(message) +{ + var args = JSON.parse(message); + var methodName = args[0]; + var parameters = args.slice(1); + WebInspector[methodName].apply(WebInspector, parameters); +}; + + +/** + * Evaluates js expression. + * @param {string} expr + */ +devtools.ToolsAgent.prototype.evaluate = function(expr) +{ + RemoteToolsAgent.evaluate(expr); +}; + + +/** + * Enables / disables resources panel in the ui. + * @param {boolean} enabled New panel status. + */ +WebInspector.setResourcesPanelEnabled = function(enabled) +{ + InspectorBackend._resourceTrackingEnabled = enabled; + WebInspector.panels.resources.reset(); +}; + + +/** + * Prints string to the inspector console or shows alert if the console doesn't + * exist. + * @param {string} text + */ +function debugPrint(text) { + var console = WebInspector.console; + if (console) { + console.addMessage(new WebInspector.ConsoleMessage( + WebInspector.ConsoleMessage.MessageSource.JS, + WebInspector.ConsoleMessage.MessageType.Log, + WebInspector.ConsoleMessage.MessageLevel.Log, + 1, "chrome://devtools/<internal>", undefined, -1, text)); + } else + alert(text); +} + + +/** + * Global instance of the tools agent. + * @type {devtools.ToolsAgent} + */ +devtools.tools = null; + + +var context = {}; // Used by WebCore's inspector routines. + +/////////////////////////////////////////////////////////////////////////////// +// Here and below are overrides to existing WebInspector methods only. +// TODO(pfeldman): Patch WebCore and upstream changes. +var oldLoaded = WebInspector.loaded; +WebInspector.loaded = function() +{ + devtools.tools = new devtools.ToolsAgent(); + devtools.tools.reset(); + + Preferences.ignoreWhitespace = false; + Preferences.samplingCPUProfiler = true; + Preferences.heapProfilerPresent = true; + oldLoaded.call(this); + + InspectorFrontendHost.loaded(); +}; + + +(function() +{ + + /** + * Handles an F3 keydown event to focus the Inspector search box. + * @param {KeyboardEvent} event Event to optionally handle + * @return {boolean} whether the event has been handled + */ + function handleF3Keydown(event) { + if (event.keyIdentifier === "F3" && !event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) { + var searchField = document.getElementById("search"); + searchField.focus(); + searchField.select(); + event.preventDefault(); + return true; + } + return false; + } + + + var oldKeyDown = WebInspector.documentKeyDown; + /** + * This override allows to intercept keydown events we want to handle in a + * custom way. Some nested documents (iframes) delegate keydown handling to + * WebInspector.documentKeyDown (e.g. SourceFrame). + * @param {KeyboardEvent} event + * @override + */ + WebInspector.documentKeyDown = function(event) { + var isHandled = handleF3Keydown(event); + if (!isHandled) { + // Mute refresh action. + if (event.keyIdentifier === "F5") + event.preventDefault(); + else if (event.keyIdentifier === "U+0052" /* "R" */ && (event.ctrlKey || event.metaKey)) + event.preventDefault(); + else + oldKeyDown.call(this, event); + } + }; +})(); + + +/** + * This override is necessary for adding script source asynchronously. + * @override + */ +WebInspector.ScriptView.prototype.setupSourceFrameIfNeeded = function() +{ + if (!this._frameNeedsSetup) + return; + + this.attach(); + + if (this.script.source) + this.didResolveScriptSource_(); + else { + var self = this; + devtools.tools.getDebuggerAgent().resolveScriptSource( + this.script.sourceID, + function(source) { + self.script.source = source || WebInspector.UIString("<source is not available>"); + self.didResolveScriptSource_(); + }); + } +}; + + +/** + * Performs source frame setup when script source is aready resolved. + */ +WebInspector.ScriptView.prototype.didResolveScriptSource_ = function() +{ + this.sourceFrame.setContent("text/javascript", this.script.source); + this._sourceFrameSetup = true; + delete this._frameNeedsSetup; +}; + + +/** + * @param {string} type Type of the the property value("object" or "function"). + * @param {string} className Class name of the property value. + * @constructor + */ +WebInspector.UnresolvedPropertyValue = function(type, className) +{ + this.type = type; + this.className = className; +}; + + +(function() +{ + var oldShow = WebInspector.ScriptsPanel.prototype.show; + WebInspector.ScriptsPanel.prototype.show = function() + { + devtools.tools.getDebuggerAgent().initUI(); + this.enableToggleButton.visible = false; + oldShow.call(this); + }; +})(); + + +(function InterceptProfilesPanelEvents() +{ + var oldShow = WebInspector.ProfilesPanel.prototype.show; + WebInspector.ProfilesPanel.prototype.show = function() + { + devtools.tools.getProfilerAgent().initializeProfiling(); + this.enableToggleButton.visible = false; + oldShow.call(this); + // Show is called on every show event of a panel, so + // we only need to intercept it once. + WebInspector.ProfilesPanel.prototype.show = oldShow; + }; +})(); + + +/* + * @override + * TODO(mnaganov): Restore l10n when it will be agreed that it is needed. + */ +WebInspector.UIString = function(string) +{ + return String.vsprintf(string, Array.prototype.slice.call(arguments, 1)); +}; + + +// There is no clear way of setting frame title yet. So sniffing main resource +// load. +(function OverrideUpdateResource() { + var originalUpdateResource = WebInspector.updateResource; + WebInspector.updateResource = function(identifier, payload) + { + originalUpdateResource.call(this, identifier, payload); + var resource = this.resources[identifier]; + if (resource && resource.mainResource && resource.finished) + document.title = WebInspector.UIString("Developer Tools - %s", resource.url); + }; +})(); + + +// Highlight extension content scripts in the scripts list. +(function () { + var original = WebInspector.ScriptsPanel.prototype._addScriptToFilesMenu; + WebInspector.ScriptsPanel.prototype._addScriptToFilesMenu = function(script) + { + var result = original.apply(this, arguments); + var debuggerAgent = devtools.tools.getDebuggerAgent(); + var type = debuggerAgent.getScriptContextType(script.sourceID); + var option = script.filesSelectOption; + if (type === "injected" && option) + option.addStyleClass("injected"); + return result; + }; +})(); + + +/** Pending WebKit upstream by apavlov). Fixes iframe vs drag problem. */ +(function() +{ + var originalDragStart = WebInspector.elementDragStart; + WebInspector.elementDragStart = function(element) + { + if (element) { + var glassPane = document.createElement("div"); + glassPane.style.cssText = "position:absolute;width:100%;height:100%;opacity:0;z-index:1"; + glassPane.id = "glass-pane-for-drag"; + element.parentElement.appendChild(glassPane); + } + + originalDragStart.apply(this, arguments); + }; + + var originalDragEnd = WebInspector.elementDragEnd; + WebInspector.elementDragEnd = function() + { + originalDragEnd.apply(this, arguments); + + var glassPane = document.getElementById("glass-pane-for-drag"); + if (glassPane) + glassPane.parentElement.removeChild(glassPane); + }; +})(); + + +(function () { +var orig = InjectedScriptAccess.prototype.getProperties; +InjectedScriptAccess.prototype.getProperties = function(objectProxy, ignoreHasOwnProperty, abbreviate, callback) +{ + if (objectProxy.isScope) + devtools.tools.getDebuggerAgent().resolveScope(objectProxy.objectId, callback); + else if (objectProxy.isV8Ref) + devtools.tools.getDebuggerAgent().resolveChildren(objectProxy.objectId, callback, false); + else + orig.apply(this, arguments); +}; +})(); + + +(function() +{ +InjectedScriptAccess.prototype.evaluateInCallFrame = function(callFrameId, code, objectGroup, callback) +{ + //TODO(pfeldman): remove once 49084 is rolled. + if (!callback) + callback = objectGroup; + devtools.tools.getDebuggerAgent().evaluateInCallFrame(callFrameId, code, callback); +}; +})(); + + +WebInspector.resourceTrackingWasEnabled = function() +{ + InspectorBackend._resourceTrackingEnabled = true; + this.panels.resources.resourceTrackingWasEnabled(); +}; + +WebInspector.resourceTrackingWasDisabled = function() +{ + InspectorBackend._resourceTrackingEnabled = false; + this.panels.resources.resourceTrackingWasDisabled(); +}; + +(function() +{ +var orig = WebInspector.ConsoleMessage.prototype.setMessageBody; +WebInspector.ConsoleMessage.prototype.setMessageBody = function(args) +{ + for (var i = 0; i < args.length; ++i) { + if (typeof args[i] === "string") + args[i] = WebInspector.ObjectProxy.wrapPrimitiveValue(args[i]); + } + orig.call(this, args); +}; +})(); + + +(function() +{ +var orig = InjectedScriptAccess.prototype.getCompletions; +InjectedScriptAccess.prototype.getCompletions = function(expressionString, includeInspectorCommandLineAPI, callFrameId, reportCompletions) +{ + if (typeof callFrameId === "number") + devtools.tools.getDebuggerAgent().resolveCompletionsOnFrame(expressionString, callFrameId, reportCompletions); + else + return orig.apply(this, arguments); +}; +})(); + + +(function() +{ +WebInspector.ElementsPanel.prototype._nodeSearchButtonClicked = function( event) +{ + InspectorBackend.toggleNodeSearch(); + this.nodeSearchButton.toggled = !this.nodeSearchButton.toggled; +}; +})(); + + +// We need to have a place for postponed tasks +// which should be executed when all the messages between agent and frontend +// are processed. + +WebInspector.runAfterPendingDispatchesQueue = []; + +WebInspector.TestController.prototype.runAfterPendingDispatches = function(callback) +{ + WebInspector.runAfterPendingDispatchesQueue.push(callback); +}; + +WebInspector.queuesAreEmpty = function() +{ + var copy = this.runAfterPendingDispatchesQueue.slice(); + this.runAfterPendingDispatchesQueue = []; + for (var i = 0; i < copy.length; ++i) + copy[i].call(this); +}; + +(function() +{ +var originalAddToFrame = InspectorFrontendHost.addResourceSourceToFrame; +InspectorFrontendHost.addResourceSourceToFrame = function(identifier, element) +{ + var resource = WebInspector.resources[identifier]; + if (!resource) + return; + originalAddToFrame.call(this, identifier, resource.mimeType, element); +}; +})(); + +WebInspector.pausedScript = function(callFrames) +{ + this.panels.scripts.debuggerPaused(callFrames); +}; diff --git a/WebKit/chromium/src/js/DevToolsHostStub.js b/WebKit/chromium/src/js/DevToolsHostStub.js new file mode 100644 index 0000000..8b2f46b --- /dev/null +++ b/WebKit/chromium/src/js/DevToolsHostStub.js @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * @fileoverview These stubs emulate backend functionality and allows + * DevTools frontend to function as a standalone web app. + */ + +if (!window["RemoteDebuggerAgent"]) { + +/** + * FIXME: change field naming style to use trailing underscore. + * @constructor + */ +RemoteDebuggerAgentStub = function() +{ +}; + + +RemoteDebuggerAgentStub.prototype.getContextId = function() +{ + RemoteDebuggerAgent.setContextId(3); +}; + + +RemoteDebuggerAgentStub.prototype.processDebugCommands = function() +{ +}; + + +/** + * @constructor + */ +RemoteProfilerAgentStub = function() +{ +}; + + +RemoteProfilerAgentStub.prototype.getActiveProfilerModules = function() +{ + ProfilerStubHelper.GetInstance().getActiveProfilerModules(); +}; + + +RemoteProfilerAgentStub.prototype.getLogLines = function(pos) +{ + ProfilerStubHelper.GetInstance().getLogLines(pos); +}; + + +/** + * @constructor + */ +RemoteToolsAgentStub = function() +{ +}; + + +RemoteToolsAgentStub.prototype.dispatchOnInjectedScript = function() +{ +}; + + +RemoteToolsAgentStub.prototype.dispatchOnInspectorController = function() +{ +}; + + +/** + * @constructor + */ +ProfilerStubHelper = function() +{ + this.activeProfilerModules_ = devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_NONE; + this.heapProfSample_ = 0; + this.log_ = ''; +}; + + +ProfilerStubHelper.GetInstance = function() +{ + if (!ProfilerStubHelper.instance_) + ProfilerStubHelper.instance_ = new ProfilerStubHelper(); + return ProfilerStubHelper.instance_; +}; + + +ProfilerStubHelper.prototype.StopProfiling = function(modules) +{ + this.activeProfilerModules_ &= ~modules; +}; + + +ProfilerStubHelper.prototype.StartProfiling = function(modules) +{ + var profModules = devtools.ProfilerAgent.ProfilerModules; + if (modules & profModules.PROFILER_MODULE_HEAP_SNAPSHOT) { + if (modules & profModules.PROFILER_MODULE_HEAP_STATS) { + this.log_ += + 'heap-sample-begin,"Heap","allocated",' + + (new Date()).getTime() + '\n' + + 'heap-sample-stats,"Heap","allocated",10000,1000\n'; + this.log_ += + 'heap-sample-item,STRING_TYPE,100,1000\n' + + 'heap-sample-item,CODE_TYPE,10,200\n' + + 'heap-sample-item,MAP_TYPE,20,350\n'; + this.log_ += ProfilerStubHelper.HeapSamples[this.heapProfSample_++]; + this.heapProfSample_ %= ProfilerStubHelper.HeapSamples.length; + this.log_ += 'heap-sample-end,"Heap","allocated"\n'; + } + } else { + if (modules & profModules.PROFILER_MODULE_CPU) + this.log_ += ProfilerStubHelper.ProfilerLogBuffer; + this.activeProfilerModules_ |= modules; + } +}; + + +ProfilerStubHelper.prototype.getActiveProfilerModules = function() +{ + var self = this; + setTimeout(function() { + RemoteProfilerAgent.didGetActiveProfilerModules(self.activeProfilerModules_); + }, 100); +}; + + +ProfilerStubHelper.prototype.getLogLines = function(pos) +{ + var profModules = devtools.ProfilerAgent.ProfilerModules; + var logLines = this.log_.substr(pos); + setTimeout(function() { + RemoteProfilerAgent.didGetLogLines(pos + logLines.length, logLines); + }, 100); +}; + + +ProfilerStubHelper.ProfilerLogBuffer = + 'profiler,begin,1\n' + + 'profiler,resume\n' + + 'code-creation,LazyCompile,0x1000,256,"test1 http://aaa.js:1"\n' + + 'code-creation,LazyCompile,0x2000,256,"test2 http://bbb.js:2"\n' + + 'code-creation,LazyCompile,0x3000,256,"test3 http://ccc.js:3"\n' + + 'tick,0x1010,0x0,3\n' + + 'tick,0x2020,0x0,3,0x1010\n' + + 'tick,0x2020,0x0,3,0x1010\n' + + 'tick,0x3010,0x0,3,0x2020, 0x1010\n' + + 'tick,0x2020,0x0,3,0x1010\n' + + 'tick,0x2030,0x0,3,0x2020, 0x1010\n' + + 'tick,0x2020,0x0,3,0x1010\n' + + 'tick,0x1010,0x0,3\n' + + 'profiler,pause\n'; + + +ProfilerStubHelper.HeapSamples = [ + 'heap-js-cons-item,foo,1,100\n' + + 'heap-js-cons-item,bar,20,2000\n' + + 'heap-js-cons-item,Object,5,100\n' + + 'heap-js-ret-item,foo,bar;3\n' + + 'heap-js-ret-item,bar,foo;5\n' + + 'heap-js-ret-item,Object:0x1234,(roots);1\n', + + 'heap-js-cons-item,foo,2000,200000\n' + + 'heap-js-cons-item,bar,10,1000\n' + + 'heap-js-cons-item,Object,6,120\n' + + 'heap-js-ret-item,foo,bar;7,Object:0x1234;10\n' + + 'heap-js-ret-item,bar,foo;10,Object:0x1234;10\n' + + 'heap-js-ret-item,Object:0x1234,(roots);1\n', + + 'heap-js-cons-item,foo,15,1500\n' + + 'heap-js-cons-item,bar,15,1500\n' + + 'heap-js-cons-item,Object,5,100\n' + + 'heap-js-cons-item,Array,3,1000\n' + + 'heap-js-ret-item,foo,bar;3,Array:0x5678;1\n' + + 'heap-js-ret-item,bar,foo;5,Object:0x1234;8,Object:0x5678;2\n' + + 'heap-js-ret-item,Object:0x1234,(roots);1,Object:0x5678;2\n' + + 'heap-js-ret-item,Object:0x5678,(global property);3,Object:0x1234;5\n' + + 'heap-js-ret-item,Array:0x5678,(global property);3,Array:0x5678;2\n', + + 'heap-js-cons-item,bar,20,2000\n' + + 'heap-js-cons-item,Object,6,120\n' + + 'heap-js-ret-item,bar,foo;5,Object:0x1234;1,Object:0x1235;3\n' + + 'heap-js-ret-item,Object:0x1234,(global property);3\n' + + 'heap-js-ret-item,Object:0x1235,(global property);5\n', + + 'heap-js-cons-item,foo,15,1500\n' + + 'heap-js-cons-item,bar,15,1500\n' + + 'heap-js-cons-item,Array,10,1000\n' + + 'heap-js-ret-item,foo,bar;1,Array:0x5678;1\n' + + 'heap-js-ret-item,bar,foo;5\n' + + 'heap-js-ret-item,Array:0x5678,(roots);3\n', + + 'heap-js-cons-item,bar,20,2000\n' + + 'heap-js-cons-item,baz,15,1500\n' + + 'heap-js-ret-item,bar,baz;3\n' + + 'heap-js-ret-item,baz,bar;3\n' +]; + + +/** + * @constructor + */ +RemoteDebuggerCommandExecutorStub = function() +{ +}; + + +RemoteDebuggerCommandExecutorStub.prototype.DebuggerCommand = function(cmd) +{ + if ('{"seq":2,"type":"request","command":"scripts","arguments":{"includeSource":false}}' === cmd) { + var response1 = + '{"seq":5,"request_seq":2,"type":"response","command":"scripts","' + + 'success":true,"body":[{"handle":61,"type":"script","name":"' + + 'http://www/~test/t.js","id":59,"lineOffset":0,"columnOffset":0,' + + '"lineCount":1,"sourceStart":"function fib(n) {","sourceLength":300,' + + '"scriptType":2,"compilationType":0,"context":{"ref":60}}],"refs":[{' + + '"handle":60,"type":"context","data":"page,3"}],"running":false}'; + this.sendResponse_(response1); + } else if ('{"seq":3,"type":"request","command":"scripts","arguments":{"ids":[59],"includeSource":true}}' === cmd) { + this.sendResponse_( + '{"seq":8,"request_seq":3,"type":"response","command":"scripts",' + + '"success":true,"body":[{"handle":1,"type":"script","name":' + + '"http://www/~test/t.js","id":59,"lineOffset":0,"columnOffset":0,' + + '"lineCount":1,"source":"function fib(n) {return n+1;}",' + + '"sourceLength":244,"scriptType":2,"compilationType":0,"context":{' + + '"ref":0}}],"refs":[{"handle":0,"type":"context","data":"page,3}],"' + + '"running":false}'); + } else if (cmd.indexOf('"command":"profile"') !== -1) { + var cmdObj = JSON.parse(cmd); + if (cmdObj.arguments.command === "resume") + ProfilerStubHelper.GetInstance().StartProfiling(parseInt(cmdObj.arguments.modules)); + else if (cmdObj.arguments.command === "pause") + ProfilerStubHelper.GetInstance().StopProfiling(parseInt(cmdObj.arguments.modules)); + else + debugPrint("Unexpected profile command: " + cmdObj.arguments.command); + } else + debugPrint("Unexpected command: " + cmd); +}; + + +RemoteDebuggerCommandExecutorStub.prototype.DebuggerPauseScript = function() +{ +}; + + +RemoteDebuggerCommandExecutorStub.prototype.sendResponse_ = function(response) +{ + setTimeout(function() { + RemoteDebuggerAgent.debuggerOutput(response); + }, 0); +}; + + +DevToolsHostStub = function() +{ + this.isStub = true; +}; +DevToolsHostStub.prototype.__proto__ = WebInspector.InspectorFrontendHostStub.prototype; + + +DevToolsHostStub.prototype.reset = function() +{ +}; + + +DevToolsHostStub.prototype.setting = function() +{ +}; + + +DevToolsHostStub.prototype.setSetting = function() +{ +}; + + +window["RemoteDebuggerAgent"] = new RemoteDebuggerAgentStub(); +window["RemoteDebuggerCommandExecutor"] = new RemoteDebuggerCommandExecutorStub(); +window["RemoteProfilerAgent"] = new RemoteProfilerAgentStub(); +window["RemoteToolsAgent"] = new RemoteToolsAgentStub(); +InspectorFrontendHost = new DevToolsHostStub(); + +} diff --git a/WebKit/chromium/src/js/HeapProfilerPanel.js b/WebKit/chromium/src/js/HeapProfilerPanel.js new file mode 100644 index 0000000..abbf580 --- /dev/null +++ b/WebKit/chromium/src/js/HeapProfilerPanel.js @@ -0,0 +1,966 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * @fileoverview Heap profiler panel implementation. + */ + +WebInspector.ProfilesPanel.prototype.addSnapshot = function(snapshot) { + snapshot.title = WebInspector.UIString("Snapshot %d", snapshot.number); + snapshot.typeId = WebInspector.HeapSnapshotProfileType.TypeId; + + var snapshots = WebInspector.HeapSnapshotProfileType.snapshots; + snapshots.push(snapshot); + + snapshot.listIndex = snapshots.length - 1; + + if (WebInspector.CPUProfile) + this.addProfileHeader(WebInspector.HeapSnapshotProfileType.TypeId, snapshot); + else + this.addProfileHeader(snapshot); + + this.dispatchEventToListeners("snapshot added"); +} + + +WebInspector.HeapSnapshotView = function(parent, profile) +{ + WebInspector.View.call(this); + + this.element.addStyleClass("heap-snapshot-view"); + + this.parent = parent; + this.parent.addEventListener("snapshot added", this._updateBaseOptions, this); + + this.showCountAsPercent = false; + this.showSizeAsPercent = false; + this.showCountDeltaAsPercent = false; + this.showSizeDeltaAsPercent = false; + + this.categories = { + code: new WebInspector.ResourceCategory("code", WebInspector.UIString("Code"), "rgb(255,121,0)"), + data: new WebInspector.ResourceCategory("data", WebInspector.UIString("Objects"), "rgb(47,102,236)") + }; + + var summaryContainer = document.createElement("div"); + summaryContainer.id = "heap-snapshot-summary-container"; + + this.countsSummaryBar = new WebInspector.SummaryBar(this.categories); + this.countsSummaryBar.element.className = "heap-snapshot-summary"; + this.countsSummaryBar.calculator = new WebInspector.HeapSummaryCountCalculator(); + var countsLabel = document.createElement("div"); + countsLabel.className = "heap-snapshot-summary-label"; + countsLabel.textContent = WebInspector.UIString("Count"); + this.countsSummaryBar.element.appendChild(countsLabel); + summaryContainer.appendChild(this.countsSummaryBar.element); + + this.sizesSummaryBar = new WebInspector.SummaryBar(this.categories); + this.sizesSummaryBar.element.className = "heap-snapshot-summary"; + this.sizesSummaryBar.calculator = new WebInspector.HeapSummarySizeCalculator(); + var sizesLabel = document.createElement("label"); + sizesLabel.className = "heap-snapshot-summary-label"; + sizesLabel.textContent = WebInspector.UIString("Size"); + this.sizesSummaryBar.element.appendChild(sizesLabel); + summaryContainer.appendChild(this.sizesSummaryBar.element); + + this.element.appendChild(summaryContainer); + + var columns = { "cons": { title: WebInspector.UIString("Constructor"), disclosure: true, sortable: true }, + "count": { title: WebInspector.UIString("Count"), width: "54px", sortable: true }, + "size": { title: WebInspector.UIString("Size"), width: "72px", sort: "descending", sortable: true }, + "countDelta": { title: WebInspector.UIString("\xb1 Count"), width: "72px", sortable: true }, + "sizeDelta": { title: WebInspector.UIString("\xb1 Size"), width: "72px", sortable: true } }; + + this.dataGrid = new WebInspector.DataGrid(columns); + this.dataGrid.addEventListener("sorting changed", this._sortData, this); + this.dataGrid.element.addEventListener("mousedown", this._mouseDownInDataGrid.bind(this), true); + this.element.appendChild(this.dataGrid.element); + + this.profile = profile; + + this.baseSelectElement = document.createElement("select"); + this.baseSelectElement.className = "status-bar-item"; + this.baseSelectElement.addEventListener("change", this._changeBase.bind(this), false); + this._updateBaseOptions(); + if (this.profile.listIndex > 0) + this.baseSelectElement.selectedIndex = this.profile.listIndex - 1; + else + this.baseSelectElement.selectedIndex = this.profile.listIndex; + this._resetDataGridList(); + + this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item status-bar-item"); + this.percentButton.addEventListener("click", this._percentClicked.bind(this), false); + + this.refresh(); + + this._updatePercentButton(); +}; + +WebInspector.HeapSnapshotView.prototype = { + + get statusBarItems() + { + return [this.baseSelectElement, this.percentButton.element]; + }, + + get profile() + { + return this._profile; + }, + + set profile(profile) + { + this._profile = profile; + }, + + show: function(parentElement) + { + WebInspector.View.prototype.show.call(this, parentElement); + this.dataGrid.updateWidths(); + }, + + hide: function() + { + WebInspector.View.prototype.hide.call(this); + this._currentSearchResultIndex = -1; + }, + + resize: function() + { + if (this.dataGrid) + this.dataGrid.updateWidths(); + }, + + refresh: function() + { + this.dataGrid.removeChildren(); + + var children = this.snapshotDataGridList.children; + var count = children.length; + for (var index = 0; index < count; ++index) + this.dataGrid.appendChild(children[index]); + + this._updateSummaryGraph(); + }, + + refreshShowAsPercents: function() + { + this._updatePercentButton(); + this.refreshVisibleData(); + }, + + _deleteSearchMatchedFlags: function(node) + { + delete node._searchMatchedConsColumn; + delete node._searchMatchedCountColumn; + delete node._searchMatchedSizeColumn; + delete node._searchMatchedCountDeltaColumn; + delete node._searchMatchedSizeDeltaColumn; + }, + + searchCanceled: function() + { + if (this._searchResults) { + for (var i = 0; i < this._searchResults.length; ++i) { + var profileNode = this._searchResults[i].profileNode; + this._deleteSearchMatchedFlags(profileNode); + profileNode.refresh(); + } + } + + delete this._searchFinishedCallback; + this._currentSearchResultIndex = -1; + this._searchResults = []; + }, + + performSearch: function(query, finishedCallback) + { + // Call searchCanceled since it will reset everything we need before doing a new search. + this.searchCanceled(); + + query = query.trimWhitespace(); + + if (!query.length) + return; + + this._searchFinishedCallback = finishedCallback; + + var helper = WebInspector.HeapSnapshotView.SearchHelper; + + var operationAndNumber = helper.parseOperationAndNumber(query); + var operation = operationAndNumber[0]; + var queryNumber = operationAndNumber[1]; + + var percentUnits = helper.percents.test(query); + var megaBytesUnits = helper.megaBytes.test(query); + var kiloBytesUnits = helper.kiloBytes.test(query); + var bytesUnits = helper.bytes.test(query); + + var queryNumberBytes = (megaBytesUnits ? (queryNumber * 1024 * 1024) : (kiloBytesUnits ? (queryNumber * 1024) : queryNumber)); + + function matchesQuery(heapSnapshotDataGridNode) + { + WebInspector.HeapSnapshotView.prototype._deleteSearchMatchedFlags(heapSnapshotDataGridNode); + + if (percentUnits) { + heapSnapshotDataGridNode._searchMatchedCountColumn = operation(heapSnapshotDataGridNode.countPercent, queryNumber); + heapSnapshotDataGridNode._searchMatchedSizeColumn = operation(heapSnapshotDataGridNode.sizePercent, queryNumber); + heapSnapshotDataGridNode._searchMatchedCountDeltaColumn = operation(heapSnapshotDataGridNode.countDeltaPercent, queryNumber); + heapSnapshotDataGridNode._searchMatchedSizeDeltaColumn = operation(heapSnapshotDataGridNode.sizeDeltaPercent, queryNumber); + } else if (megaBytesUnits || kiloBytesUnits || bytesUnits) { + heapSnapshotDataGridNode._searchMatchedSizeColumn = operation(heapSnapshotDataGridNode.size, queryNumberBytes); + heapSnapshotDataGridNode._searchMatchedSizeDeltaColumn = operation(heapSnapshotDataGridNode.sizeDelta, queryNumberBytes); + } else { + heapSnapshotDataGridNode._searchMatchedCountColumn = operation(heapSnapshotDataGridNode.count, queryNumber); + heapSnapshotDataGridNode._searchMatchedCountDeltaColumn = operation(heapSnapshotDataGridNode.countDelta, queryNumber); + } + + if (heapSnapshotDataGridNode.constructorName.hasSubstring(query, true)) + heapSnapshotDataGridNode._searchMatchedConsColumn = true; + + if (heapSnapshotDataGridNode._searchMatchedConsColumn || + heapSnapshotDataGridNode._searchMatchedCountColumn || + heapSnapshotDataGridNode._searchMatchedSizeColumn || + heapSnapshotDataGridNode._searchMatchedCountDeltaColumn || + heapSnapshotDataGridNode._searchMatchedSizeDeltaColumn) { + heapSnapshotDataGridNode.refresh(); + return true; + } + + return false; + } + + var current = this.snapshotDataGridList.children[0]; + var depth = 0; + var info = {}; + + // The second and subsequent levels of heap snapshot nodes represent retainers, + // so recursive expansion will be infinite, since a graph is being traversed. + // So default to a recursion cap of 2 levels. + var maxDepth = 2; + + while (current) { + if (matchesQuery(current)) + this._searchResults.push({ profileNode: current }); + current = current.traverseNextNode(false, null, (depth >= maxDepth), info); + depth += info.depthChange; + } + + finishedCallback(this, this._searchResults.length); + }, + + jumpToFirstSearchResult: WebInspector.CPUProfileView.prototype.jumpToFirstSearchResult, + jumpToLastSearchResult: WebInspector.CPUProfileView.prototype.jumpToLastSearchResult, + jumpToNextSearchResult: WebInspector.CPUProfileView.prototype.jumpToNextSearchResult, + jumpToPreviousSearchResult: WebInspector.CPUProfileView.prototype.jumpToPreviousSearchResult, + showingFirstSearchResult: WebInspector.CPUProfileView.prototype.showingFirstSearchResult, + showingLastSearchResult: WebInspector.CPUProfileView.prototype.showingLastSearchResult, + _jumpToSearchResult: WebInspector.CPUProfileView.prototype._jumpToSearchResult, + + refreshVisibleData: function() + { + var child = this.dataGrid.children[0]; + while (child) { + child.refresh(); + child = child.traverseNextNode(false, null, true); + } + this._updateSummaryGraph(); + }, + + _changeBase: function() { + if (this.baseSnapshot === WebInspector.HeapSnapshotProfileType.snapshots[this.baseSelectElement.selectedIndex]) + return; + + this._resetDataGridList(); + this.refresh(); + + if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults) + return; + + // The current search needs to be performed again. First negate out previous match + // count by calling the search finished callback with a negative number of matches. + // Then perform the search again with the same query and callback. + this._searchFinishedCallback(this, -this._searchResults.length); + this.performSearch(this.currentQuery, this._searchFinishedCallback); + }, + + _createSnapshotDataGridList: function() + { + if (this._snapshotDataGridList) + delete this._snapshotDataGridList; + + this._snapshotDataGridList = new WebInspector.HeapSnapshotDataGridList(this, this.baseSnapshot.entries, this.profile.entries); + return this._snapshotDataGridList; + }, + + _mouseDownInDataGrid: function(event) + { + if (event.detail < 2) + return; + + var cell = event.target.enclosingNodeOrSelfWithNodeName("td"); + if (!cell || (!cell.hasStyleClass("count-column") && !cell.hasStyleClass("size-column") && !cell.hasStyleClass("countDelta-column") && !cell.hasStyleClass("sizeDelta-column"))) + return; + + if (cell.hasStyleClass("count-column")) + this.showCountAsPercent = !this.showCountAsPercent; + else if (cell.hasStyleClass("size-column")) + this.showSizeAsPercent = !this.showSizeAsPercent; + else if (cell.hasStyleClass("countDelta-column")) + this.showCountDeltaAsPercent = !this.showCountDeltaAsPercent; + else if (cell.hasStyleClass("sizeDelta-column")) + this.showSizeDeltaAsPercent = !this.showSizeDeltaAsPercent; + + this.refreshShowAsPercents(); + + event.preventDefault(); + event.stopPropagation(); + }, + + get _isShowingAsPercent() + { + return this.showCountAsPercent && this.showSizeAsPercent && this.showCountDeltaAsPercent && this.showSizeDeltaAsPercent; + }, + + _percentClicked: function(event) + { + var currentState = this._isShowingAsPercent; + this.showCountAsPercent = !currentState; + this.showSizeAsPercent = !currentState; + this.showCountDeltaAsPercent = !currentState; + this.showSizeDeltaAsPercent = !currentState; + this.refreshShowAsPercents(); + }, + + _resetDataGridList: function() + { + this.baseSnapshot = WebInspector.HeapSnapshotProfileType.snapshots[this.baseSelectElement.selectedIndex]; + var lastComparator = WebInspector.HeapSnapshotDataGridList.propertyComparator("size", false); + if (this.snapshotDataGridList) + lastComparator = this.snapshotDataGridList.lastComparator; + this.snapshotDataGridList = this._createSnapshotDataGridList(); + this.snapshotDataGridList.sort(lastComparator, true); + }, + + _sortData: function() + { + var sortAscending = this.dataGrid.sortOrder === "ascending"; + var sortColumnIdentifier = this.dataGrid.sortColumnIdentifier; + var sortProperty = { + "cons": ["constructorName", null], + "count": ["count", null], + "size": ["size", "count"], + "countDelta": this.showCountDeltaAsPercent ? ["countDeltaPercent", null] : ["countDelta", null], + "sizeDelta": this.showSizeDeltaAsPercent ? ["sizeDeltaPercent", "countDeltaPercent"] : ["sizeDelta", "sizeDeltaPercent"] + }[sortColumnIdentifier]; + + this.snapshotDataGridList.sort(WebInspector.HeapSnapshotDataGridList.propertyComparator(sortProperty[0], sortProperty[1], sortAscending)); + + this.refresh(); + }, + + _updateBaseOptions: function() + { + var list = WebInspector.HeapSnapshotProfileType.snapshots; + // We're assuming that snapshots can only be added. + if (this.baseSelectElement.length === list.length) + return; + + for (var i = this.baseSelectElement.length, n = list.length; i < n; ++i) { + var baseOption = document.createElement("option"); + baseOption.label = WebInspector.UIString("Compared to %s", list[i].title); + this.baseSelectElement.appendChild(baseOption); + } + }, + + _updatePercentButton: function() + { + if (this._isShowingAsPercent) { + this.percentButton.title = WebInspector.UIString("Show absolute counts and sizes."); + this.percentButton.toggled = true; + } else { + this.percentButton.title = WebInspector.UIString("Show counts and sizes as percentages."); + this.percentButton.toggled = false; + } + }, + + _updateSummaryGraph: function() + { + this.countsSummaryBar.calculator.showAsPercent = this._isShowingAsPercent; + this.countsSummaryBar.update(this.profile.lowlevels); + + this.sizesSummaryBar.calculator.showAsPercent = this._isShowingAsPercent; + this.sizesSummaryBar.update(this.profile.lowlevels); + } +}; + +WebInspector.HeapSnapshotView.prototype.__proto__ = WebInspector.View.prototype; + +WebInspector.HeapSnapshotView.SearchHelper = { + // In comparators, we assume that a value from a node is passed as the first parameter. + operations: { LESS: function (a, b) { return a !== null && a < b; }, + LESS_OR_EQUAL: function (a, b) { return a !== null && a <= b; }, + EQUAL: function (a, b) { return a !== null && a === b; }, + GREATER_OR_EQUAL: function (a, b) { return a !== null && a >= b; }, + GREATER: function (a, b) { return a !== null && a > b; } }, + + operationParsers: { LESS: /^<(\d+)/, + LESS_OR_EQUAL: /^<=(\d+)/, + GREATER_OR_EQUAL: /^>=(\d+)/, + GREATER: /^>(\d+)/ }, + + parseOperationAndNumber: function(query) + { + var operations = WebInspector.HeapSnapshotView.SearchHelper.operations; + var parsers = WebInspector.HeapSnapshotView.SearchHelper.operationParsers; + for (var operation in parsers) { + var match = query.match(parsers[operation]); + if (match !== null) + return [operations[operation], parseFloat(match[1])]; + } + return [operations.EQUAL, parseFloat(query)]; + }, + + percents: /%$/, + + megaBytes: /MB$/i, + + kiloBytes: /KB$/i, + + bytes: /B$/i +} + +WebInspector.HeapSummaryCalculator = function(lowLevelField) +{ + this.total = 1; + this.lowLevelField = lowLevelField; +} + +WebInspector.HeapSummaryCalculator.prototype = { + computeSummaryValues: function(lowLevels) + { + var highLevels = {data: 0, code: 0}; + this.total = 0; + for (var item in lowLevels) { + var highItem = this._highFromLow(item); + if (highItem) { + var value = lowLevels[item][this.lowLevelField]; + highLevels[highItem] += value; + this.total += value; + } + } + var result = {categoryValues: highLevels}; + if (!this.showAsPercent) + result.total = this.total; + return result; + }, + + formatValue: function(value) + { + if (this.showAsPercent) + return WebInspector.UIString("%.2f%%", value / this.total * 100.0); + else + return this._valueToString(value); + }, + + get showAsPercent() + { + return this._showAsPercent; + }, + + set showAsPercent(x) + { + this._showAsPercent = x; + } +} + +WebInspector.HeapSummaryCountCalculator = function() +{ + WebInspector.HeapSummaryCalculator.call(this, "count"); +} + +WebInspector.HeapSummaryCountCalculator.prototype = { + _highFromLow: function(type) { + if (type === "CODE_TYPE" || type === "SHARED_FUNCTION_INFO_TYPE" || type === "SCRIPT_TYPE") return "code"; + if (type === "STRING_TYPE" || type === "HEAP_NUMBER_TYPE" || type.match(/^JS_/)) return "data"; + return null; + }, + + _valueToString: function(value) { + return value.toString(); + } +} + +WebInspector.HeapSummaryCountCalculator.prototype.__proto__ = WebInspector.HeapSummaryCalculator.prototype; + +WebInspector.HeapSummarySizeCalculator = function() +{ + WebInspector.HeapSummaryCalculator.call(this, "size"); +} + +WebInspector.HeapSummarySizeCalculator.prototype = { + _highFromLow: function(type) { + if (type === "CODE_TYPE" || type === "SHARED_FUNCTION_INFO_TYPE" || type === "SCRIPT_TYPE") return "code"; + if (type === "STRING_TYPE" || type === "HEAP_NUMBER_TYPE" || type.match(/^JS_/) || type.match(/_ARRAY_TYPE$/)) return "data"; + return null; + }, + + _valueToString: Number.bytesToString +} + +WebInspector.HeapSummarySizeCalculator.prototype.__proto__ = WebInspector.HeapSummaryCalculator.prototype; + +WebInspector.HeapSnapshotSidebarTreeElement = function(snapshot) +{ + this.profile = snapshot; + + WebInspector.SidebarTreeElement.call(this, "heap-snapshot-sidebar-tree-item", "", "", snapshot, false); + + this.refreshTitles(); +}; + +WebInspector.HeapSnapshotSidebarTreeElement.prototype = { + get mainTitle() + { + if (this._mainTitle) + return this._mainTitle; + return this.profile.title; + }, + + set mainTitle(x) + { + this._mainTitle = x; + this.refreshTitles(); + } +}; + +WebInspector.HeapSnapshotSidebarTreeElement.prototype.__proto__ = WebInspector.ProfileSidebarTreeElement.prototype; + +WebInspector.HeapSnapshotDataGridNodeWithRetainers = function(owningTree) +{ + this.tree = owningTree; + + WebInspector.DataGridNode.call(this, null, this._hasRetainers); + + this.addEventListener("populate", this._populate, this); +}; + +WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype = { + isEmptySet: function(set) + { + for (var x in set) + return false; + return true; + }, + + get _hasRetainers() + { + return !this.isEmptySet(this.retainers); + }, + + get _parent() + { + // For top-level nodes, return owning tree as a parent, not data grid. + return this.parent !== this.dataGrid ? this.parent : this.tree; + }, + + _populate: function(event) + { + var self = this; + this.produceDiff(this.baseRetainers, this.retainers, function(baseItem, snapshotItem) { + self.appendChild(new WebInspector.HeapSnapshotDataGridRetainerNode(self.snapshotView, baseItem, snapshotItem, self.tree)); + }); + + if (this._parent) { + var currentComparator = this._parent.lastComparator; + if (currentComparator) + this.sort(currentComparator, true); + } + + this.removeEventListener("populate", this._populate, this); + }, + + produceDiff: function(baseEntries, currentEntries, callback) + { + for (var item in currentEntries) + callback(baseEntries[item], currentEntries[item]); + + for (item in baseEntries) { + if (!(item in currentEntries)) + callback(baseEntries[item], null); + } + }, + + sort: function(comparator, force) { + if (!force && this.lastComparator === comparator) + return; + + this.children.sort(comparator); + var childCount = this.children.length; + for (var childIndex = 0; childIndex < childCount; ++childIndex) + this.children[childIndex]._recalculateSiblings(childIndex); + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i]; + if (!force && (!child.expanded || child.lastComparator === comparator)) + continue; + child.sort(comparator, force); + } + this.lastComparator = comparator; + }, + + signForDelta: function(delta) { + if (delta === 0) + return ""; + if (delta > 0) + return "+"; + else + // Math minus sign, same width as plus. + return "\u2212"; + }, + + showDeltaAsPercent: function(value) { + if (value === Number.POSITIVE_INFINITY) + return WebInspector.UIString("new"); + else if (value === Number.NEGATIVE_INFINITY) + return WebInspector.UIString("deleted"); + if (value > 1000.0) + return WebInspector.UIString("%s >1000%%", this.signForDelta(value)); + return WebInspector.UIString("%s%.2f%%", this.signForDelta(value), Math.abs(value)); + }, + + getTotalCount: function() { + if (!this._count) { + this._count = 0; + for (var i = 0, n = this.children.length; i < n; ++i) + this._count += this.children[i].count; + } + return this._count; + }, + + getTotalSize: function() { + if (!this._size) { + this._size = 0; + for (var i = 0, n = this.children.length; i < n; ++i) + this._size += this.children[i].size; + } + return this._size; + }, + + get countPercent() + { + return this.count / this._parent.getTotalCount() * 100.0; + }, + + get sizePercent() + { + return this.size / this._parent.getTotalSize() * 100.0; + }, + + get countDeltaPercent() + { + if (this.baseCount > 0) { + if (this.count > 0) + return this.countDelta / this.baseCount * 100.0; + else + return Number.NEGATIVE_INFINITY; + } else + return Number.POSITIVE_INFINITY; + }, + + get sizeDeltaPercent() + { + if (this.baseSize > 0) { + if (this.size > 0) + return this.sizeDelta / this.baseSize * 100.0; + else + return Number.NEGATIVE_INFINITY; + } else + return Number.POSITIVE_INFINITY; + }, + + get data() + { + var data = {}; + + data["cons"] = this.constructorName; + + if (this.snapshotView.showCountAsPercent) + data["count"] = WebInspector.UIString("%.2f%%", this.countPercent); + else + data["count"] = this.count; + + if (this.size !== null) { + if (this.snapshotView.showSizeAsPercent) + data["size"] = WebInspector.UIString("%.2f%%", this.sizePercent); + else + data["size"] = Number.bytesToString(this.size); + } else + data["size"] = ""; + + if (this.snapshotView.showCountDeltaAsPercent) + data["countDelta"] = this.showDeltaAsPercent(this.countDeltaPercent); + else + data["countDelta"] = WebInspector.UIString("%s%d", this.signForDelta(this.countDelta), Math.abs(this.countDelta)); + + if (this.sizeDelta !== null) { + if (this.snapshotView.showSizeDeltaAsPercent) + data["sizeDelta"] = this.showDeltaAsPercent(this.sizeDeltaPercent); + else + data["sizeDelta"] = WebInspector.UIString("%s%s", this.signForDelta(this.sizeDelta), Number.bytesToString(Math.abs(this.sizeDelta))); + } else + data["sizeDelta"] = ""; + + return data; + }, + + createCell: function(columnIdentifier) + { + var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); + + if ((columnIdentifier === "cons" && this._searchMatchedConsColumn) || + (columnIdentifier === "count" && this._searchMatchedCountColumn) || + (columnIdentifier === "size" && this._searchMatchedSizeColumn) || + (columnIdentifier === "countDelta" && this._searchMatchedCountDeltaColumn) || + (columnIdentifier === "sizeDelta" && this._searchMatchedSizeDeltaColumn)) + cell.addStyleClass("highlight"); + + return cell; + } +}; + +WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype.__proto__ = WebInspector.DataGridNode.prototype; + +WebInspector.HeapSnapshotDataGridNode = function(snapshotView, baseEntry, snapshotEntry, owningTree) +{ + this.snapshotView = snapshotView; + + if (!snapshotEntry) + snapshotEntry = { cons: baseEntry.cons, count: 0, size: 0, retainers: {} }; + this.constructorName = snapshotEntry.cons; + this.count = snapshotEntry.count; + this.size = snapshotEntry.size; + this.retainers = snapshotEntry.retainers; + + if (!baseEntry) + baseEntry = { count: 0, size: 0, retainers: {} }; + this.baseCount = baseEntry.count; + this.countDelta = this.count - this.baseCount; + this.baseSize = baseEntry.size; + this.sizeDelta = this.size - this.baseSize; + this.baseRetainers = baseEntry.retainers; + + WebInspector.HeapSnapshotDataGridNodeWithRetainers.call(this, owningTree); +}; + +WebInspector.HeapSnapshotDataGridNode.prototype.__proto__ = WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype; + +WebInspector.HeapSnapshotDataGridList = function(snapshotView, baseEntries, snapshotEntries) +{ + this.tree = this; + this.snapshotView = snapshotView; + this.children = []; + this.lastComparator = null; + this.populateChildren(baseEntries, snapshotEntries); +}; + +WebInspector.HeapSnapshotDataGridList.prototype = { + appendChild: function(child) + { + this.insertChild(child, this.children.length); + }, + + insertChild: function(child, index) + { + this.children.splice(index, 0, child); + }, + + removeChildren: function() + { + this.children = []; + }, + + populateChildren: function(baseEntries, snapshotEntries) + { + var self = this; + this.produceDiff(baseEntries, snapshotEntries, function(baseItem, snapshotItem) { + self.appendChild(new WebInspector.HeapSnapshotDataGridNode(self.snapshotView, baseItem, snapshotItem, self)); + }); + }, + + produceDiff: WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype.produceDiff, + sort: WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype.sort, + getTotalCount: WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype.getTotalCount, + getTotalSize: WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype.getTotalSize +}; + +WebInspector.HeapSnapshotDataGridList.propertyComparators = [{}, {}]; + +WebInspector.HeapSnapshotDataGridList.propertyComparator = function(property, property2, isAscending) +{ + var propertyHash = property + "#" + property2; + var comparator = this.propertyComparators[(isAscending ? 1 : 0)][propertyHash]; + if (!comparator) { + comparator = function(lhs, rhs) { + var l = lhs[property], r = rhs[property]; + if ((l === null || r === null) && property2 !== null) + l = lhs[property2], r = rhs[property2]; + var result = l < r ? -1 : (l > r ? 1 : 0); + return isAscending ? result : -result; + }; + this.propertyComparators[(isAscending ? 1 : 0)][propertyHash] = comparator; + } + return comparator; +}; + +WebInspector.HeapSnapshotDataGridRetainerNode = function(snapshotView, baseEntry, snapshotEntry, owningTree) +{ + this.snapshotView = snapshotView; + + if (!snapshotEntry) + snapshotEntry = { cons: baseEntry.cons, count: 0, clusters: {} }; + this.constructorName = snapshotEntry.cons; + this.count = snapshotEntry.count; + this.retainers = this._calculateRetainers(this.snapshotView.profile, snapshotEntry.clusters); + + if (!baseEntry) + baseEntry = { count: 0, clusters: {} }; + this.baseCount = baseEntry.count; + this.countDelta = this.count - this.baseCount; + this.baseRetainers = this._calculateRetainers(this.snapshotView.baseSnapshot, baseEntry.clusters); + + this.size = null; + this.sizeDelta = null; + + WebInspector.HeapSnapshotDataGridNodeWithRetainers.call(this, owningTree); +} + +WebInspector.HeapSnapshotDataGridRetainerNode.prototype = { + get sizePercent() + { + return null; + }, + + get sizeDeltaPercent() + { + return null; + }, + + _calculateRetainers: function(snapshot, clusters) { + var retainers = {}; + if (this.isEmptySet(clusters)) { + if (this.constructorName in snapshot.entries) + return snapshot.entries[this.constructorName].retainers; + } else { + // In case when an entry is retained by clusters, we need to gather up the list + // of retainers by merging retainers of every cluster. + // E.g. having such a tree: + // A + // Object:1 10 + // X 3 + // Y 4 + // Object:2 5 + // X 6 + // + // will result in a following retainers list: X 9, Y 4. + for (var clusterName in clusters) { + if (clusterName in snapshot.clusters) { + var clusterRetainers = snapshot.clusters[clusterName].retainers; + for (var clusterRetainer in clusterRetainers) { + var clusterRetainerEntry = clusterRetainers[clusterRetainer]; + if (!(clusterRetainer in retainers)) + retainers[clusterRetainer] = { cons: clusterRetainerEntry.cons, count: 0, clusters: {} }; + retainers[clusterRetainer].count += clusterRetainerEntry.count; + for (var clusterRetainerCluster in clusterRetainerEntry.clusters) + retainers[clusterRetainer].clusters[clusterRetainerCluster] = true; + } + } + } + } + return retainers; + } +}; + +WebInspector.HeapSnapshotDataGridRetainerNode.prototype.__proto__ = WebInspector.HeapSnapshotDataGridNodeWithRetainers.prototype; + + +WebInspector.HeapSnapshotProfileType = function() +{ + WebInspector.ProfileType.call(this, WebInspector.HeapSnapshotProfileType.TypeId, WebInspector.UIString("HEAP SNAPSHOTS")); +} + +WebInspector.HeapSnapshotProfileType.TypeId = "HEAP"; + +WebInspector.HeapSnapshotProfileType.snapshots = []; + +WebInspector.HeapSnapshotProfileType.prototype = { + get buttonTooltip() + { + return WebInspector.UIString("Take heap snapshot."); + }, + + get buttonStyle() + { + return "heap-snapshot-status-bar-item status-bar-item"; + }, + + buttonClicked: function() + { + InspectorBackend.takeHeapSnapshot(); + }, + + get welcomeMessage() + { + return WebInspector.UIString("Get a heap snapshot by pressing<br>the %s button on the status bar."); + }, + + createSidebarTreeElementForProfile: function(profile) + { + var element = new WebInspector.HeapSnapshotSidebarTreeElement(profile); + element.small = false; + return element; + }, + + createView: function(profile) + { + return new WebInspector.HeapSnapshotView(WebInspector.panels.profiles, profile); + } +} + +WebInspector.HeapSnapshotProfileType.prototype.__proto__ = WebInspector.ProfileType.prototype; + + +(function() { + var originalCreatePanels = WebInspector._createPanels; + WebInspector._createPanels = function() { + originalCreatePanels.apply(this, arguments); + if (WebInspector.panels.profiles) + WebInspector.panels.profiles.registerProfileType(new WebInspector.HeapSnapshotProfileType()); + } +})(); diff --git a/WebKit/chromium/src/js/Images/segmentChromium.png b/WebKit/chromium/src/js/Images/segmentChromium.png Binary files differnew file mode 100755 index 0000000..607559b --- /dev/null +++ b/WebKit/chromium/src/js/Images/segmentChromium.png diff --git a/WebKit/chromium/src/js/Images/segmentHoverChromium.png b/WebKit/chromium/src/js/Images/segmentHoverChromium.png Binary files differnew file mode 100755 index 0000000..0a743d9 --- /dev/null +++ b/WebKit/chromium/src/js/Images/segmentHoverChromium.png diff --git a/WebKit/chromium/src/js/Images/segmentHoverEndChromium.png b/WebKit/chromium/src/js/Images/segmentHoverEndChromium.png Binary files differnew file mode 100755 index 0000000..cf62072 --- /dev/null +++ b/WebKit/chromium/src/js/Images/segmentHoverEndChromium.png diff --git a/WebKit/chromium/src/js/Images/segmentSelectedChromium.png b/WebKit/chromium/src/js/Images/segmentSelectedChromium.png Binary files differnew file mode 100755 index 0000000..a1f7251 --- /dev/null +++ b/WebKit/chromium/src/js/Images/segmentSelectedChromium.png diff --git a/WebKit/chromium/src/js/Images/segmentSelectedEndChromium.png b/WebKit/chromium/src/js/Images/segmentSelectedEndChromium.png Binary files differnew file mode 100755 index 0000000..07641db --- /dev/null +++ b/WebKit/chromium/src/js/Images/segmentSelectedEndChromium.png diff --git a/WebKit/chromium/src/js/Images/statusbarBackgroundChromium.png b/WebKit/chromium/src/js/Images/statusbarBackgroundChromium.png Binary files differnew file mode 100755 index 0000000..9d326ac --- /dev/null +++ b/WebKit/chromium/src/js/Images/statusbarBackgroundChromium.png diff --git a/WebKit/chromium/src/js/Images/statusbarBottomBackgroundChromium.png b/WebKit/chromium/src/js/Images/statusbarBottomBackgroundChromium.png Binary files differnew file mode 100755 index 0000000..7c7db0a --- /dev/null +++ b/WebKit/chromium/src/js/Images/statusbarBottomBackgroundChromium.png diff --git a/WebKit/chromium/src/js/Images/statusbarButtonsChromium.png b/WebKit/chromium/src/js/Images/statusbarButtonsChromium.png Binary files differnew file mode 100755 index 0000000..0c6635d --- /dev/null +++ b/WebKit/chromium/src/js/Images/statusbarButtonsChromium.png diff --git a/WebKit/chromium/src/js/Images/statusbarMenuButtonChromium.png b/WebKit/chromium/src/js/Images/statusbarMenuButtonChromium.png Binary files differnew file mode 100755 index 0000000..bf26684 --- /dev/null +++ b/WebKit/chromium/src/js/Images/statusbarMenuButtonChromium.png diff --git a/WebKit/chromium/src/js/Images/statusbarMenuButtonSelectedChromium.png b/WebKit/chromium/src/js/Images/statusbarMenuButtonSelectedChromium.png Binary files differnew file mode 100755 index 0000000..3c0aeec --- /dev/null +++ b/WebKit/chromium/src/js/Images/statusbarMenuButtonSelectedChromium.png diff --git a/WebKit/chromium/src/js/InjectDispatch.js b/WebKit/chromium/src/js/InjectDispatch.js new file mode 100644 index 0000000..e070c42 --- /dev/null +++ b/WebKit/chromium/src/js/InjectDispatch.js @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * @fileoverview Injects "injected" object into the inspectable page. + */ + + +var InspectorControllerDispatcher = {}; + +/** + * Main dispatch method, all calls from the host to InspectorController go + * through this one. + * @param {string} functionName Function to call + * @param {string} json_args JSON-serialized call parameters. + * @return {string} JSON-serialized result of the dispatched call. + */ +InspectorControllerDispatcher.dispatch = function(functionName, json_args) +{ + var params = JSON.parse(json_args); + InspectorBackend[functionName].apply(InspectorBackend, params); +}; + +/** + * Special controller object for APU related messages. Outgoing messages + * are sent to this object if the ApuAgentDispatcher is enabled. + **/ +var ApuAgentDispatcher = { enabled : false }; + +/** + * Dispatches messages to APU. This filters and transforms + * outgoing messages that are used by APU. + * @param {string} method name of the dispatch method. + **/ +ApuAgentDispatcher.dispatchToApu = function(method, args) +{ + if (method !== "addRecordToTimeline" && method !== "updateResource" && method !== "addResource") + return; + // TODO(knorton): Transform args so they can be used + // by APU. + DevToolsAgentHost.dispatchToApu(JSON.stringify(args)); +}; + +/** + * This is called by the InspectorFrontend for serialization. + * We serialize the call and send it to the client over the IPC + * using dispatchOut bound method. + */ +function dispatch(method, var_args) { + // Handle all messages with non-primitieve arguments here. + var args = Array.prototype.slice.call(arguments); + + if (method === "inspectedWindowCleared" || method === "reset" || method === "setAttachedWindow") { + // Filter out messages we don't need here. + // We do it on the sender side since they may have non-serializable + // parameters. + return; + } + + // Sniff some inspector controller state changes in order to support + // cross-navigation instrumentation. Keep names in sync with + // webdevtoolsagent_impl. + if (method === "timelineProfilerWasStarted") + DevToolsAgentHost.runtimeFeatureStateChanged("timeline-profiler", true); + else if (method === "timelineProfilerWasStopped") + DevToolsAgentHost.runtimeFeatureStateChanged("timeline-profiler", false); + else if (method === "resourceTrackingWasEnabled") + DevToolsAgentHost.runtimeFeatureStateChanged("resource-tracking", true); + else if (method === "resourceTrackingWasDisabled") + DevToolsAgentHost.runtimeFeatureStateChanged("resource-tracking", false); + + if (ApuAgentDispatcher.enabled) { + ApuAgentDispatcher.dispatchToApu(method, args); + return; + } + + var call = JSON.stringify(args); + DevToolsAgentHost.dispatch(call); +}; diff --git a/WebKit/chromium/src/js/InspectorControllerImpl.js b/WebKit/chromium/src/js/InspectorControllerImpl.js new file mode 100644 index 0000000..c92a94c --- /dev/null +++ b/WebKit/chromium/src/js/InspectorControllerImpl.js @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * @fileoverview DevTools' implementation of the InspectorController API. + */ + +if (!this.devtools) + devtools = {}; + +devtools.InspectorBackendImpl = function() +{ + WebInspector.InspectorBackendStub.call(this); + this.installInspectorControllerDelegate_("clearMessages"); + this.installInspectorControllerDelegate_("copyNode"); + this.installInspectorControllerDelegate_("deleteCookie"); + this.installInspectorControllerDelegate_("didEvaluateForTestInFrontend"); + this.installInspectorControllerDelegate_("disableResourceTracking"); + this.installInspectorControllerDelegate_("disableTimeline"); + this.installInspectorControllerDelegate_("enableResourceTracking"); + this.installInspectorControllerDelegate_("enableTimeline"); + this.installInspectorControllerDelegate_("getChildNodes"); + this.installInspectorControllerDelegate_("getCookies"); + this.installInspectorControllerDelegate_("getDatabaseTableNames"); + this.installInspectorControllerDelegate_("getDOMStorageEntries"); + this.installInspectorControllerDelegate_("getEventListenersForNode"); + this.installInspectorControllerDelegate_("getResourceContent"); + this.installInspectorControllerDelegate_("highlightDOMNode"); + this.installInspectorControllerDelegate_("hideDOMNodeHighlight"); + this.installInspectorControllerDelegate_("releaseWrapperObjectGroup"); + this.installInspectorControllerDelegate_("removeAttribute"); + this.installInspectorControllerDelegate_("removeDOMStorageItem"); + this.installInspectorControllerDelegate_("removeNode"); + this.installInspectorControllerDelegate_("saveFrontendSettings"); + this.installInspectorControllerDelegate_("setAttribute"); + this.installInspectorControllerDelegate_("setDOMStorageItem"); + this.installInspectorControllerDelegate_("setInjectedScriptSource"); + this.installInspectorControllerDelegate_("setTextNodeValue"); + this.installInspectorControllerDelegate_("startTimelineProfiler"); + this.installInspectorControllerDelegate_("stopTimelineProfiler"); + this.installInspectorControllerDelegate_("storeLastActivePanel"); +}; +devtools.InspectorBackendImpl.prototype.__proto__ = WebInspector.InspectorBackendStub.prototype; + + +/** + * {@inheritDoc}. + */ +devtools.InspectorBackendImpl.prototype.toggleNodeSearch = function() +{ + WebInspector.InspectorBackendStub.prototype.toggleNodeSearch.call(this); + this.callInspectorController_.call(this, "toggleNodeSearch"); + if (!this.searchingForNode()) { + // This is called from ElementsPanel treeOutline's focusNodeChanged(). + DevToolsHost.activateWindow(); + } +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.debuggerEnabled = function() +{ + return true; +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.profilerEnabled = function() +{ + return true; +}; + + +devtools.InspectorBackendImpl.prototype.addBreakpoint = function(sourceID, line, condition) +{ + devtools.tools.getDebuggerAgent().addBreakpoint(sourceID, line, condition); +}; + + +devtools.InspectorBackendImpl.prototype.removeBreakpoint = function(sourceID, line) +{ + devtools.tools.getDebuggerAgent().removeBreakpoint(sourceID, line); +}; + +devtools.InspectorBackendImpl.prototype.updateBreakpoint = function(sourceID, line, condition) +{ + devtools.tools.getDebuggerAgent().updateBreakpoint(sourceID, line, condition); +}; + +devtools.InspectorBackendImpl.prototype.pauseInDebugger = function() +{ + devtools.tools.getDebuggerAgent().pauseExecution(); +}; + + +devtools.InspectorBackendImpl.prototype.resumeDebugger = function() +{ + devtools.tools.getDebuggerAgent().resumeExecution(); +}; + + +devtools.InspectorBackendImpl.prototype.stepIntoStatementInDebugger = function() +{ + devtools.tools.getDebuggerAgent().stepIntoStatement(); +}; + + +devtools.InspectorBackendImpl.prototype.stepOutOfFunctionInDebugger = function() +{ + devtools.tools.getDebuggerAgent().stepOutOfFunction(); +}; + + +devtools.InspectorBackendImpl.prototype.stepOverStatementInDebugger = function() +{ + devtools.tools.getDebuggerAgent().stepOverStatement(); +}; + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.setPauseOnExceptionsState = function(state) +{ + this._setPauseOnExceptionsState = state; + // TODO(yurys): support all three states. See http://crbug.com/32877 + var enabled = (state !== WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions); + return devtools.tools.getDebuggerAgent().setPauseOnExceptions(enabled); +}; + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.pauseOnExceptionsState = function() +{ + return (this._setPauseOnExceptionsState || WebInspector.ScriptsPanel.PauseOnExceptionsState.DontPauseOnExceptions); +}; + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.pauseOnExceptions = function() +{ + return devtools.tools.getDebuggerAgent().pauseOnExceptions(); +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.setPauseOnExceptions = function(value) +{ + return devtools.tools.getDebuggerAgent().setPauseOnExceptions(value); +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.startProfiling = function() +{ + devtools.tools.getProfilerAgent().startProfiling(devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_CPU); +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.stopProfiling = function() +{ + devtools.tools.getProfilerAgent().stopProfiling( devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_CPU); +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.getProfileHeaders = function(callId) +{ + WebInspector.didGetProfileHeaders(callId, []); +}; + + +/** + * Emulate WebKit InspectorController behavior. It stores profiles on renderer side, + * and is able to retrieve them by uid using "getProfile". + */ +devtools.InspectorBackendImpl.prototype.addFullProfile = function(profile) +{ + WebInspector.__fullProfiles = WebInspector.__fullProfiles || {}; + WebInspector.__fullProfiles[profile.uid] = profile; +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.getProfile = function(callId, uid) +{ + if (WebInspector.__fullProfiles && (uid in WebInspector.__fullProfiles)) + WebInspector.didGetProfile(callId, WebInspector.__fullProfiles[uid]); +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.takeHeapSnapshot = function() +{ + devtools.tools.getProfilerAgent().startProfiling(devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_HEAP_SNAPSHOT + | devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_HEAP_STATS + | devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_JS_CONSTRUCTORS); +}; + + +/** + * @override + */ +devtools.InspectorBackendImpl.prototype.dispatchOnInjectedScript = function(callId, injectedScriptId, methodName, argsString, async) +{ + // Encode injectedScriptId into callId + if (typeof injectedScriptId !== "number") + injectedScriptId = 0; + RemoteToolsAgent.dispatchOnInjectedScript(callId, injectedScriptId, methodName, argsString, async); +}; + + +/** + * Installs delegating handler into the inspector controller. + * @param {string} methodName Method to install delegating handler for. + */ +devtools.InspectorBackendImpl.prototype.installInspectorControllerDelegate_ = function(methodName) +{ + this[methodName] = this.callInspectorController_.bind(this, methodName); +}; + + +/** + * Bound function with the installInjectedScriptDelegate_ actual + * implementation. + */ +devtools.InspectorBackendImpl.prototype.callInspectorController_ = function(methodName, var_arg) +{ + var args = Array.prototype.slice.call(arguments, 1); + RemoteToolsAgent.dispatchOnInspectorController(WebInspector.Callback.wrap(function(){}), methodName, JSON.stringify(args)); +}; + + +InspectorBackend = new devtools.InspectorBackendImpl(); diff --git a/WebKit/chromium/src/js/ProfilerAgent.js b/WebKit/chromium/src/js/ProfilerAgent.js new file mode 100644 index 0000000..e08c5d2 --- /dev/null +++ b/WebKit/chromium/src/js/ProfilerAgent.js @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * @fileoverview Provides communication interface to remote v8 profiler. + */ + +/** + * @constructor + */ +devtools.ProfilerAgent = function() +{ + RemoteProfilerAgent.didGetActiveProfilerModules = this._didGetActiveProfilerModules.bind(this); + RemoteProfilerAgent.didGetLogLines = this._didGetLogLines.bind(this); + + /** + * Active profiler modules flags. + * @type {number} + */ + this._activeProfilerModules = devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_NONE; + + /** + * Interval for polling profiler state. + * @type {number} + */ + this._getActiveProfilerModulesInterval = null; + + /** + * Profiler log position. + * @type {number} + */ + this._logPosition = 0; + + /** + * Last requested log position. + * @type {number} + */ + this._lastRequestedLogPosition = -1; + + /** + * Whether log contents retrieval must be forced next time. + * @type {boolean} + */ + this._forceGetLogLines = false; + + /** + * Profiler processor instance. + * @type {devtools.profiler.Processor} + */ + this._profilerProcessor = new devtools.profiler.Processor(); +}; + + +/** + * A copy of enum from include/v8.h + * @enum {number} + */ +devtools.ProfilerAgent.ProfilerModules = { + PROFILER_MODULE_NONE: 0, + PROFILER_MODULE_CPU: 1, + PROFILER_MODULE_HEAP_STATS: 1 << 1, + PROFILER_MODULE_JS_CONSTRUCTORS: 1 << 2, + PROFILER_MODULE_HEAP_SNAPSHOT: 1 << 16 +}; + + +/** + * Sets up callbacks that deal with profiles processing. + */ +devtools.ProfilerAgent.prototype.setupProfilerProcessorCallbacks = function() +{ + // A temporary icon indicating that the profile is being processed. + var processingIcon = new WebInspector.SidebarTreeElement( + "profile-sidebar-tree-item", + WebInspector.UIString("Processing..."), + '', null, false); + var profilesSidebar = WebInspector.panels.profiles.getProfileType(WebInspector.CPUProfileType.TypeId).treeElement; + + this._profilerProcessor.setCallbacks( + function onProfileProcessingStarted() { + // Set visually empty string. Subtitle hiding is done via styles + // manipulation which doesn't play well with dynamic append / removal. + processingIcon.subtitle = " "; + profilesSidebar.appendChild(processingIcon); + }, + function onProfileProcessingStatus(ticksCount) { + processingIcon.subtitle = WebInspector.UIString("%d ticks processed", ticksCount); + }, + function onProfileProcessingFinished(profile) { + profilesSidebar.removeChild(processingIcon); + profile.typeId = WebInspector.CPUProfileType.TypeId; + InspectorBackend.addFullProfile(profile); + WebInspector.addProfileHeader(profile); + // If no profile is currently shown, show the new one. + var profilesPanel = WebInspector.panels.profiles; + if (!profilesPanel.visibleView) { + profilesPanel.showProfile(profile); + } + } + ); +}; + + +/** + * Initializes profiling state. + */ +devtools.ProfilerAgent.prototype.initializeProfiling = function() +{ + this.setupProfilerProcessorCallbacks(); + this._forceGetLogLines = true; + this._getActiveProfilerModulesInterval = setInterval(function() { RemoteProfilerAgent.getActiveProfilerModules(); }, 1000); +}; + + +/** + * Requests the next chunk of log lines. + * @param {boolean} immediately Do not postpone the request. + * @private + */ +devtools.ProfilerAgent.prototype._getNextLogLines = function(immediately) +{ + if (this._lastRequestedLogPosition == this._logPosition) + return; + var pos = this._lastRequestedLogPosition = this._logPosition; + if (immediately) + RemoteProfilerAgent.getLogLines(pos); + else + setTimeout(function() { RemoteProfilerAgent.getLogLines(pos); }, 500); +}; + + +/** + * Starts profiling. + * @param {number} modules List of modules to enable. + */ +devtools.ProfilerAgent.prototype.startProfiling = function(modules) +{ + var cmd = new devtools.DebugCommand("profile", { + "modules": modules, + "command": "resume"}); + devtools.DebuggerAgent.sendCommand_(cmd); + RemoteDebuggerAgent.processDebugCommands(); + if (modules & devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_HEAP_SNAPSHOT) { + // Active modules will not change, instead, a snapshot will be logged. + this._getNextLogLines(); + } +}; + + +/** + * Stops profiling. + */ +devtools.ProfilerAgent.prototype.stopProfiling = function(modules) +{ + var cmd = new devtools.DebugCommand("profile", { + "modules": modules, + "command": "pause"}); + devtools.DebuggerAgent.sendCommand_(cmd); + RemoteDebuggerAgent.processDebugCommands(); +}; + + +/** + * Handles current profiler status. + * @param {number} modules List of active (started) modules. + */ +devtools.ProfilerAgent.prototype._didGetActiveProfilerModules = function(modules) +{ + var profModules = devtools.ProfilerAgent.ProfilerModules; + var profModuleNone = profModules.PROFILER_MODULE_NONE; + if (this._forceGetLogLines || (modules !== profModuleNone && this._activeProfilerModules === profModuleNone)) { + this._forceGetLogLines = false; + // Start to query log data. + this._getNextLogLines(true); + } + this._activeProfilerModules = modules; + // Update buttons. + WebInspector.setRecordingProfile(modules & profModules.PROFILER_MODULE_CPU); +}; + + +/** + * Handles a portion of a profiler log retrieved by getLogLines call. + * @param {number} pos Current position in log. + * @param {string} log A portion of profiler log. + */ +devtools.ProfilerAgent.prototype._didGetLogLines = function(pos, log) +{ + this._logPosition = pos; + if (log.length > 0) + this._profilerProcessor.processLogChunk(log); + else { + // Allow re-reading from the last position. + this._lastRequestedLogPosition = this._logPosition - 1; + if (this._activeProfilerModules === devtools.ProfilerAgent.ProfilerModules.PROFILER_MODULE_NONE) + // No new data and profiling is stopped---suspend log reading. + return; + } + this._getNextLogLines(); +}; diff --git a/WebKit/chromium/src/js/ProfilerProcessor.js b/WebKit/chromium/src/js/ProfilerProcessor.js new file mode 100644 index 0000000..f678d2c --- /dev/null +++ b/WebKit/chromium/src/js/ProfilerProcessor.js @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2010 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. + */ + +/** + * @fileoverview Profiler processor is used to process log file produced + * by V8 and produce an internal profile representation which is used + * for building profile views in "Profiles" tab. + */ + + +/** + * Creates a Profile View builder object compatible with WebKit Profiler UI. + * + * @param {number} samplingRate Number of ms between profiler ticks. + * @constructor + */ +devtools.profiler.WebKitViewBuilder = function(samplingRate) +{ + devtools.profiler.ViewBuilder.call(this, samplingRate); +}; +devtools.profiler.WebKitViewBuilder.prototype.__proto__ = devtools.profiler.ViewBuilder.prototype; + + +/** + * @override + */ +devtools.profiler.WebKitViewBuilder.prototype.createViewNode = function(funcName, totalTime, selfTime, head) +{ + return new devtools.profiler.WebKitViewNode(funcName, totalTime, selfTime, head); +}; + + +/** + * Constructs a Profile View node object for displaying in WebKit Profiler UI. + * + * @param {string} internalFuncName A fully qualified function name. + * @param {number} totalTime Amount of time that application spent in the + * corresponding function and its descendants (not that depending on + * profile they can be either callees or callers.) + * @param {number} selfTime Amount of time that application spent in the + * corresponding function only. + * @param {devtools.profiler.ProfileView.Node} head Profile view head. + * @constructor + */ +devtools.profiler.WebKitViewNode = function(internalFuncName, totalTime, selfTime, head) +{ + devtools.profiler.ProfileView.Node.call(this, internalFuncName, totalTime, selfTime, head); + this.initFuncInfo_(); + this.callUID = internalFuncName; +}; +devtools.profiler.WebKitViewNode.prototype.__proto__ = devtools.profiler.ProfileView.Node.prototype; + + +/** + * RegEx for stripping V8's prefixes of compiled functions. + */ +devtools.profiler.WebKitViewNode.FUNC_NAME_STRIP_RE = /^(?:LazyCompile|Function|Callback): (.*)$/; + + +/** + * RegEx for extracting script source URL and line number. + */ +devtools.profiler.WebKitViewNode.FUNC_NAME_PARSE_RE = /^((?:get | set )?[^ ]+) (.*):(\d+)( \{\d+\})?$/; + + +/** + * Inits "functionName", "url", and "lineNumber" fields using "internalFuncName" + * field. + * @private + */ +devtools.profiler.WebKitViewNode.prototype.initFuncInfo_ = function() +{ + var nodeAlias = devtools.profiler.WebKitViewNode; + this.functionName = this.internalFuncName; + + var strippedName = nodeAlias.FUNC_NAME_STRIP_RE.exec(this.functionName); + if (strippedName) + this.functionName = strippedName[1]; + + var parsedName = nodeAlias.FUNC_NAME_PARSE_RE.exec(this.functionName); + if (parsedName) { + this.functionName = parsedName[1]; + if (parsedName[4]) + this.functionName += parsedName[4]; + this.url = parsedName[2]; + this.lineNumber = parsedName[3]; + } else { + this.url = ''; + this.lineNumber = 0; + } +}; + + +/** + * Ancestor of a profile object that leaves out only JS-related functions. + * @constructor + */ +devtools.profiler.JsProfile = function() +{ + devtools.profiler.Profile.call(this); +}; +devtools.profiler.JsProfile.prototype.__proto__ = devtools.profiler.Profile.prototype; + + +/** + * RegExp that leaves only non-native JS functions. + * @type {RegExp} + */ +devtools.profiler.JsProfile.JS_NON_NATIVE_RE = new RegExp( + "^" + + "(?:Callback:)|" + + "(?:Script: (?!native))|" + + "(?:(?:LazyCompile|Function): [^ ]*(?: (?!native )[^ ]+:\\d+)?$)"); + + +/** + * @override + */ +devtools.profiler.JsProfile.prototype.skipThisFunction = function(name) +{ + return !devtools.profiler.JsProfile.JS_NON_NATIVE_RE.test(name); +}; + + +/** + * Profiler processor. Consumes profiler log and builds profile views. + * FIXME: change field naming style to use trailing underscore. + * + * @param {function(devtools.profiler.ProfileView)} newProfileCallback Callback + * that receives a new processed profile. + * @constructor + */ +devtools.profiler.Processor = function() +{ + var dispatches = { + "code-creation": { + parsers: [null, this.createAddressParser("code"), parseInt, null], + processor: this.processCodeCreation_, backrefs: true, + needsProfile: true }, + "code-move": { parsers: [this.createAddressParser("code"), + this.createAddressParser("code-move-to")], + processor: this.processCodeMove_, backrefs: true, + needsProfile: true }, + "code-delete": { parsers: [this.createAddressParser("code")], + processor: this.processCodeDelete_, backrefs: true, + needsProfile: true }, + "function-creation": { parsers: [this.createAddressParser("code"), + this.createAddressParser("function-obj")], + processor: this.processFunctionCreation_, backrefs: true }, + "function-move": { parsers: [this.createAddressParser("code"), + this.createAddressParser("code-move-to")], + processor: this.processFunctionMove_, backrefs: true }, + "function-delete": { parsers: [this.createAddressParser("code")], + processor: this.processFunctionDelete_, backrefs: true }, + "tick": { parsers: [this.createAddressParser("code"), + this.createAddressParser("stack"), parseInt, "var-args"], + processor: this.processTick_, backrefs: true, needProfile: true }, + "profiler": { parsers: [null, "var-args"], + processor: this.processProfiler_, needsProfile: false }, + "heap-sample-begin": { parsers: [null, null, parseInt], + processor: this.processHeapSampleBegin_ }, + "heap-sample-stats": { parsers: [null, null, parseInt, parseInt], + processor: this.processHeapSampleStats_ }, + "heap-sample-item": { parsers: [null, parseInt, parseInt], + processor: this.processHeapSampleItem_ }, + "heap-js-cons-item": { parsers: [null, parseInt, parseInt], + processor: this.processHeapJsConsItem_ }, + "heap-js-ret-item": { parsers: [null, "var-args"], + processor: this.processHeapJsRetItem_ }, + "heap-sample-end": { parsers: [null, null], + processor: this.processHeapSampleEnd_ }, + // Not used in DevTools Profiler. + "shared-library": null, + // Obsolete row types. + "code-allocate": null, + "begin-code-region": null, + "end-code-region": null}; + + if (devtools.profiler.Profile.VERSION === 2) { + dispatches["tick"] = { parsers: [this.createAddressParser("code"), + this.createAddressParser("stack"), + this.createAddressParser("func"), parseInt, "var-args"], + processor: this.processTickV2_, backrefs: true }; + } + + devtools.profiler.LogReader.call(this, dispatches); + + /** + * Callback that is called when a new profile is encountered in the log. + * @type {function()} + */ + this.startedProfileProcessing_ = null; + + /** + * Callback that is called periodically to display processing status. + * @type {function()} + */ + this.profileProcessingStatus_ = null; + + /** + * Callback that is called when a profile has been processed and is ready + * to be shown. + * @type {function(devtools.profiler.ProfileView)} + */ + this.finishedProfileProcessing_ = null; + + /** + * The current profile. + * @type {devtools.profiler.JsProfile} + */ + this.currentProfile_ = null; + + /** + * Builder of profile views. Created during "profiler,begin" event processing. + * @type {devtools.profiler.WebKitViewBuilder} + */ + this.viewBuilder_ = null; + + /** + * Next profile id. + * @type {number} + */ + this.profileId_ = 1; + + /** + * Counter for processed ticks. + * @type {number} + */ + this.ticksCount_ = 0; + + /** + * Interval id for updating processing status. + * @type {number} + */ + this.processingInterval_ = null; + + /** + * The current heap snapshot. + * @type {string} + */ + this.currentHeapSnapshot_ = null; + + /** + * Next heap snapshot id. + * @type {number} + */ + this.heapSnapshotId_ = 1; +}; +devtools.profiler.Processor.prototype.__proto__ = devtools.profiler.LogReader.prototype; + + +/** + * @override + */ +devtools.profiler.Processor.prototype.printError = function(str) +{ + debugPrint(str); +}; + + +/** + * @override + */ +devtools.profiler.Processor.prototype.skipDispatch = function(dispatch) +{ + return dispatch.needsProfile && this.currentProfile_ === null; +}; + + +/** + * Sets profile processing callbacks. + * + * @param {function()} started Started processing callback. + * @param {function(devtools.profiler.ProfileView)} finished Finished + * processing callback. + */ +devtools.profiler.Processor.prototype.setCallbacks = function(started, processing, finished) +{ + this.startedProfileProcessing_ = started; + this.profileProcessingStatus_ = processing; + this.finishedProfileProcessing_ = finished; +}; + + +/** + * An address for the fake "(program)" entry. WebKit's visualisation + * has assumptions on how the top of the call tree should look like, + * and we need to add a fake entry as the topmost function. This + * address is chosen because it's the end address of the first memory + * page, which is never used for code or data, but only as a guard + * page for catching AV errors. + * + * @type {number} + */ +devtools.profiler.Processor.PROGRAM_ENTRY = 0xffff; +/** + * @type {string} + */ +devtools.profiler.Processor.PROGRAM_ENTRY_STR = "0xffff"; + + +/** + * Sets new profile callback. + * @param {function(devtools.profiler.ProfileView)} callback Callback function. + */ +devtools.profiler.Processor.prototype.setNewProfileCallback = function(callback) +{ + this.newProfileCallback_ = callback; +}; + + +devtools.profiler.Processor.prototype.processProfiler_ = function(state, params) +{ + switch (state) { + case "resume": + if (this.currentProfile_ === null) { + this.currentProfile_ = new devtools.profiler.JsProfile(); + // see the comment for devtools.profiler.Processor.PROGRAM_ENTRY + this.currentProfile_.addCode("Function", "(program)", devtools.profiler.Processor.PROGRAM_ENTRY, 1); + if (this.startedProfileProcessing_) + this.startedProfileProcessing_(); + this.ticksCount_ = 0; + var self = this; + if (this.profileProcessingStatus_) { + this.processingInterval_ = window.setInterval( + function() { self.profileProcessingStatus_(self.ticksCount_); }, + 1000); + } + } + break; + case "pause": + if (this.currentProfile_ !== null) { + window.clearInterval(this.processingInterval_); + this.processingInterval_ = null; + if (this.finishedProfileProcessing_) + this.finishedProfileProcessing_(this.createProfileForView()); + this.currentProfile_ = null; + } + break; + case "begin": + var samplingRate = NaN; + if (params.length > 0) + samplingRate = parseInt(params[0]); + if (isNaN(samplingRate)) + samplingRate = 1; + this.viewBuilder_ = new devtools.profiler.WebKitViewBuilder(samplingRate); + break; + // These events are valid but aren't used. + case "compression": + case "end": break; + default: + throw new Error("unknown profiler state: " + state); + } +}; + + +devtools.profiler.Processor.prototype.processCodeCreation_ = function(type, start, size, name) +{ + this.currentProfile_.addCode(this.expandAlias(type), name, start, size); +}; + + +devtools.profiler.Processor.prototype.processCodeMove_ = function(from, to) +{ + this.currentProfile_.moveCode(from, to); +}; + + +devtools.profiler.Processor.prototype.processCodeDelete_ = function(start) +{ + this.currentProfile_.deleteCode(start); +}; + + +devtools.profiler.Processor.prototype.processFunctionCreation_ = function(functionAddr, codeAddr) +{ + this.currentProfile_.addCodeAlias(functionAddr, codeAddr); +}; + + +devtools.profiler.Processor.prototype.processFunctionMove_ = function(from, to) +{ + this.currentProfile_.safeMoveDynamicCode(from, to); +}; + + +devtools.profiler.Processor.prototype.processFunctionDelete_ = function(start) +{ + this.currentProfile_.safeDeleteDynamicCode(start); +}; + + +// TODO(mnaganov): Remove after next V8 roll. +devtools.profiler.Processor.prototype.processTick_ = function(pc, sp, vmState, stack) +{ + // see the comment for devtools.profiler.Processor.PROGRAM_ENTRY + stack.push(devtools.profiler.Processor.PROGRAM_ENTRY_STR); + this.currentProfile_.recordTick(this.processStack(pc, stack)); + this.ticksCount_++; +}; + + +devtools.profiler.Processor.prototype.processTickV2_ = function(pc, sp, func, vmState, stack) +{ + // see the comment for devtools.profiler.Processor.PROGRAM_ENTRY + stack.push(devtools.profiler.Processor.PROGRAM_ENTRY_STR); + + + if (func) { + var funcEntry = this.currentProfile_.findEntry(func); + if (!funcEntry || !funcEntry.isJSFunction || !funcEntry.isJSFunction()) + func = 0; + else { + var currEntry = this.currentProfile_.findEntry(pc); + if (!currEntry || !currEntry.isJSFunction || currEntry.isJSFunction()) { + func = 0; + } + } + } + + this.currentProfile_.recordTick(this.processStack(pc, func, stack)); + this.ticksCount_++; +}; + + +devtools.profiler.Processor.prototype.processHeapSampleBegin_ = function(space, state, ticks) +{ + if (space !== "Heap") return; + this.currentHeapSnapshot_ = { + number: this.heapSnapshotId_++, + entries: {}, + clusters: {}, + lowlevels: {}, + ticks: ticks + }; +}; + + +devtools.profiler.Processor.prototype.processHeapSampleStats_ = function(space, state, capacity, used) +{ + if (space !== "Heap") return; +}; + + +devtools.profiler.Processor.prototype.processHeapSampleItem_ = function(item, number, size) +{ + if (!this.currentHeapSnapshot_) return; + this.currentHeapSnapshot_.lowlevels[item] = { + type: item, count: number, size: size + }; +}; + + +devtools.profiler.Processor.prototype.processHeapJsConsItem_ = function(item, number, size) +{ + if (!this.currentHeapSnapshot_) return; + this.currentHeapSnapshot_.entries[item] = { + cons: item, count: number, size: size, retainers: {} + }; +}; + + +devtools.profiler.Processor.prototype.processHeapJsRetItem_ = function(item, retainersArray) +{ + if (!this.currentHeapSnapshot_) return; + var rawRetainers = {}; + for (var i = 0, n = retainersArray.length; i < n; ++i) { + var entry = retainersArray[i].split(";"); + rawRetainers[entry[0]] = parseInt(entry[1], 10); + } + + function mergeRetainers(entry) { + for (var rawRetainer in rawRetainers) { + var consName = rawRetainer.indexOf(":") !== -1 ? rawRetainer.split(":")[0] : rawRetainer; + if (!(consName in entry.retainers)) + entry.retainers[consName] = { cons: consName, count: 0, clusters: {} }; + var retainer = entry.retainers[consName]; + retainer.count += rawRetainers[rawRetainer]; + if (consName !== rawRetainer) + retainer.clusters[rawRetainer] = true; + } + } + + if (item.indexOf(":") !== -1) { + // Array, Function, or Object instances cluster case. + if (!(item in this.currentHeapSnapshot_.clusters)) { + this.currentHeapSnapshot_.clusters[item] = { + cons: item, retainers: {} + }; + } + mergeRetainers(this.currentHeapSnapshot_.clusters[item]); + item = item.split(":")[0]; + } + mergeRetainers(this.currentHeapSnapshot_.entries[item]); +}; + + +devtools.profiler.Processor.prototype.processHeapSampleEnd_ = function(space, state) +{ + if (space !== "Heap") return; + var snapshot = this.currentHeapSnapshot_; + this.currentHeapSnapshot_ = null; + WebInspector.panels.profiles.addSnapshot(snapshot); +}; + + +/** + * Creates a profile for further displaying in ProfileView. + */ +devtools.profiler.Processor.prototype.createProfileForView = function() +{ + var profile = this.viewBuilder_.buildView(this.currentProfile_.getTopDownProfile()); + profile.uid = this.profileId_++; + profile.title = UserInitiatedProfileName + "." + profile.uid; + return profile; +}; diff --git a/WebKit/chromium/src/js/Tests.js b/WebKit/chromium/src/js/Tests.js new file mode 100644 index 0000000..fa0c99f --- /dev/null +++ b/WebKit/chromium/src/js/Tests.js @@ -0,0 +1,1862 @@ +/* + * Copyright (C) 2010 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. + */ + + +/** + * @fileoverview This file contains small testing framework along with the + * test suite for the frontend. These tests are a part of the continues build + * and are executed by the devtools_sanity_unittest.cc as a part of the + * Interactive UI Test suite. + * FIXME: change field naming style to use trailing underscore. + */ + +if (window.domAutomationController) { + +var ___interactiveUiTestsMode = true; + +/** + * Test suite for interactive UI tests. + * @constructor + */ +TestSuite = function() +{ + this.controlTaken_ = false; + this.timerId_ = -1; +}; + + +/** + * Reports test failure. + * @param {string} message Failure description. + */ +TestSuite.prototype.fail = function(message) +{ + if (this.controlTaken_) + this.reportFailure_(message); + else + throw message; +}; + + +/** + * Equals assertion tests that expected === actual. + * @param {Object} expected Expected object. + * @param {Object} actual Actual object. + * @param {string} opt_message User message to print if the test fails. + */ +TestSuite.prototype.assertEquals = function(expected, actual, opt_message) +{ + if (expected !== actual) { + var message = "Expected: '" + expected + "', but was '" + actual + "'"; + if (opt_message) + message = opt_message + "(" + message + ")"; + this.fail(message); + } +}; + + +/** + * True assertion tests that value == true. + * @param {Object} value Actual object. + * @param {string} opt_message User message to print if the test fails. + */ +TestSuite.prototype.assertTrue = function(value, opt_message) +{ + this.assertEquals(true, !!value, opt_message); +}; + + +/** + * Contains assertion tests that string contains substring. + * @param {string} string Outer. + * @param {string} substring Inner. + */ +TestSuite.prototype.assertContains = function(string, substring) +{ + if (string.indexOf(substring) === -1) + this.fail("Expected to: '" + string + "' to contain '" + substring + "'"); +}; + + +/** + * Takes control over execution. + */ +TestSuite.prototype.takeControl = function() +{ + this.controlTaken_ = true; + // Set up guard timer. + var self = this; + this.timerId_ = setTimeout(function() { + self.reportFailure_("Timeout exceeded: 20 sec"); + }, 20000); +}; + + +/** + * Releases control over execution. + */ +TestSuite.prototype.releaseControl = function() +{ + if (this.timerId_ !== -1) { + clearTimeout(this.timerId_); + this.timerId_ = -1; + } + this.reportOk_(); +}; + + +/** + * Async tests use this one to report that they are completed. + */ +TestSuite.prototype.reportOk_ = function() +{ + window.domAutomationController.send("[OK]"); +}; + + +/** + * Async tests use this one to report failures. + */ +TestSuite.prototype.reportFailure_ = function(error) +{ + if (this.timerId_ !== -1) { + clearTimeout(this.timerId_); + this.timerId_ = -1; + } + window.domAutomationController.send("[FAILED] " + error); +}; + + +/** + * Runs all global functions starting with "test" as unit tests. + */ +TestSuite.prototype.runTest = function(testName) +{ + try { + this[testName](); + if (!this.controlTaken_) + this.reportOk_(); + } catch (e) { + this.reportFailure_(e); + } +}; + + +/** + * @param {string} panelName Name of the panel to show. + */ +TestSuite.prototype.showPanel = function(panelName) +{ + // Open Scripts panel. + var toolbar = document.getElementById("toolbar"); + var button = toolbar.getElementsByClassName(panelName)[0]; + button.click(); + this.assertEquals(WebInspector.panels[panelName], WebInspector.currentPanel); +}; + + +/** + * Overrides the method with specified name until it's called first time. + * @param {Object} receiver An object whose method to override. + * @param {string} methodName Name of the method to override. + * @param {Function} override A function that should be called right after the + * overriden method returns. + * @param {boolean} opt_sticky Whether restore original method after first run + * or not. + */ +TestSuite.prototype.addSniffer = function(receiver, methodName, override, opt_sticky) +{ + var orig = receiver[methodName]; + if (typeof orig !== "function") + this.fail("Cannot find method to override: " + methodName); + var test = this; + receiver[methodName] = function(var_args) { + try { + var result = orig.apply(this, arguments); + } finally { + if (!opt_sticky) + receiver[methodName] = orig; + } + // In case of exception the override won't be called. + try { + override.apply(this, arguments); + } catch (e) { + test.fail("Exception in overriden method '" + methodName + "': " + e); + } + return result; + }; +}; + + +// UI Tests + + +/** + * Tests that the real injected host is present in the context. + */ +TestSuite.prototype.testHostIsPresent = function() +{ + this.assertTrue(typeof InspectorFrontendHost === "object" && !InspectorFrontendHost.isStub); +}; + + +/** + * Tests elements tree has an "HTML" root. + */ +TestSuite.prototype.testElementsTreeRoot = function() +{ + var doc = WebInspector.domAgent.document; + this.assertEquals("HTML", doc.documentElement.nodeName); + this.assertTrue(doc.documentElement.hasChildNodes()); +}; + + +/** + * Tests that main resource is present in the system and that it is + * the only resource. + */ +TestSuite.prototype.testMainResource = function() +{ + var tokens = []; + var resources = WebInspector.resources; + for (var id in resources) + tokens.push(resources[id].lastPathComponent); + this.assertEquals("simple_page.html", tokens.join(",")); +}; + + +/** + * Tests that resources tab is enabled when corresponding item is selected. + */ +TestSuite.prototype.testEnableResourcesTab = function() +{ + this.showPanel("resources"); + + var test = this; + this.addSniffer(WebInspector, "updateResource", + function(identifier, payload) { + test.assertEquals("simple_page.html", payload.lastPathComponent); + WebInspector.panels.resources.refresh(); + WebInspector.panels.resources.revealAndSelectItem(WebInspector.resources[identifier]); + + test.releaseControl(); + }); + + // Following call should lead to reload that we capture in the + // addResource override. + WebInspector.panels.resources._enableResourceTracking(); + + // We now have some time to report results to controller. + this.takeControl(); +}; + + +/** + * Tests that correct content length is reported for resources. + */ +TestSuite.prototype.testResourceContentLength = function() +{ + this.showPanel("resources"); + var test = this; + + var png = false; + var html = false; + this.addSniffer(WebInspector, "updateResource", + function(identifier, payload) { + if (!payload.didLengthChange) + return; + var resource = WebInspector.resources[identifier]; + if (!resource || !resource.url) + return; + if (resource.url.search("image.html$") !== -1) { + var expectedLength = 87; + test.assertTrue( + resource.contentLength <= expectedLength, + "image.html content length is greater thatn expected."); + if (expectedLength === resource.contentLength) + html = true; + } else if (resource.url.search("image.png") !== -1) { + var expectedLength = 257796; + test.assertTrue( + resource.contentLength <= expectedLength, + "image.png content length is greater than expected."); + if (expectedLength === resource.contentLength) + png = true; + } + if (html && png) { + // Wait 1 second before releasing control to check that the content + // lengths are not updated anymore. + setTimeout(function() { + test.releaseControl(); + }, 1000); + } + }, true); + + // Make sure resource tracking is on. + WebInspector.panels.resources._enableResourceTracking(); + // Reload inspected page to update all resources. + test.evaluateInConsole_( + "window.location.reload(true);", + function(resultText) { + test.assertEquals("undefined", resultText, "Unexpected result of reload()."); + }); + + // We now have some time to report results to controller. + this.takeControl(); +}; + + +/** + * Tests resource headers. + */ +TestSuite.prototype.testResourceHeaders = function() +{ + this.showPanel("resources"); + + var test = this; + + var responseOk = false; + var timingOk = false; + + this.addSniffer(WebInspector, "updateResource", + function(identifier, payload) { + var resource = this.resources[identifier]; + if (!resource || resource.mainResource) { + // We are only interested in secondary resources in this test. + return; + } + + var requestHeaders = JSON.stringify(resource.requestHeaders); + test.assertContains(requestHeaders, "Accept"); + + if (payload.didResponseChange) { + var responseHeaders = JSON.stringify(resource.responseHeaders); + test.assertContains(responseHeaders, "Content-type"); + test.assertContains(responseHeaders, "Content-Length"); + test.assertTrue(typeof resource.responseReceivedTime !== "undefined"); + responseOk = true; + } + + if (payload.didTimingChange) { + test.assertTrue(typeof resource.startTime !== "undefined"); + timingOk = true; + } + + if (payload.didCompletionChange) { + test.assertTrue(responseOk); + test.assertTrue(timingOk); + test.assertTrue(typeof resource.endTime !== "undefined"); + test.releaseControl(); + } + }, true); + + WebInspector.panels.resources._enableResourceTracking(); + this.takeControl(); +}; + + +/** + * Tests the mime type of a cached (HTTP 304) resource. + */ +TestSuite.prototype.testCachedResourceMimeType = function() +{ + this.showPanel("resources"); + + var test = this; + var hasReloaded = false; + + this.addSniffer(WebInspector, "updateResource", + function(identifier, payload) { + var resource = this.resources[identifier]; + if (!resource || resource.mainResource) { + // We are only interested in secondary resources in this test. + return; + } + + if (payload.didResponseChange) { + // Test server uses a default mime type for JavaScript files. + test.assertEquals("text/html", payload.mimeType); + if (!hasReloaded) { + hasReloaded = true; + // Reload inspected page to update all resources. + test.evaluateInConsole_("window.location.reload(true);", function() {}); + } else + test.releaseControl(); + } + + }, true); + + WebInspector.panels.resources._enableResourceTracking(); + this.takeControl(); +}; + + +/** + * Tests that profiler works. + */ +TestSuite.prototype.testProfilerTab = function() +{ + this.showPanel("profiles"); + + var test = this; + this.addSniffer(WebInspector.panels.profiles, "addProfileHeader", + function(typeOrProfile, profile) { + if (!profile) + profile = typeOrProfile; + var panel = WebInspector.panels.profiles; + panel.showProfile(profile); + var node = panel.visibleView.profileDataGridTree.children[0]; + // Iterate over displayed functions and search for a function + // that is called "fib" or "eternal_fib". If found, it will mean + // that we actually have profiled page's code. + while (node) { + if (node.functionName.indexOf("fib") !== -1) + test.releaseControl(); + node = node.traverseNextNode(true, null, true); + } + + test.fail(); + }); + var ticksCount = 0; + var tickRecord = "\nt,"; + this.addSniffer(RemoteProfilerAgent, "didGetLogLines", + function(posIgnored, log) { + var pos = 0; + while ((pos = log.indexOf(tickRecord, pos)) !== -1) { + pos += tickRecord.length; + ticksCount++; + } + if (ticksCount > 100) + InspectorBackend.stopProfiling(); + }, true); + + InspectorBackend.startProfiling(); + this.takeControl(); +}; + + +/** + * Tests that scripts tab can be open and populated with inspected scripts. + */ +TestSuite.prototype.testShowScriptsTab = function() +{ + this.showPanel("scripts"); + var test = this; + // There should be at least main page script. + this._waitUntilScriptsAreParsed(["debugger_test_page.html$"], + function() { + test.releaseControl(); + }); + // Wait until all scripts are added to the debugger. + this.takeControl(); +}; + + +/** + * Tests that scripts tab is populated with inspected scripts even if it + * hadn't been shown by the moment inspected paged refreshed. + * @see http://crbug.com/26312 + */ +TestSuite.prototype.testScriptsTabIsPopulatedOnInspectedPageRefresh = function() +{ + var test = this; + this.assertEquals(WebInspector.panels.elements, WebInspector.currentPanel, "Elements panel should be current one."); + + this.addSniffer(devtools.DebuggerAgent.prototype, "reset", waitUntilScriptIsParsed); + + // Reload inspected page. It will reset the debugger agent. + test.evaluateInConsole_( + "window.location.reload(true);", + function(resultText) { + test.assertEquals("undefined", resultText, "Unexpected result of reload()."); + }); + + function waitUntilScriptIsParsed() { + var parsed = devtools.tools.getDebuggerAgent().parsedScripts_; + for (var id in parsed) { + var url = parsed[id].getUrl(); + if (url && url.search("debugger_test_page.html$") !== -1) { + checkScriptsPanel(); + return; + } + } + test.addSniffer(devtools.DebuggerAgent.prototype, "addScriptInfo_", waitUntilScriptIsParsed); + } + + function checkScriptsPanel() { + test.showPanel("scripts"); + test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html$"]), "Inspected script not found in the scripts list"); + test.releaseControl(); + } + + // Wait until all scripts are added to the debugger. + this.takeControl(); +}; + + +/** + * Tests that scripts list contains content scripts. + */ +TestSuite.prototype.testContentScriptIsPresent = function() +{ + this.showPanel("scripts"); + var test = this; + + test._waitUntilScriptsAreParsed( + ["page_with_content_script.html$", "simple_content_script.js$"], + function() { + test.releaseControl(); + }); + + // Wait until all scripts are added to the debugger. + this.takeControl(); +}; + + +/** + * Tests that scripts are not duplicaed on Scripts tab switch. + */ +TestSuite.prototype.testNoScriptDuplicatesOnPanelSwitch = function() +{ + var test = this; + + // There should be two scripts: one for the main page and another + // one which is source of console API(see + // InjectedScript._ensureCommandLineAPIInstalled). + var expectedScriptsCount = 2; + var parsedScripts = []; + + this.showPanel("scripts"); + + + function switchToElementsTab() { + test.showPanel("elements"); + setTimeout(switchToScriptsTab, 0); + } + + function switchToScriptsTab() { + test.showPanel("scripts"); + setTimeout(checkScriptsPanel, 0); + } + + function checkScriptsPanel() { + test.assertTrue(!!WebInspector.panels.scripts.visibleView, "No visible script view."); + test.assertTrue(test._scriptsAreParsed(["debugger_test_page.html$"]), "Some scripts are missing."); + checkNoDuplicates(); + test.releaseControl(); + } + + function checkNoDuplicates() { + var scriptSelect = document.getElementById("scripts-files"); + var options = scriptSelect.options; + for (var i = 0; i < options.length; i++) { + var scriptName = options[i].text; + for (var j = i + 1; j < options.length; j++) + test.assertTrue(scriptName !== options[j].text, "Found script duplicates: " + test.optionsToString_(options)); + } + } + + test._waitUntilScriptsAreParsed( + ["debugger_test_page.html$"], + function() { + checkNoDuplicates(); + setTimeout(switchToElementsTab, 0); + }); + + + // Wait until all scripts are added to the debugger. + this.takeControl(); +}; + + +/** + * Tests that a breakpoint can be set. + */ +TestSuite.prototype.testSetBreakpoint = function() +{ + var test = this; + this.showPanel("scripts"); + + var breakpointLine = 12; + + this._waitUntilScriptsAreParsed(["debugger_test_page.html"], + function() { + test.showMainPageScriptSource_( + "debugger_test_page.html", + function(view, url) { + view._addBreakpoint(breakpointLine); + // Force v8 execution. + RemoteDebuggerAgent.processDebugCommands(); + test.waitForSetBreakpointResponse_(url, breakpointLine, + function() { + test.releaseControl(); + }); + }); + }); + + this.takeControl(); +}; + + +/** + * Tests that pause on exception works. + */ +TestSuite.prototype.testPauseOnException = function() +{ + this.showPanel("scripts"); + var test = this; + + // TODO(yurys): remove else branch once the states are supported. + if (WebInspector.ScriptsPanel.PauseOnExceptionsState) { + while (WebInspector.currentPanel.pauseOnExceptionButton.state !== WebInspector.ScriptsPanel.PauseOnExceptionsState.PauseOnUncaughtExceptions) + WebInspector.currentPanel.pauseOnExceptionButton.element.click(); + } else { + // Make sure pause on exceptions is on. + if (!WebInspector.currentPanel.pauseOnExceptionButton.toggled) + WebInspector.currentPanel.pauseOnExceptionButton.element.click(); + } + + this._executeCodeWhenScriptsAreParsed("handleClick()", ["pause_on_exception.html$"]); + + this._waitForScriptPause( + { + functionsOnStack: ["throwAnException", "handleClick", "(anonymous function)"], + lineNumber: 6, + lineText: " return unknown_var;" + }, + function() { + test.releaseControl(); + }); + + this.takeControl(); +}; + + +// Tests that debugger works correctly if pause event occurs when DevTools +// frontend is being loaded. +TestSuite.prototype.testPauseWhenLoadingDevTools = function() +{ + this.showPanel("scripts"); + var test = this; + + var expectations = { + functionsOnStack: ["callDebugger"], + lineNumber: 8, + lineText: " debugger;" + }; + + + // Script execution can already be paused. + if (WebInspector.currentPanel.paused) { + var callFrame = WebInspector.currentPanel.sidebarPanes.callstack.selectedCallFrame; + this.assertEquals(expectations.functionsOnStack[0], callFrame.functionName); + var callbackInvoked = false; + this._checkSourceFrameWhenLoaded(expectations, function() { + callbackInvoked = true; + if (test.controlTaken_) + test.releaseControl(); + }); + if (!callbackInvoked) { + test.takeControl(); + } + return; + } + + this._waitForScriptPause( + { + functionsOnStack: ["callDebugger"], + lineNumber: 8, + lineText: " debugger;" + }, + function() { + test.releaseControl(); + }); + this.takeControl(); +}; + + +// Tests that pressing "Pause" will pause script execution if the script +// is already running. +TestSuite.prototype.testPauseWhenScriptIsRunning = function() +{ + this.showPanel("scripts"); + var test = this; + + test.evaluateInConsole_( + 'setTimeout("handleClick()" , 0)', + function(resultText) { + test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText); + testScriptPauseAfterDelay(); + }); + + // Wait for some time to make sure that inspected page is running the + // infinite loop. + function testScriptPauseAfterDelay() { + setTimeout(testScriptPause, 300); + } + + function testScriptPause() { + // The script should be in infinite loop. Click "Pause" button to + // pause it and wait for the result. + WebInspector.panels.scripts.pauseButton.click(); + + test._waitForScriptPause( + { + functionsOnStack: ["handleClick", "(anonymous function)"], + lineNumber: 5, + lineText: " while(true) {" + }, + function() { + test.releaseControl(); + }); + } + + this.takeControl(); +}; + + +/** + * Serializes options collection to string. + * @param {HTMLOptionsCollection} options + * @return {string} + */ +TestSuite.prototype.optionsToString_ = function(options) +{ + var names = []; + for (var i = 0; i < options.length; i++) + names.push('"' + options[i].text + '"'); + return names.join(","); +}; + + +/** + * Ensures that main HTML resource is selected in Scripts panel and that its + * source frame is setup. Invokes the callback when the condition is satisfied. + * @param {HTMLOptionsCollection} options + * @param {function(WebInspector.SourceView,string)} callback + */ +TestSuite.prototype.showMainPageScriptSource_ = function(scriptName, callback) +{ + var test = this; + + var scriptSelect = document.getElementById("scripts-files"); + var options = scriptSelect.options; + + test.assertTrue(options.length, "Scripts list is empty"); + + // Select page's script if it's not current option. + var scriptResource; + if (options[scriptSelect.selectedIndex].text === scriptName) + scriptResource = options[scriptSelect.selectedIndex].representedObject; + else { + var pageScriptIndex = -1; + for (var i = 0; i < options.length; i++) { + if (options[i].text === scriptName) { + pageScriptIndex = i; + break; + } + } + test.assertTrue(-1 !== pageScriptIndex, "Script with url " + scriptName + " not found among " + test.optionsToString_(options)); + scriptResource = options[pageScriptIndex].representedObject; + + // Current panel is "Scripts". + WebInspector.currentPanel._showScriptOrResource(scriptResource); + test.assertEquals(pageScriptIndex, scriptSelect.selectedIndex, "Unexpected selected option index."); + } + + test.assertTrue(scriptResource instanceof WebInspector.Resource, + "Unexpected resource class."); + test.assertTrue(!!scriptResource.url, "Resource URL is null."); + test.assertTrue(scriptResource.url.search(scriptName + "$") !== -1, "Main HTML resource should be selected."); + + var scriptsPanel = WebInspector.panels.scripts; + + var view = scriptsPanel.visibleView; + test.assertTrue(view instanceof WebInspector.SourceView); + + if (!view.sourceFrame._loaded) { + test.addSniffer(view, "_sourceFrameSetupFinished", function(event) { + callback(view, scriptResource.url); + }); + } else + callback(view, scriptResource.url); +}; + + +/* + * Evaluates the code in the console as if user typed it manually and invokes + * the callback when the result message is received and added to the console. + * @param {string} code + * @param {function(string)} callback + */ +TestSuite.prototype.evaluateInConsole_ = function(code, callback) +{ + WebInspector.console.visible = true; + WebInspector.console.prompt.text = code; + WebInspector.console.promptElement.dispatchEvent( TestSuite.createKeyEvent("Enter")); + + this.addSniffer(WebInspector.ConsoleView.prototype, "addMessage", + function(commandResult) { + callback(commandResult.toMessageElement().textContent); + }); +}; + + +/* + * Waits for "setbreakpoint" response, checks that corresponding breakpoint + * was successfully set and invokes the callback if it was. + * @param {string} scriptUrl + * @param {number} breakpointLine + * @param {function()} callback + */ +TestSuite.prototype.waitForSetBreakpointResponse_ = function(scriptUrl, breakpointLine, callback) +{ + var test = this; + test.addSniffer( + devtools.DebuggerAgent.prototype, + "handleSetBreakpointResponse_", + function(msg) { + var bps = this.urlToBreakpoints_[scriptUrl]; + test.assertTrue(!!bps, "No breakpoints for line " + breakpointLine); + var line = devtools.DebuggerAgent.webkitToV8LineNumber_(breakpointLine); + test.assertTrue(!!bps[line].getV8Id(), "Breakpoint id was not assigned."); + callback(); + }); +}; + + +/** + * Tests eval on call frame. + */ +TestSuite.prototype.testEvalOnCallFrame = function() +{ + this.showPanel("scripts"); + + var breakpointLine = 16; + + var test = this; + this.addSniffer(devtools.DebuggerAgent.prototype, "handleScriptsResponse_", + function(msg) { + test.showMainPageScriptSource_( + "debugger_test_page.html", + function(view, url) { + view._addBreakpoint(breakpointLine); + // Force v8 execution. + RemoteDebuggerAgent.processDebugCommands(); + test.waitForSetBreakpointResponse_(url, breakpointLine, setBreakpointCallback); + }); + }); + + function setBreakpointCallback() { + // Since breakpoints are ignored in evals' calculate() function is + // execute after zero-timeout so that the breakpoint is hit. + test.evaluateInConsole_( + 'setTimeout("calculate(123)" , 0)', + function(resultText) { + test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText); + waitForBreakpointHit(); + }); + } + + function waitForBreakpointHit() { + test.addSniffer( + devtools.DebuggerAgent.prototype, + "handleBacktraceResponse_", + function(msg) { + test.assertEquals(2, this.callFrames_.length, "Unexpected stack depth on the breakpoint. " + JSON.stringify(msg)); + test.assertEquals("calculate", this.callFrames_[0].functionName, "Unexpected top frame function."); + // Evaluate "e+1" where "e" is an argument of "calculate" function. + test.evaluateInConsole_( + "e+1", + function(resultText) { + test.assertEquals("124", resultText, 'Unexpected "e+1" value.'); + test.releaseControl(); + }); + }); + } + + this.takeControl(); +}; + + +/** + * Tests that console auto completion works when script execution is paused. + */ +TestSuite.prototype.testCompletionOnPause = function() +{ + this.showPanel("scripts"); + var test = this; + this._executeCodeWhenScriptsAreParsed("handleClick()", ["completion_on_pause.html$"]); + + this._waitForScriptPause( + { + functionsOnStack: ["innerFunction", "handleClick", "(anonymous function)"], + lineNumber: 9, + lineText: " debugger;" + }, + showConsole); + + function showConsole() { + test.addSniffer(WebInspector.console, "afterShow", testLocalsCompletion); + WebInspector.showConsole(); + } + + function testLocalsCompletion() { + checkCompletions("th", ["parameter1", "closureLocal", "p", "createClosureLocal"], testThisCompletion); + } + + function testThisCompletion() { + checkCompletions("this.", ["field1", "field2", "m"], testFieldCompletion); + } + + function testFieldCompletion() { + checkCompletions("this.field1.", ["id", "name"], function() { test.releaseControl(); }); + } + + function checkCompletions(expression, expectedProperties, callback) { + test.addSniffer(WebInspector.console, "_reportCompletions", + function(bestMatchOnly, completionsReadyCallback, dotNotation, bracketNotation, prefix, result, isException) { + test.assertTrue(!isException, "Exception while collecting completions"); + for (var i = 0; i < expectedProperties.length; i++) { + var name = expectedProperties[i]; + test.assertTrue(result[name], "Name " + name + " not found among the completions: " + JSON.stringify(result)); + } + setTimeout(callback, 0); + }); + WebInspector.console.prompt.text = expression; + WebInspector.console.prompt.autoCompleteSoon(); + } + + this.takeControl(); +}; + + +/** + * Tests that inspected page doesn't hang on reload if it contains a syntax + * error and DevTools window is open. + */ +TestSuite.prototype.testAutoContinueOnSyntaxError = function() +{ + this.showPanel("scripts"); + var test = this; + + function checkScriptsList() { + var scriptSelect = document.getElementById("scripts-files"); + var options = scriptSelect.options; + // There should be only console API source (see + // InjectedScript._ensureCommandLineAPIInstalled) since the page script + // contains a syntax error. + for (var i = 0 ; i < options.length; i++) { + if (options[i].text.search("script_syntax_error.html$") !== -1) + test.fail("Script with syntax error should not be in the list of parsed scripts."); + } + } + + this.addSniffer(devtools.DebuggerAgent.prototype, "handleScriptsResponse_", + function(msg) { + checkScriptsList(); + + // Reload inspected page. + test.evaluateInConsole_( + "window.location.reload(true);", + function(resultText) { + test.assertEquals("undefined", resultText, "Unexpected result of reload()."); + waitForExceptionEvent(); + }); + }); + + function waitForExceptionEvent() { + var exceptionCount = 0; + test.addSniffer( + devtools.DebuggerAgent.prototype, + "handleExceptionEvent_", + function(msg) { + exceptionCount++; + test.assertEquals(1, exceptionCount, "Too many exceptions."); + test.assertEquals(undefined, msg.getBody().script, "Unexpected exception: " + JSON.stringify(msg)); + test.releaseControl(); + }); + + // Check that the script is not paused on parse error. + test.addSniffer( + WebInspector, + "pausedScript", + function(callFrames) { + test.fail("Script execution should not pause on syntax error."); + }); + } + + this.takeControl(); +}; + + +/** + * Checks current execution line against expectations. + * @param {WebInspector.SourceFrame} sourceFrame + * @param {number} lineNumber Expected line number + * @param {string} lineContent Expected line text + */ +TestSuite.prototype._checkExecutionLine = function(sourceFrame, lineNumber, lineContent) +{ + this.assertEquals(lineNumber, sourceFrame.executionLine, "Unexpected execution line number."); + this.assertEquals(lineContent, sourceFrame._textModel.line(lineNumber - 1), "Unexpected execution line text."); +} + + +/** + * Checks that all expected scripts are present in the scripts list + * in the Scripts panel. + * @param {Array.<string>} expected Regular expressions describing + * expected script names. + * @return {boolean} Whether all the scripts are in "scripts-files" select + * box + */ +TestSuite.prototype._scriptsAreParsed = function(expected) +{ + var scriptSelect = document.getElementById("scripts-files"); + var options = scriptSelect.options; + + // Check that at least all the expected scripts are present. + var missing = expected.slice(0); + for (var i = 0 ; i < options.length; i++) { + for (var j = 0; j < missing.length; j++) { + if (options[i].text.search(missing[j]) !== -1) { + missing.splice(j, 1); + break; + } + } + } + return missing.length === 0; +}; + + +/** + * Waits for script pause, checks expectations, and invokes the callback. + * @param {Object} expectations Dictionary of expectations + * @param {function():void} callback + */ +TestSuite.prototype._waitForScriptPause = function(expectations, callback) +{ + var test = this; + // Wait until script is paused. + test.addSniffer( + WebInspector, + "pausedScript", + function(callFrames) { + var functionsOnStack = []; + for (var i = 0; i < callFrames.length; i++) + functionsOnStack.push(callFrames[i].functionName); + + test.assertEquals(expectations.functionsOnStack.join(","), functionsOnStack.join(","), "Unexpected stack."); + + // Check that execution line where the script is paused is + // expected one. + test._checkSourceFrameWhenLoaded(expectations, callback); + }); +}; + + +/** + * Waits for current source frame to load, checks expectations, and invokes + * the callback. + * @param {Object} expectations Dictionary of expectations + * @param {function():void} callback + */ +TestSuite.prototype._checkSourceFrameWhenLoaded = function(expectations, callback) +{ + var test = this; + + var frame = WebInspector.currentPanel.visibleView.sourceFrame; + if (frame._loaded) + checkExecLine(); + else { + setTimeout(function() { + test._checkSourceFrameWhenLoaded(expectations, callback); + }, 100); + } + function checkExecLine() { + test._checkExecutionLine(frame, expectations.lineNumber, expectations.lineText); + callback(); + } +}; + + +/** + * Performs sequence of steps. + * @param {Array.<Object|Function>} Array [expectations1,action1,expectations2, + * action2,...,actionN]. + */ +TestSuite.prototype._performSteps = function(actions) +{ + var test = this; + var i = 0; + function doNextAction() { + if (i > 0) + actions[i++](); + if (i < actions.length - 1) + test._waitForScriptPause(actions[i++], doNextAction); + } + doNextAction(); +}; + + +/** + * Waits until all the scripts are parsed and asynchronously executes the code + * in the inspected page. + */ +TestSuite.prototype._executeCodeWhenScriptsAreParsed = function(code, expectedScripts) +{ + var test = this; + + function executeFunctionInInspectedPage() { + // Since breakpoints are ignored in evals' calculate() function is + // execute after zero-timeout so that the breakpoint is hit. + test.evaluateInConsole_( + 'setTimeout("' + code + '" , 0)', + function(resultText) { + test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText); + }); + } + + test._waitUntilScriptsAreParsed(expectedScripts, executeFunctionInInspectedPage); +}; + + +/** + * Waits until all the scripts are parsed and invokes the callback. + */ +TestSuite.prototype._waitUntilScriptsAreParsed = function(expectedScripts, callback) +{ + var test = this; + + function waitForAllScripts() { + if (test._scriptsAreParsed(expectedScripts)) + callback(); + else + test.addSniffer(WebInspector, "parsedScriptSource", waitForAllScripts); + } + + waitForAllScripts(); +}; + + +/** + * Waits until all debugger scripts are parsed and executes "a()" in the + * inspected page. + */ +TestSuite.prototype._executeFunctionForStepTest = function() +{ + this._executeCodeWhenScriptsAreParsed("a()", ["debugger_step.html$", "debugger_step.js$"]); +}; + + +/** + * Tests step over in the debugger. + */ +TestSuite.prototype.testStepOver = function() +{ + this.showPanel("scripts"); + var test = this; + + this._executeFunctionForStepTest(); + + this._performSteps([ + { + functionsOnStack: ["d","a","(anonymous function)"], + lineNumber: 3, + lineText: " debugger;" + }, + function() { + document.getElementById("scripts-step-over").click(); + }, + { + functionsOnStack: ["d","a","(anonymous function)"], + lineNumber: 5, + lineText: " var y = fact(10);" + }, + function() { + document.getElementById("scripts-step-over").click(); + }, + { + functionsOnStack: ["d","a","(anonymous function)"], + lineNumber: 6, + lineText: " return y;" + }, + function() { + test.releaseControl(); + } + ]); + + test.takeControl(); +}; + + +/** + * Tests step out in the debugger. + */ +TestSuite.prototype.testStepOut = function() +{ + this.showPanel("scripts"); + var test = this; + + this._executeFunctionForStepTest(); + + this._performSteps([ + { + functionsOnStack: ["d","a","(anonymous function)"], + lineNumber: 3, + lineText: " debugger;" + }, + function() { + document.getElementById("scripts-step-out").click(); + }, + { + functionsOnStack: ["a","(anonymous function)"], + lineNumber: 8, + lineText: " printResult(result);" + }, + function() { + test.releaseControl(); + } + ]); + + test.takeControl(); +}; + + +/** + * Tests step in in the debugger. + */ +TestSuite.prototype.testStepIn = function() +{ + this.showPanel("scripts"); + var test = this; + + this._executeFunctionForStepTest(); + + this._performSteps([ + { + functionsOnStack: ["d","a","(anonymous function)"], + lineNumber: 3, + lineText: " debugger;" + }, + function() { + document.getElementById("scripts-step-over").click(); + }, + { + functionsOnStack: ["d","a","(anonymous function)"], + lineNumber: 5, + lineText: " var y = fact(10);" + }, + function() { + document.getElementById("scripts-step-into").click(); + }, + { + functionsOnStack: ["fact","d","a","(anonymous function)"], + lineNumber: 15, + lineText: " return r;" + }, + function() { + test.releaseControl(); + } + ]); + + test.takeControl(); +}; + + +/** + * Gets a XPathResult matching given xpath. + * @param {string} xpath + * @param {number} resultType + * @param {Node} opt_ancestor Context node. If not specified documentElement + * will be used + * @return {XPathResult} Type of returned value is determined by "resultType" parameter + */ + +TestSuite.prototype._evaluateXpath = function(xpath, resultType, opt_ancestor) +{ + if (!opt_ancestor) + opt_ancestor = document.documentElement; + try { + return document.evaluate(xpath, opt_ancestor, null, resultType, null); + } catch(e) { + this.fail('Error in expression: "' + xpath + '".' + e); + } +}; + + +/** + * Gets first Node matching given xpath. + * @param {string} xpath + * @param {Node} opt_ancestor Context node. If not specified documentElement + * will be used + * @return {?Node} + */ +TestSuite.prototype._findNode = function(xpath, opt_ancestor) +{ + var result = this._evaluateXpath(xpath, XPathResult.FIRST_ORDERED_NODE_TYPE, opt_ancestor).singleNodeValue; + this.assertTrue(!!result, "Cannot find node on path: " + xpath); + return result; +}; + + +/** + * Gets a text matching given xpath. + * @param {string} xpath + * @param {Node} opt_ancestor Context node. If not specified documentElement + * will be used + * @return {?string} + */ +TestSuite.prototype._findText = function(xpath, opt_ancestor) +{ + var result = this._evaluateXpath(xpath, XPathResult.STRING_TYPE, opt_ancestor).stringValue; + this.assertTrue(!!result, "Cannot find text on path: " + xpath); + return result; +}; + + +/** + * Gets an iterator over nodes matching given xpath. + * @param {string} xpath + * @param {Node} opt_ancestor Context node. If not specified, documentElement + * will be used + * @return {XPathResult} Iterator over the nodes + */ +TestSuite.prototype._nodeIterator = function(xpath, opt_ancestor) +{ + return this._evaluateXpath(xpath, XPathResult.ORDERED_NODE_ITERATOR_TYPE, opt_ancestor); +}; + + +/** + * Checks the scopeSectionDiv against the expectations. + * @param {Node} scopeSectionDiv The section div + * @param {Object} expectations Expectations dictionary + */ +TestSuite.prototype._checkScopeSectionDiv = function(scopeSectionDiv, expectations) +{ + var scopeTitle = this._findText('./div[@class="header"]/div[@class="title"]/text()', scopeSectionDiv); + this.assertEquals(expectations.title, scopeTitle, "Unexpected scope section title."); + if (!expectations.properties) + return; + this.assertTrue(scopeSectionDiv.hasStyleClass("expanded"), 'Section "' + scopeTitle + '" is collapsed.'); + + var propertyIt = this._nodeIterator("./ol/li", scopeSectionDiv); + var propertyLi; + var foundProps = []; + while (propertyLi = propertyIt.iterateNext()) { + var name = this._findText('./span[@class="name"]/text()', propertyLi); + var value = this._findText('./span[@class="value"]/text()', propertyLi); + this.assertTrue(!!name, 'Invalid variable name: "' + name + '"'); + this.assertTrue(name in expectations.properties, "Unexpected property: " + name); + this.assertEquals(expectations.properties[name], value, 'Unexpected "' + name + '" property value.'); + delete expectations.properties[name]; + foundProps.push(name + " = " + value); + } + + // Check that all expected properties were found. + for (var p in expectations.properties) + this.fail('Property "' + p + '" was not found in scope "' + scopeTitle + '". Found properties: "' + foundProps.join(",") + '"'); +}; + + +/** + * Expands scope sections matching the filter and invokes the callback on + * success. + * @param {function(WebInspector.ObjectPropertiesSection, number):boolean} + * filter + * @param {Function} callback + */ +TestSuite.prototype._expandScopeSections = function(filter, callback) +{ + var sections = WebInspector.currentPanel.sidebarPanes.scopechain.sections; + + var toBeUpdatedCount = 0; + function updateListener() { + --toBeUpdatedCount; + if (toBeUpdatedCount === 0) { + // Report when all scopes are expanded and populated. + callback(); + } + } + + // Global scope is always the last one. + for (var i = 0; i < sections.length - 1; i++) { + var section = sections[i]; + if (!filter(sections, i)) + continue; + ++toBeUpdatedCount; + var populated = section.populated; + + this._hookGetPropertiesCallback(updateListener, + function() { + section.expand(); + if (populated) { + // Make sure "updateProperties" callback will be called at least once + // after it was overridden. + section.update(); + } + }); + } +}; + + +/** + * Tests that scopes can be expanded and contain expected data. + */ +TestSuite.prototype.testExpandScope = function() +{ + this.showPanel("scripts"); + var test = this; + + this._executeCodeWhenScriptsAreParsed("handleClick()", ["debugger_closure.html$"]); + + this._waitForScriptPause( + { + functionsOnStack: ["innerFunction", "handleClick", "(anonymous function)"], + lineNumber: 8, + lineText: " debugger;" + }, + expandAllSectionsExceptGlobal); + + // Expanding Global scope takes for too long so we skeep it. + function expandAllSectionsExceptGlobal() { + test._expandScopeSections(function(sections, i) { + return i < sections.length - 1; + }, + examineScopes /* When all scopes are expanded and populated check them. */); + } + + // Check scope sections contents. + function examineScopes() { + var scopeVariablesSection = test._findNode('//div[@id="scripts-sidebar"]/div[div[@class="title"]/text()="Scope Variables"]'); + var expectedScopes = [ + { + title: "Local", + properties: { + x:"2009", + innerFunctionLocalVar:"2011", + "this": "global", + } + }, + { + title: "Closure", + properties: { + n:"TextParam", + makeClosureLocalVar:"local.TextParam", + } + }, + { + title: "Global", + }, + ]; + var it = test._nodeIterator('./div[@class="body"]/div', scopeVariablesSection); + var scopeIndex = 0; + var scopeDiv; + while (scopeDiv = it.iterateNext()) { + test.assertTrue(scopeIndex < expectedScopes.length, "Too many scopes."); + test._checkScopeSectionDiv(scopeDiv, expectedScopes[scopeIndex]); + ++scopeIndex; + } + test.assertEquals(expectedScopes.length, scopeIndex, "Unexpected number of scopes."); + + test.releaseControl(); + } + + test.takeControl(); +}; + + +/** + * Returns child tree element for a property with given name. + * @param {TreeElement} parent Parent tree element. + * @param {string} childName + * @param {string} objectPath Path to the object. Will be printed in the case + * of failure. + * @return {TreeElement} + */ +TestSuite.prototype._findChildProperty = function(parent, childName, objectPath) +{ + var children = parent.children; + for (var i = 0; i < children.length; i++) { + var treeElement = children[i]; + var property = treeElement.property; + if (property.name === childName) + return treeElement; + } + this.fail('Cannot find property "' + childName + '" in ' + objectPath); +}; + + +/** + * Executes the 'code' with InjectedScriptAccess.getProperties overriden + * so that all callbacks passed to InjectedScriptAccess.getProperties are + * extended with the "hook". + * @param {Function} hook The hook function. + * @param {Function} code A code snippet to be executed. + */ +TestSuite.prototype._hookGetPropertiesCallback = function(hook, code) +{ + var accessor = InjectedScriptAccess.prototype; + var orig = accessor.getProperties; + accessor.getProperties = function(objectProxy, ignoreHasOwnProperty, abbreviate, callback) { + orig.call(this, objectProxy, ignoreHasOwnProperty, abbreviate, + function() { + callback.apply(this, arguments); + hook(); + }); + }; + try { + code(); + } finally { + accessor.getProperties = orig; + } +}; + + +/** + * Tests that all elements in prototype chain of an object have expected + * intrinic proprties(__proto__, constructor, prototype). + */ +TestSuite.prototype.testDebugIntrinsicProperties = function() +{ + this.showPanel("scripts"); + var test = this; + + this._executeCodeWhenScriptsAreParsed("handleClick()", ["debugger_intrinsic_properties.html$"]); + + this._waitForScriptPause( + { + functionsOnStack: ["callDebugger", "handleClick", "(anonymous function)"], + lineNumber: 29, + lineText: " debugger;" + }, + expandLocalScope); + + var localScopeSection = null; + function expandLocalScope() { + test._expandScopeSections(function(sections, i) { + if (i === 0) { + test.assertTrue(sections[i].object.isLocal, "Scope #0 is not Local."); + localScopeSection = sections[i]; + return true; + } + return false; + }, + examineLocalScope); + } + + function examineLocalScope() { + var scopeExpectations = [ + "a", "Object", [ + "constructor", "function Child()", [ + "constructor", "function Function()", null, + "name", "Child", null, + "prototype", "Object", [ + "childProtoField", 21, null + ] + ], + + "__proto__", "Object", [ + "__proto__", "Object", [ + "__proto__", "Object", [ + "__proto__", "null", null, + "constructor", "function Object()", null, + ], + "constructor", "function Parent()", [ + "name", "Parent", null, + "prototype", "Object", [ + "parentProtoField", 11, null, + ] + ], + "parentProtoField", 11, null, + ], + "constructor", "function Child()", null, + "childProtoField", 21, null, + ], + + "parentField", 10, null, + "childField", 20, null, + ] + ]; + + checkProperty(localScopeSection.propertiesTreeOutline, "<Local Scope>", scopeExpectations); + } + + var propQueue = []; + var index = 0; + var expectedFinalIndex = 8; + + function expandAndCheckNextProperty() { + if (index === propQueue.length) { + test.assertEquals(expectedFinalIndex, index, "Unexpected number of expanded objects."); + test.releaseControl(); + return; + } + + // Read next property data from the queue. + var treeElement = propQueue[index].treeElement; + var path = propQueue[index].path; + var expectations = propQueue[index].expectations; + index++; + + // Expand the property. + test._hookGetPropertiesCallback(function() { + checkProperty(treeElement, path, expectations); + }, + function() { + treeElement.expand(); + }); + } + + function checkProperty(treeElement, path, expectations) { + for (var i = 0; i < expectations.length; i += 3) { + var name = expectations[i]; + var description = expectations[i+1]; + var value = expectations[i+2]; + + var propertyPath = path + "." + name; + var propertyTreeElement = test._findChildProperty(treeElement, name, path); + test.assertTrue(propertyTreeElement, 'Property "' + propertyPath + '" not found.'); + test.assertEquals(description, propertyTreeElement.property.value.description, 'Unexpected "' + propertyPath + '" description.'); + if (value) { + // Schedule property content check. + propQueue.push({ + treeElement: propertyTreeElement, + path: propertyPath, + expectations: value, + }); + } + } + // Check next property in the queue. + expandAndCheckNextProperty(); + } + + test.takeControl(); +}; + + +/** + * Tests "Pause" button will pause debugger when a snippet is evaluated. + */ +TestSuite.prototype.testPauseInEval = function() +{ + this.showPanel("scripts"); + + var test = this; + + var pauseButton = document.getElementById("scripts-pause"); + pauseButton.click(); + + devtools.tools.evaluateJavaScript("fib(10)"); + + this.addSniffer(WebInspector, "pausedScript", + function() { + test.releaseControl(); + }); + + test.takeControl(); +}; + + +/** + * Key event with given key identifier. + */ +TestSuite.createKeyEvent = function(keyIdentifier) +{ + var evt = document.createEvent("KeyboardEvent"); + evt.initKeyboardEvent("keydown", true /* can bubble */, true /* can cancel */, null /* view */, keyIdentifier, ""); + return evt; +}; + + +/** + * Tests console eval. + */ +TestSuite.prototype.testConsoleEval = function() +{ + var test = this; + this.evaluateInConsole_("123", + function(resultText) { + test.assertEquals("123", resultText); + test.releaseControl(); + }); + + this.takeControl(); +}; + + +/** + * Tests console log. + */ +TestSuite.prototype.testConsoleLog = function() +{ + WebInspector.console.visible = true; + var messages = WebInspector.console.messages; + var index = 0; + + var test = this; + var assertNext = function(line, message, opt_class, opt_count, opt_substr) { + var elem = messages[index++].toMessageElement(); + var clazz = elem.getAttribute("class"); + var expectation = (opt_count || '') + 'console_test_page.html:' + line + message; + if (opt_substr) + test.assertContains(elem.textContent, expectation); + else + test.assertEquals(expectation, elem.textContent); + if (opt_class) + test.assertContains(clazz, "console-" + opt_class); + }; + + assertNext("5", "log", "log-level"); + assertNext("7", "debug", "log-level"); + assertNext("9", "info", "log-level"); + assertNext("11", "warn", "warning-level"); + assertNext("13", "error", "error-level"); + assertNext("15", "Message format number 1, 2 and 3.5"); + assertNext("17", "Message format for string"); + assertNext("19", "Object Object"); + assertNext("22", "repeated", "log-level", 5); + assertNext("26", "count: 1"); + assertNext("26", "count: 2"); + assertNext("29", "group", "group-title"); + index++; + assertNext("33", "timer:", "log-level", "", true); + assertNext("35", "1 2 3", "log-level"); + assertNext("37", "HTMLDocument", "log-level"); + assertNext("39", "<html>", "log-level", "", true); +}; + + +/** + * Tests eval of global objects. + */ +TestSuite.prototype.testEvalGlobal = function() +{ + WebInspector.console.visible = true; + + var inputs = ["foo", "foobar"]; + var expectations = ["foo", "fooValue", "foobar", "ReferenceError: foobar is not defined"]; + + // Do not change code below - simply add inputs and expectations above. + var initEval = function(input) { + WebInspector.console.prompt.text = input; + WebInspector.console.promptElement.dispatchEvent( TestSuite.createKeyEvent("Enter")); + }; + var test = this; + var messagesCount = 0; + var inputIndex = 0; + this.addSniffer(WebInspector.ConsoleView.prototype, "addMessage", + function(commandResult) { + messagesCount++; + if (messagesCount === expectations.length) { + var messages = WebInspector.console.messages; + for (var i = 0; i < expectations; ++i) { + var elem = messages[i++].toMessageElement(); + test.assertEquals(elem.textContent, expectations[i]); + } + test.releaseControl(); + } else if (messagesCount % 2 === 0) + initEval(inputs[inputIndex++]); + }, true); + + initEval(inputs[inputIndex++]); + this.takeControl(); +}; + + +/** + * Tests that Storage panel can be open and that local DOM storage is added + * to the panel. + */ +TestSuite.prototype.testShowStoragePanel = function() +{ + var test = this; + this.addSniffer(WebInspector.panels.storage, "addDOMStorage", + function(storage) { + var orig = storage.getEntries; + storage.getEntries = function(callback) { + orig.call(this, function(entries) { + callback(entries); + test.releaseControl(); + }); + }; + try { + WebInspector.currentPanel.selectDOMStorage(storage.id); + storage.getEntries = orig; + } catch (e) { + test.fail("Exception in selectDOMStorage: " + e); + } + }); + this.showPanel("storage"); + + // Access localStorage so that it's pushed to the frontend. + this.evaluateInConsole_( + 'setTimeout("localStorage.x = 10" , 0)', + function(resultText) { + test.assertTrue(!isNaN(resultText), "Failed to get timer id: " + resultText); + }); + + // Wait until DOM storage is added to the panel. + this.takeControl(); +}; + + +/** + * Test runner for the test suite. + */ +var uiTests = {}; + + +/** + * Run each test from the test suit on a fresh instance of the suite. + */ +uiTests.runAllTests = function() +{ + // For debugging purposes. + for (var name in TestSuite.prototype) { + if (name.substring(0, 4) === "test" && typeof TestSuite.prototype[name] === "function") + uiTests.runTest(name); + } +}; + + +/** + * Run specified test on a fresh instance of the test suite. + * @param {string} name Name of a test method from TestSuite class. + */ +uiTests.runTest = function(name) +{ + new TestSuite().runTest(name); +}; + + +} diff --git a/WebKit/chromium/src/js/devTools.css b/WebKit/chromium/src/js/devTools.css new file mode 100755 index 0000000..1fa935f --- /dev/null +++ b/WebKit/chromium/src/js/devTools.css @@ -0,0 +1,223 @@ +#scripts-files option.injected { + color: rgb(70, 134, 240); +} + +.data-grid table { + line-height: 120%; +} + +body.attached #toolbar { + height: 34px; + border-top: 1px solid rgb(100, 100, 100); + cursor: default; /* overriden */ + padding-left: 0; +} + + +/* Chrome theme overrides */ +body.platform-windows #toolbar { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(242, 247, 253)), to(rgb(223, 234, 248))); +} + +body.platform-windows.inactive #toolbar { + background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(248, 248, 248)), to(rgb(237, 237, 237))); +} + + +/* Heap Profiler Styles */ + +.heap-snapshot-status-bar-item .glyph { + -webkit-mask-image: url(Images/focusButtonGlyph.png); +} + +.heap-snapshot-sidebar-tree-item .icon { + content: url(Images/profileIcon.png); +} + +.heap-snapshot-sidebar-tree-item.small .icon { + content: url(Images/profileSmallIcon.png); +} + +.heap-snapshot-view { + display: none; + overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.heap-snapshot-view.visible { + display: block; +} + +.heap-snapshot-view .data-grid { + border: none; + max-height: 100%; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 93px; +} + +.heap-snapshot-view .data-grid th.count-column { + text-align: center; +} + +.heap-snapshot-view .data-grid td.count-column { + text-align: right; +} + +.heap-snapshot-view .data-grid th.size-column { + text-align: center; +} + +.heap-snapshot-view .data-grid td.size-column { + text-align: right; +} + +.heap-snapshot-view .data-grid th.countDelta-column { + text-align: center; +} + +.heap-snapshot-view .data-grid td.countDelta-column { + text-align: right; +} + +.heap-snapshot-view .data-grid th.sizeDelta-column { + text-align: center; +} + +.heap-snapshot-view .data-grid td.sizeDelta-column { + text-align: right; +} + +#heap-snapshot-summary-container { + position: absolute; + padding-top: 20px; + bottom: 0; + left: 0; + right: 0; + height: 93px; + margin-left: -1px; + border-left: 1px solid rgb(102, 102, 102); + background-color: rgb(101, 111, 130); + background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0))); + background-repeat: repeat-x; + background-position: top; + text-align: center; + text-shadow: black 0 1px 1px; + white-space: nowrap; + color: white; + -webkit-background-size: 1px 6px; + -webkit-background-origin: padding; + -webkit-background-clip: padding; +} + +.heap-snapshot-summary { + display: inline-block; + width: 50%; + min-width: 300px; + position: relative; +} + +.heap-snapshot-summary canvas.summary-graph { + width: 225px; +} + +.heap-snapshot-summary-label { + font-size: 12px; + font-weight: bold; + position: absolute; + top: 1px; + width: 50%; + left: 25%; +} + +body.platform-windows .section > .header { + border: 1px solid rgb(92, 116, 157); + background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(105, 133, 180)), to(rgb(92, 116, 157))); +} + +body.platform-windows .console-group-messages .section > .header { + padding: 0 8px 0 0; + background-image: none; + border: none; + min-height: 0; +} + +body.platform-windows #resources-filter { + background: -webkit-gradient(linear, left top, left bottom, from(rgb(233, 233, 233)), to(rgb(233, 233, 233))); +} + +body.platform-windows .crumbs .crumb { + -webkit-border-image: url(Images/segmentChromium.png) 0 12 0 2; +} + +body.platform-windows .crumbs .crumb.end { + -webkit-border-image: url(Images/segmentEndChromium.png) 0 2 0 2; +} + +body.platform-windows .crumbs .crumb.selected { + -webkit-border-image: url(Images/segmentSelectedChromium.png) 0 12 0 2; + color: white; + text-shadow: rgba(255, 255, 255, 0.5) 0 0px 0; +} + +body.platform-windows .crumbs .crumb.selected:hover { + -webkit-border-image: url(Images/segmentSelectedChromium.png) 0 12 0 2; +} + +body.platform-windows .crumbs .crumb.selected.end, .crumbs .crumb.selected.end:hover { + -webkit-border-image: url(Images/segmentSelectedEndChromium.png) 0 2 0 2; +} + +body.platform-windows .crumbs .crumb:hover { + -webkit-border-image: url(Images/segmentHoverChromium.png) 0 12 0 2; +} + +body.platform-windows .crumbs .crumb.dimmed:hover { + -webkit-border-image: url(Images/segmentHoverChromium.png) 0 12 0 2; +} + +body.platform-windows .crumbs .crumb.end:hover { + -webkit-border-image: url(Images/segmentHoverEndChromium.png) 0 2 0 2; +} + +body.platform-windows body.drawer-visible #main-status-bar { + background-image: url(Images/statusbarResizerVertical.png), url(Images/statusbarBackgroundChromium.png); +} + +body.platform-windows .status-bar { + background-image: url(Images/statusbarBackgroundChromium.png); +} + +body.platform-windows button.status-bar-item { + background-image: url(Images/statusbarButtonsChromium.png); +} + +body.platform-windows select.status-bar-item:active { + -webkit-border-image: url(Images/statusbarMenuButtonSelectedChromium.png) 0 17 0 2; +} + +body.platform-windows #drawer { + background-image: url(Images/statusbarBottomBackgroundChromium.png); +} + +body.platform-windows select.status-bar-item { + -webkit-border-image: url(Images/statusbarMenuButtonChromium.png) 0 17 0 2; +} + +.scope-bar li.selected { + -webkit-box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.0); +} + +.scope-bar li:active { + -webkit-box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.0); +} + +.timeline-category-tree-item input { + vertical-align: middle; +} diff --git a/WebKit/chromium/src/linux/WebRenderTheme.cpp b/WebKit/chromium/src/linux/WebRenderTheme.cpp new file mode 100644 index 0000000..16ea22c --- /dev/null +++ b/WebKit/chromium/src/linux/WebRenderTheme.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Joel Stanley. 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 "WebRenderTheme.h" + +#include "RenderThemeChromiumLinux.h" +#include "WebView.h" + +using WebCore::RenderTheme; +using WebCore::RenderThemeChromiumLinux; + +namespace WebKit { + +void setCaretBlinkInterval(double interval) +{ + RenderThemeChromiumLinux::setCaretBlinkInterval(interval); +} + +} // namespace WebKit diff --git a/WebKit/chromium/src/mac/WebInputEventFactory.mm b/WebKit/chromium/src/mac/WebInputEventFactory.mm index d618228..46b0afe 100644 --- a/WebKit/chromium/src/mac/WebInputEventFactory.mm +++ b/WebKit/chromium/src/mac/WebInputEventFactory.mm @@ -92,6 +92,9 @@ static bool isKeypadEvent(NSEvent* event) return false; } + if ([event modifierFlags] & NSNumericPadKeyMask) + return true; + switch ([event keyCode]) { case 71: // Clear case 81: // = |
