diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-27 00:20:51 +0000 |
commit | f5b16fed647e941aa66933178da85db2860d639b (patch) | |
tree | f00e9856c04aad3b558a140955e7674add33f051 /webkit/glue | |
parent | 920c091ac3ee15079194c82ae8a7a18215f3f23c (diff) | |
download | chromium_src-f5b16fed647e941aa66933178da85db2860d639b.zip chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.gz chromium_src-f5b16fed647e941aa66933178da85db2860d639b.tar.bz2 |
Add webkit to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/glue')
235 files changed, 39381 insertions, 0 deletions
diff --git a/webkit/glue/SConscript b/webkit/glue/SConscript new file mode 100644 index 0000000..15f0557 --- /dev/null +++ b/webkit/glue/SConscript @@ -0,0 +1,108 @@ +# Copyright 2008, 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.
+
+Import('env')
+
+env = env.Clone()
+
+env.Append(
+ CCFLAGS = [
+ '/TP',
+
+ '/WX',
+
+ '/wd4800',
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+input_files = [
+ 'alt_404_page_resource_fetcher.cc',
+ 'autocomplete_input_listener.cc',
+ 'plugins/webplugin_delegate_impl.cc',
+ 'plugins/plugin_string_stream.cc',
+ 'plugins/plugin_stream_url.cc',
+ 'plugins/plugin_stream.cc',
+ 'plugins/plugin_list.cc',
+ 'plugins/plugin_lib.cc',
+ 'plugins/plugin_instance.cc',
+ 'plugins/plugin_host.cc',
+ 'plugins/plugin_data_stream.cc',
+ 'plugins/mozilla_extensions.cc',
+ 'webwidget_impl.cc',
+ 'webview_impl.cc',
+ 'weburlrequest_impl.cc',
+ 'webtextinput_impl.cc',
+ 'webplugin_impl.cc',
+ 'webkit_glue.cc',
+ 'webinputevent.cc',
+ 'webhistoryitem_impl.cc',
+ 'webframeloaderclient_impl.cc',
+ 'webframe_impl.cc',
+ 'weberror_impl.cc',
+ 'webdropdata.cc',
+ 'webdocumentloader_impl.cc',
+ 'webdatasource_impl.cc',
+ 'webcursor.cc',
+ 'simple_clipboard_impl.cc',
+ 'searchable_form_data.cc',
+ 'resource_handle_win.cc',
+ 'resource_fetcher.cc',
+ 'password_form_dom_manager.cc',
+ 'password_autocomplete_listener.cc',
+ 'multipart_response_delegate.cc',
+ 'localized_strings.cc',
+ 'inspector_client_impl.cc',
+ 'glue_util.cc',
+ 'glue_serialize.cc',
+ 'feed_preview.cc',
+ 'image_resource_fetcher.cc',
+ 'image_decoder.cc',
+ 'event_conversion.cc',
+ 'editor_client_impl.cc',
+ 'entity_map.cc',
+ 'dragclient_impl.cc',
+ 'dom_operations.cc',
+ 'dom_serializer.cc',
+ 'debugger.cc',
+ 'cpp_variant.cc',
+ 'cpp_bound_class.cc',
+ 'cpp_binding_example.cc',
+ 'context_menu_client_impl.cc',
+ 'chrome_client_impl.cc',
+ 'cache_manager.cc',
+ 'alt_error_page_resource_fetcher.cc',
+ '$PENDING_DIR/AccessibleBase.cpp',
+ '$PENDING_DIR/AccessibleDocument.cpp',
+]
+
+env.StaticLibrary('Glue', input_files)
+
+
diff --git a/webkit/glue/alt_404_page_resource_fetcher.cc b/webkit/glue/alt_404_page_resource_fetcher.cc new file mode 100644 index 0000000..5b20187 --- /dev/null +++ b/webkit/glue/alt_404_page_resource_fetcher.cc @@ -0,0 +1,70 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "DocumentLoader.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/alt_404_page_resource_fetcher.h" + +#include "googleurl/src/gurl.h" +#include "webkit/glue/webframeloaderclient_impl.h" + +using WebCore::DocumentLoader; + +// Number of seconds to wait for the alternate 404 page server. If it takes +// too long, just show the original 404 page. +static const double kDownloadTimeoutSec = 3.0; + +Alt404PageResourceFetcher::Alt404PageResourceFetcher( + WebFrameLoaderClient* webframeloaderclient, + WebCore::Frame* frame, + DocumentLoader* doc_loader, + const GURL& url) + : webframeloaderclient_(webframeloaderclient), + doc_loader_(doc_loader) { + + fetcher_.reset(new ResourceFetcherWithTimeout(url, frame, + kDownloadTimeoutSec, this)); +} + +void Alt404PageResourceFetcher::OnURLFetchComplete( + const WebCore::ResourceResponse& response, + const std::string& data) { + if (response.httpStatusCode() == 200) { + // Only show server response if we got a 200. + webframeloaderclient_->Alt404PageFinished(doc_loader_.get(), data); + } else { + webframeloaderclient_->Alt404PageFinished(doc_loader_.get(), std::string()); + } + doc_loader_ = NULL; +} diff --git a/webkit/glue/alt_404_page_resource_fetcher.h b/webkit/glue/alt_404_page_resource_fetcher.h new file mode 100644 index 0000000..a30b294 --- /dev/null +++ b/webkit/glue/alt_404_page_resource_fetcher.h @@ -0,0 +1,79 @@ +// Copyright 2008, 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 WEBKIT_GLUE_ALT_404_PAGE_RESOURCE_HANDLE_CLIENT_H__ +#define WEBKIT_GLUE_ALT_404_PAGE_RESOURCE_HANDLE_CLIENT_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" + +#include "webkit/glue/resource_fetcher.h" + +class WebFrameLoaderClient; +class WebCore::DocumentLoader; +class WebCore::Frame; +class WebCore::ResourceResponse; + +// ResourceHandleClient implementation that is used for downloading alternate +// 404 pages. Once downloading is done (or fails), the WebFrameLoaderClient is +// notified. +class Alt404PageResourceFetcher : public ResourceFetcher::Delegate { + public: + Alt404PageResourceFetcher(WebFrameLoaderClient* webframeloaderclient, + WebCore::Frame* frame, + WebCore::DocumentLoader* doc_loader, + const GURL& url); + + virtual void OnURLFetchComplete(const WebCore::ResourceResponse& response, + const std::string& data); + + // Stop any pending loads. + void Cancel() { + if (fetcher_.get()) + fetcher_->Cancel(); + } + + private: + // Does the actual fetching. + scoped_ptr<ResourceFetcherWithTimeout> fetcher_; + + // References to our owner which we call when finished. + WebFrameLoaderClient* webframeloaderclient_; + + // The DocumentLoader associated with this load. If there's an error + // talking with the alt 404 page server, we need this to complete the + // original load. + RefPtr<WebCore::DocumentLoader> doc_loader_; + + DISALLOW_EVIL_CONSTRUCTORS(Alt404PageResourceFetcher); +}; + +#endif // WEBKIT_GLUE_ALT_404_PAGE_RESOURCE_HANDLE_CLIENT_H__ diff --git a/webkit/glue/alt_error_page_resource_fetcher.cc b/webkit/glue/alt_error_page_resource_fetcher.cc new file mode 100644 index 0000000..b7b6c32 --- /dev/null +++ b/webkit/glue/alt_error_page_resource_fetcher.cc @@ -0,0 +1,78 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "ResourceResponse.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/alt_error_page_resource_fetcher.h" + +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webview_delegate.h" +#include "webkit/glue/webview.h" + +// Number of seconds to wait for the alternate error page server. If it takes +// too long, just use the local error page. +static const double kDownloadTimeoutSec = 3.0; + +AltErrorPageResourceFetcher::AltErrorPageResourceFetcher( + WebView* web_view, + const WebErrorImpl& web_error, + WebFrameImpl* web_frame, + const GURL& url) + : web_view_(web_view), + web_error_(web_error), + web_frame_(web_frame) { + failed_request_.reset(web_frame_->GetProvisionalDataSource()-> + GetRequest().Clone()); + fetcher_.reset(new ResourceFetcherWithTimeout(url, web_frame->frame(), + kDownloadTimeoutSec, this)); +} + +void AltErrorPageResourceFetcher::OnURLFetchComplete( + const WebCore::ResourceResponse& response, + const std::string& data) { + WebViewDelegate* delegate = web_view_->GetDelegate(); + if (!delegate) + return; + + if (response.httpStatusCode() == 200) { + // We successfully got a response from the alternate error page server, so + // load it. + delegate->LoadNavigationErrorPage(web_frame_, failed_request_.get(), + web_error_, data, true); + } else { + delegate->LoadNavigationErrorPage(web_frame_, failed_request_.get(), + web_error_, std::string(), true); + } +} diff --git a/webkit/glue/alt_error_page_resource_fetcher.h b/webkit/glue/alt_error_page_resource_fetcher.h new file mode 100644 index 0000000..093f22b --- /dev/null +++ b/webkit/glue/alt_error_page_resource_fetcher.h @@ -0,0 +1,74 @@ +// Copyright 2008, 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 WEBKIT_GLUE_ALT_ERROR_PAGE_RESOURCE_FETCHER_H__ +#define WEBKIT_GLUE_ALT_ERROR_PAGE_RESOURCE_FETCHER_H__ + +#include <string> + +#pragma warning(push, 0) +#include "Timer.h" +#pragma warning(pop) + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/resource_fetcher.h" +#include "webkit/glue/weberror_impl.h" +#include "webkit/glue/weburlrequest.h" + +class WebCore::ResourceResponse; +class WebFrameImpl; +class WebView; + +// Used for downloading alternate dns error pages. Once downloading is done +// (or fails), the webview delegate is notified. +class AltErrorPageResourceFetcher : public ResourceFetcher::Delegate { + public: + AltErrorPageResourceFetcher(WebView* web_view, + const WebErrorImpl& web_error, + WebFrameImpl* web_frame, + const GURL& url); + + virtual void OnURLFetchComplete(const WebCore::ResourceResponse& response, + const std::string& data); + + private: + // References to our owners + WebView* web_view_; + WebErrorImpl web_error_; + WebFrameImpl* web_frame_; + scoped_ptr<WebRequest> failed_request_; + + // Does the actual fetching. + scoped_ptr<ResourceFetcherWithTimeout> fetcher_; + + DISALLOW_EVIL_CONSTRUCTORS(AltErrorPageResourceFetcher); +}; + +#endif // WEBKIT_GLUE_ALT_ERROR_PAGE_RESOURCE_FETCHER_H__ diff --git a/webkit/glue/autocomplete_input_listener.cc b/webkit/glue/autocomplete_input_listener.cc new file mode 100644 index 0000000..508a461 --- /dev/null +++ b/webkit/glue/autocomplete_input_listener.cc @@ -0,0 +1,222 @@ +// Copyright 2008, 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. +// +// This file provides an abstract implementation of the inline autocomplete +// infrastructure defined in autocomplete_input_listener.h. + +#include "webkit/glue/autocomplete_input_listener.h" + +#pragma warning(push, 0) +#include "HTMLInputElement.h" +#include "HTMLFormElement.h" +#include "Document.h" +#include "Frame.h" +#include "Editor.h" +#include "EventNames.h" +#include "Event.h" +#pragma warning(pop) + +#undef LOG + +#include "base/logging.h" +#include "webkit/glue/editor_client_impl.h" +#include "webkit/glue/glue_util.h" + +namespace webkit_glue { + +// Hack (1 of 2) for http://bugs.webkit.org/show_bug.cgi?id=16976. This bug +// causes the caret position to be set after handling input events, which +// trumps our modifications, so for now we tell the EditorClient to preserve +// whatever selection set by our code. +// TODO(timsteele): Remove this function altogether once bug is fixed. +static void PreserveSelection(WebCore::HTMLInputElement* element) { + WebCore::EditorClient* ec = + element->form()->document()->frame()->editor()->client(); + EditorClientImpl* client = static_cast<EditorClientImpl*>(ec); + client->PreserveSelection(); +} + +HTMLInputDelegate::HTMLInputDelegate(WebCore::HTMLInputElement* element) + : element_(element) { + // Reference the element for the lifetime of this delegate. + // e is NULL when testing. + if (element_) + element_->ref(); +} + +HTMLInputDelegate::~HTMLInputDelegate() { + if (element_) + element_->deref(); +} + +bool HTMLInputDelegate::IsCaretAtEndOfText(size_t input_length, + size_t previous_length) const { + // Hack 2 of 2 for http://bugs.webkit.org/show_bug.cgi?id=16976. + // TODO(timsteele): This check should only return early if + // !(selectionEnd == selectionStart == user_input.length()). + // However, because of webkit bug #16976 the caret is not properly moved + // until after the handlers have executed, so for now we do the following + // several checks. The first check handles the case webkit sets the End + // selection but not the Start selection correctly, and the second is for + // when webcore sets neither. This won't be perfect if the user moves the + // selection around during inline autocomplete, but for now its the + // friendliest behavior we can offer. Once the bug is fixed this method + // should no longer need the previous_length parameter. + if (((element_->selectionEnd() != element_->selectionStart() + 1) || + (element_->selectionEnd() != input_length)) && + ((element_->selectionEnd() != element_->selectionStart()) || + (element_->selectionEnd() != previous_length))) { + return false; + } + return true; +} + +void HTMLInputDelegate::SetValue(const std::wstring& value) { + element_->setValue(StdWStringToString(value)); +} + +std::wstring HTMLInputDelegate::GetValue() const { + return StringToStdWString(element_->value()); +} + +void HTMLInputDelegate::SetSelectionRange(size_t start, size_t end) { + element_->setSelectionRange(start, end); + // Hack, see comments for PreserveSelection(). + PreserveSelection(element_); +} + +void HTMLInputDelegate::OnFinishedAutocompleting() { + // This sets the input element to an autofilled state which will result in it + // having a yellow background. + element_->setAutofilled(true); + // Notify any changeEvent listeners. + element_->onChange(); +} + +AutocompleteInputListener::AutocompleteInputListener( + AutocompleteEditDelegate* edit_delegate) + : edit_delegate_(edit_delegate) { + previous_text_ = edit_delegate->GetValue(); +} +// The following method is based on Firefox2 code in +// toolkit/components/autocomplete/src/nsAutoCompleteController.cpp +// Its license block is +// + /* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Joe Hewitt <hewitt@netscape.com> (Original Author) + * Dean Tessman <dean_tessman@hotmail.com> + * Johnny Stenback <jst@mozilla.jstenback.com> + * Masayuki Nakano <masayuki@d-toybox.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +bool AutocompleteInputListener::ShouldInlineAutocomplete( + const std::wstring& user_input) { + size_t prev_length = previous_text_.length(); + // The following are a bunch of early returns in cases we don't want to + // go through with inline autocomplete. + + // Don't bother doing AC if nothing changed. + if (user_input.length() > 0 && (user_input == previous_text_)) + return false; + + // Did user backspace? + if ((user_input.length() < previous_text_.length()) && + previous_text_.substr(0, user_input.length()) == user_input) { + previous_text_ = user_input; + return false; + } + + // Remember the current input. + previous_text_ = user_input; + + // Is search string empty? + if (user_input.empty()) + return false; + return edit_delegate_->IsCaretAtEndOfText(user_input.length(), prev_length); +} + +void AutocompleteInputListener::handleEvent(WebCore::Event* event, + bool /*is_window_event*/) { + const WebCore::AtomicString& webcore_type = event->type(); + const std::wstring& user_input = edit_delegate_->GetValue(); + if (webcore_type == WebCore::EventNames::DOMFocusOutEvent) { + OnBlur(user_input); + } else if (webcore_type == WebCore::EventNames::inputEvent) { + // Perform inline autocomplete if it is safe to do so. + if (ShouldInlineAutocomplete(user_input)) + OnInlineAutocompleteNeeded(user_input); + } else { + NOTREACHED() << "unexpected EventName for autocomplete listener"; + } +} + +void AttachForInlineAutocomplete( + WebCore::HTMLInputElement* target, + AutocompleteInputListener* listener) { + target->addEventListener(WebCore::EventNames::DOMFocusOutEvent, + listener, + false); + target->addEventListener(WebCore::EventNames::inputEvent, + listener, + false); +} + +} // webkit_glue diff --git a/webkit/glue/autocomplete_input_listener.h b/webkit/glue/autocomplete_input_listener.h new file mode 100644 index 0000000..72c2d16 --- /dev/null +++ b/webkit/glue/autocomplete_input_listener.h @@ -0,0 +1,241 @@ +// Copyright 2008, 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. +// +// This file defines some infrastructure to handle inline autocomplete of DOM +// input elements. + +#ifndef WEBKIT_GLUE_AUTOCOMPLETE_INPUT_LISTENER_H__ +#define WEBKIT_GLUE_AUTOCOMPLETE_INPUT_LISTENER_H__ + +#include <string> +#include "config.h" + +#pragma warning(push, 0) +#include "EventListener.h" +#pragma warning(pop) +#undef LOG + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" + +namespace WebCore { +class AtomicString; +class Event; +class HTMLInputElement; +} + +namespace webkit_glue { + +// This interface exposes all required functionality to perform inline +// autocomplete on an edit field. +class AutocompleteEditDelegate { + public: + // Virtual destructor so it is safe to delete through AutocompleteEditDelegate + // pointers. + virtual ~AutocompleteEditDelegate() { + } + + // Whether or not the caret/selection is at the end of input. + // input_length gives the length of the user-typed input. + // previous_length is the length of the previously typed input. + virtual bool IsCaretAtEndOfText(size_t input_length, + size_t previous_length) const = 0; + + // Set the selected range of text that should be displayed to the user + // to [start,end] (inclusive). + virtual void SetSelectionRange(size_t start, size_t end) = 0; + + // Accessor/Mutator for the text value. + virtual void SetValue(const std::wstring& value) = 0; + virtual std::wstring GetValue() const = 0; + + // Called when processing is finished. + virtual void OnFinishedAutocompleting() = 0; +}; + +// A proxy interface to a WebCore::HTMLInputElement for inline autocomplete. +// This class is NOT used directly by the AutocompleteInputListener but +// is included here since it is likely most listener implementations will +// want to interface with an HTMLInputElement (see PasswordACListener). +// The delegate does not own the WebCore element; it only interfaces it. +class HTMLInputDelegate : public AutocompleteEditDelegate { + public: + explicit HTMLInputDelegate(WebCore::HTMLInputElement* element); + virtual ~HTMLInputDelegate(); + + // AutocompleteEditDelegate implementation. + virtual bool IsCaretAtEndOfText(size_t input_length, + size_t previous_length) const; + virtual void SetValue(const std::wstring& value); + virtual std::wstring GetValue() const; + virtual void SetSelectionRange(size_t start, size_t end); + virtual void OnFinishedAutocompleting(); + + private: + // The underlying DOM element we're wrapping. We reference the + // underlying HTMLInputElement for its lifetime to ensure it does not get + // freed by WebCore while in use by the delegate instance. + WebCore::HTMLInputElement* element_; + + DISALLOW_EVIL_CONSTRUCTORS(HTMLInputDelegate); +}; + +// Reasons for attaching to DOM directly rather than using EditorClient API: +// 1. Since we don't need to stop listening until the DOM node is unloaded, +// it makes sense to use an object owned by the DOM node itself. Attaching +// as a listener gives you this for free (nodes cleanup their listeners +// upon destruction). +// 2. It allows fine-grained control when the popup/down is implemented +// in handling key events / selecting elements. +// +// Note: The element an AutocompleteInputListener is attached to is kept +// separate from the element it explicitly interacts with (via the +// AutocompleteEditDelegate API) so that the listeners are effectively +// decoupled from HTMLInputElement; which is great when it comes time +// for testing. The listeners can be constructed using only the delegates, +// and events can be manually fired to test specific behaviour. +// +class AutocompleteInputListener : public WebCore::EventListener { + public: + // Construct a listener with access to an edit field (i.e an HTMLInputElement) + // through a delegate, so that it can obtain and set values needed for + // autocomplete. See the above Note which explains why the edit_delegate it + // is handed here is not necessarily the node it is attached to as an + // EventListener. This object takes ownership of its edit_delegate. + explicit AutocompleteInputListener(AutocompleteEditDelegate* edit_delegate); + + virtual ~AutocompleteInputListener() { + } + + // EventListener implementation. Code that is common to inline autocomplete, + // such as deciding whether or not it is safe to perform it, is refactored + // into this method and the appropriate delegate method is invoked. + virtual void handleEvent(WebCore::Event* event, bool is_window_event); + + // Subclasses need only implement the following two methods. They could + // be declared protected but are left public to be invoked by testing. + + // OnBlur: DOMFocusOutEvent occured, means one of two things. + // 1. The user removed focus from the text field + // either by tabbing out or clicking; + // 2. The page is being destroyed (e.g user closed the tab) + virtual void OnBlur(const std::wstring& user_input) = 0; + + // This method is called when there was a user-initiated text delta in + // the edit field that now needs some inline autocompletion. + // ShouldInlineAutocomplete gives the precondition for invoking this method. + virtual void OnInlineAutocompleteNeeded(const std::wstring& user_input) = 0; + + protected: + // Access and modify the edit field only via the AutocompleteEditDelegate API. + AutocompleteEditDelegate* edit_delegate() { return edit_delegate_.get(); } + + private: + // Determines, based on current state (previous_text_) and user input, + // whether or not it is a good idea to attempt inline autocomplete. + // + // This method is based on firefox2 code in + // toolkit/components/autocomplete/src/nsAutoCompleteController.cpp + // Its license block is: + /* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Joe Hewitt <hewitt@netscape.com> (Original Author) + * Dean Tessman <dean_tessman@hotmail.com> + * Johnny Stenback <jst@mozilla.jstenback.com> + * Masayuki Nakano <masayuki@d-toybox.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + // The semantics of deciding whether or not the field is in a inline- + // autocomplete-healthy state are summarized: + // 1. The text is not identical to the text on the previous input event. + // 2. This is not the result of a backspace. + // 3. The text is not empty. + // 4. The caret is at the end of the textbox. + // TODO(timsteele): Examine autocomplete_edit.cc in the browser/ code and + // make sure to capture all common exclusion cases here. + bool ShouldInlineAutocomplete(const std::wstring& user_input); + + // For testability, the AutocompleteEditDelegate API is used to decouple + // AutocompleteInputListeners from underlying HTMLInputElements. This + // allows testcases to mock delegates and test the autocomplete code without + // a real underlying DOM. + scoped_ptr<AutocompleteEditDelegate> edit_delegate_; + + // Stores the text across input events during inline autocomplete. + std::wstring previous_text_; + + DISALLOW_EVIL_CONSTRUCTORS(AutocompleteInputListener); +}; + +// Attach the listener as an EventListener to basic events required +// to handle inline autocomplete (and blur events for tab/click-out). +// Attaching to the WebCore element effectively transfers ownership of +// the listener objects. When WebCore is tearing down the document, +// any attached listeners are destroyed. +// See Document::removeAllEventListenersFromAllNodes which is called by +// FrameLoader::stopLoading. Also, there is no need for matching calls to +// removeEventListener because the simplest and most convienient thing to do +// for autocompletion is to stop listening once the element is destroyed. +void AttachForInlineAutocomplete(WebCore::HTMLInputElement* target, + AutocompleteInputListener* listener); + +} // webkit_glue + +#endif // WEBKIT_GLUE_AUTOCOMPLETE_INPUT_LISTENER_H__ diff --git a/webkit/glue/autocomplete_input_listener_unittest.cc b/webkit/glue/autocomplete_input_listener_unittest.cc new file mode 100644 index 0000000..6c699a2 --- /dev/null +++ b/webkit/glue/autocomplete_input_listener_unittest.cc @@ -0,0 +1,200 @@ +// Copyright 2008, 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. +// +// The DomAutocompleteTests in this file are responsible for ensuring the +// abstract dom autocomplete framework is correctly responding to events and +// delegating to appropriate places. This means concrete implementations should +// focus only on testing the code actually written for that implementation and +// those tests should be completely decoupled from WebCore::Event. + +#include <string> + +#include "config.h" +#pragma warning(push, 0) +#include "HTMLInputElement.h" +#include "HTMLFormElement.h" +#include "Document.h" +#include "Frame.h" +#include "Editor.h" +#include "EventNames.h" +#include "Event.h" +#include "EventListener.h" +#pragma warning(pop) + +#undef LOG + +#include "webkit/glue/autocomplete_input_listener.h" +#include "testing/gtest/include/gtest/gtest.h" + +using webkit_glue::AutocompleteInputListener; +using webkit_glue::AutocompleteEditDelegate; + +class TestAutocompleteEditDelegate : public AutocompleteEditDelegate { + public: + TestAutocompleteEditDelegate() : caret_at_end_(false) { + } + + virtual bool IsCaretAtEndOfText(size_t input_length, + size_t prev_length) const { + return caret_at_end_; + } + + void SetCaretAtEnd(bool caret_at_end) { + caret_at_end_ = caret_at_end; + } + + virtual void SetSelectionRange(size_t start, size_t end) { + } + + virtual void SetValue(const std::wstring& value) { + value_ = value; + } + + virtual std::wstring GetValue() const { + return value_; + } + + virtual void OnFinishedAutocompleting() { + } + + void ResetTestState() { + caret_at_end_ = false; + value_.clear(); + } + + private: + bool caret_at_end_; + std::wstring value_; +}; + +class TestAutocompleteInputListener : public AutocompleteInputListener { + public: + TestAutocompleteInputListener(AutocompleteEditDelegate* d) + : blurred_(false), + did_request_inline_autocomplete_(false), + AutocompleteInputListener(d) { + } + + void ResetTestState() { + blurred_ = false; + did_request_inline_autocomplete_ = false; + } + + bool blurred() const { return blurred_; } + bool did_request_inline_autocomplete() const { + return did_request_inline_autocomplete_; + } + + virtual void OnBlur(const std::wstring& user_input) { + blurred_ = true; + } + virtual void OnInlineAutocompleteNeeded(const std::wstring& user_input) { + did_request_inline_autocomplete_ = true; + } + + private: + bool blurred_; + bool did_request_inline_autocomplete_; +}; + +namespace { +class DomAutocompleteTests : public testing::Test { + public: + void SetUp() { + WebCore::EventNames::init(); + } + + void FireAndHandleInputEvent(AutocompleteInputListener* listener) { + WebCore::Event event(WebCore::EventNames::inputEvent, false, false); + listener->handleEvent(&event, false); + } + + void SimulateTypedInput(TestAutocompleteEditDelegate* delegate, + AutocompleteInputListener* listener, + const std::wstring& new_input) { + delegate->SetValue(new_input); + delegate->SetCaretAtEnd(true); + FireAndHandleInputEvent(listener); + } +}; +} // namespace + +TEST_F(DomAutocompleteTests, OnBlur) { + // Simulate a blur event and ensure it is properly dispatched. + // Listener takes ownership of its delegate. + TestAutocompleteInputListener listener(new TestAutocompleteEditDelegate()); + WebCore::Event event(WebCore::EventNames::DOMFocusOutEvent, false, false); + listener.handleEvent(&event, false); + EXPECT_TRUE(listener.blurred()); +} + +TEST_F(DomAutocompleteTests, InlineAutocompleteTriggeredByInputEvent) { + // Set up the edit delegate, assuming the field was initially empty. + TestAutocompleteEditDelegate* delegate = new TestAutocompleteEditDelegate(); + TestAutocompleteInputListener listener(delegate); + + // Simulate an inputEvent by setting the value and artificially firing evt. + // The user typed 'g'. + SimulateTypedInput(delegate, &listener, L"g"); + EXPECT_TRUE(listener.did_request_inline_autocomplete()); +} + +TEST_F(DomAutocompleteTests, InlineAutocompleteHeuristics) { + TestAutocompleteEditDelegate* delegate = new TestAutocompleteEditDelegate(); + TestAutocompleteInputListener listener(delegate); + + // Simulate a user entering some text, and then backspacing to remove + // a character. + SimulateTypedInput(delegate, &listener, L"g"); + EXPECT_TRUE(listener.did_request_inline_autocomplete()); + listener.ResetTestState(); + + SimulateTypedInput(delegate, &listener, L"go"); + EXPECT_TRUE(listener.did_request_inline_autocomplete()); + listener.ResetTestState(); + + SimulateTypedInput(delegate, &listener, L"g"); + EXPECT_FALSE(listener.did_request_inline_autocomplete()); + listener.ResetTestState(); + + // Now simulate the user moving the cursor to a position other than the end, + // and adding text. + delegate->SetCaretAtEnd(false); + delegate->SetValue(L"og"); + FireAndHandleInputEvent(&listener); + EXPECT_FALSE(listener.did_request_inline_autocomplete()); + listener.ResetTestState(); + + // Test that same input doesn't trigger autocomplete. + delegate->SetCaretAtEnd(true); + delegate->SetValue(L"og"); + FireAndHandleInputEvent(&listener); + EXPECT_FALSE(listener.did_request_inline_autocomplete()); + listener.ResetTestState(); +} diff --git a/webkit/glue/bookmarklet_unittest.cc b/webkit/glue/bookmarklet_unittest.cc new file mode 100644 index 0000000..cadff00 --- /dev/null +++ b/webkit/glue/bookmarklet_unittest.cc @@ -0,0 +1,84 @@ +// Copyright 2008, 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 "testing/gtest/include/gtest/gtest.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "webkit/tools/test_shell/test_shell_test.h" + +namespace { + +class BookmarkletTest : public TestShellTest { + public: + virtual void SetUp() { + TestShellTest::SetUp(); + + test_shell_->LoadURL(L"data:text/html,start page"); + test_shell_->WaitTestFinished(); + } +}; + +} + +TEST_F(BookmarkletTest, Redirect) { + test_shell_->LoadURL(L"javascript:location.href='data:text/plain,SUCCESS'"); + test_shell_->WaitTestFinished(); + std::wstring text = test_shell_->GetDocumentText(); + EXPECT_EQ(L"SUCCESS", text); +} + +TEST_F(BookmarkletTest, NonEmptyResult) { + test_shell_->LoadURL(L"javascript:false"); + MessageLoop::current()->Quit(); + MessageLoop::current()->Run(); + std::wstring text = test_shell_->GetDocumentText(); + EXPECT_EQ(L"false", text); + + test_shell_->LoadURL(L"javascript:'hello world'"); + MessageLoop::current()->Quit(); + MessageLoop::current()->Run(); + text = test_shell_->GetDocumentText(); + EXPECT_EQ(L"hello world", text); +} + +TEST_F(BookmarkletTest, DocumentWrite) { + test_shell_->LoadURL( + L"javascript:document.open();" + L"document.write('hello world');" + L"document.close()"); + MessageLoop::current()->Quit(); + MessageLoop::current()->Run(); + std::wstring text = test_shell_->GetDocumentText(); + EXPECT_EQ(L"hello world", text); +} diff --git a/webkit/glue/cache_manager.cc b/webkit/glue/cache_manager.cc new file mode 100644 index 0000000..df35b03 --- /dev/null +++ b/webkit/glue/cache_manager.cc @@ -0,0 +1,115 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +// Instead of providing accessors, we make all members of Cache public. +// This will make it easier to track WebKit changes to the Cache class. +#define private public +#include "Cache.h" +#undef private +#pragma warning(pop) + +#undef LOG +#include "base/logging.h" +#include "webkit/glue/cache_manager.h" + +namespace { + +// A helper method for coverting a WebCore::Cache::TypeStatistic to a +// CacheManager::ResourceTypeStat. +CacheManager::ResourceTypeStat TypeStatisticToResourceTypeStat( + const WebCore::Cache::TypeStatistic& in_stat) { + CacheManager::ResourceTypeStat stat; + stat.count = static_cast<size_t>(in_stat.count); + stat.size = static_cast<size_t>(in_stat.size); + stat.live_size = static_cast<size_t>(in_stat.liveSize); + stat.decoded_size = static_cast<size_t>(in_stat.decodedSize); + return stat; +} + +} // namespace + +// ---------------------------------------------------------------------------- +// CacheManager implementation + +CacheManager::CacheManager() { +} + +CacheManager::~CacheManager() { +} + +// static +void CacheManager::GetUsageStats(UsageStats* result) { + DCHECK(result); + + WebCore::Cache* cache = WebCore::cache(); + + if (cache) { + result->min_dead_capacity = cache->m_minDeadCapacity; + result->max_dead_capacity = cache->m_maxDeadCapacity; + result->capacity = cache->m_capacity; + result->live_size = cache->m_liveSize; + result->dead_size = cache->m_deadSize; + } else { + memset(result, 0, sizeof(UsageStats)); + } +} + +// static +void CacheManager::SetCapacities(size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity) { + WebCore::Cache* cache = WebCore::cache(); + + if (cache) { + cache->setCapacities(static_cast<unsigned int>(min_dead_capacity), + static_cast<unsigned int>(max_dead_capacity), + static_cast<unsigned int>(capacity)); + } +} + +// static +void CacheManager::GetResourceTypeStats( + CacheManager::ResourceTypeStats* result) { + WebCore::Cache* cache = WebCore::cache(); + if (cache) { + WebCore::Cache::Statistics in_stats = cache->getStatistics(); + result->images = TypeStatisticToResourceTypeStat(in_stats.images); + result->css_stylesheets = TypeStatisticToResourceTypeStat( + in_stats.cssStyleSheets); + result->scripts = TypeStatisticToResourceTypeStat(in_stats.scripts); + result->xsl_stylesheets = TypeStatisticToResourceTypeStat( + in_stats.xslStyleSheets); + result->fonts = TypeStatisticToResourceTypeStat(in_stats.fonts); + } else { + memset(result, 0, sizeof(CacheManager::ResourceTypeStats)); + } +} diff --git a/webkit/glue/cache_manager.h b/webkit/glue/cache_manager.h new file mode 100644 index 0000000..643b40a --- /dev/null +++ b/webkit/glue/cache_manager.h @@ -0,0 +1,88 @@ +// Copyright 2008, 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 WEBKIT_GLUE_CACHE_MANAGER_H__ +#define WEBKIT_GLUE_CACHE_MANAGER_H__ + +#include <stddef.h> + +class CacheManager { + public: + struct UsageStats { + public: + // Capacities. + size_t min_dead_capacity; + size_t max_dead_capacity; + size_t capacity; + // Utilization. + size_t live_size; + size_t dead_size; + }; + + // A struct mirroring WebCore::Cache::TypeStatistic that we can send to the + // browser process. + struct ResourceTypeStat { + size_t count; + size_t size; + size_t live_size; + size_t decoded_size; + ResourceTypeStat() + : count(0), size(0), live_size(0), decoded_size(0) {} + }; + + // A struct mirroring WebCore::Cache::Statistics that we can send to the + // browser process. + struct ResourceTypeStats { + ResourceTypeStat images; + ResourceTypeStat css_stylesheets; + ResourceTypeStat scripts; + ResourceTypeStat xsl_stylesheets; + ResourceTypeStat fonts; + }; + + // Gets the usage statistics from the WebCore cache + static void GetUsageStats(UsageStats* result); + + // Sets the capacities of the WebCore cache, evicting objects as necessary + static void SetCapacities(size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity); + + // Get usage stats about the WebCore cache. + static void GetResourceTypeStats(ResourceTypeStats* result); + + private: + // This class only has static methods. It can't be instantiated + CacheManager(); + ~CacheManager(); + + DISALLOW_EVIL_CONSTRUCTORS(CacheManager); +}; + +#endif // WEBKIT_GLUE_CACHE_MANAGER_H__ diff --git a/webkit/glue/chrome_client_impl.cc b/webkit/glue/chrome_client_impl.cc new file mode 100644 index 0000000..20109b9 --- /dev/null +++ b/webkit/glue/chrome_client_impl.cc @@ -0,0 +1,441 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "FloatRect.h" +#include "FileChooser.h" +#include "FrameLoadRequest.h" +#include "FrameView.h" +#include "HitTestResult.h" +#include "IntRect.h" +#include "Page.h" +#include "WindowFeatures.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/chrome_client_impl.h" + +#include "base/gfx/rect.h" +#include "googleurl/src/gurl.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webinputevent.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webview_delegate.h" +#include "webkit/glue/webview_impl.h" + +struct IWebURLResponse; + +// Callback class that's given to the WebViewDelegate during a file choose +// operation. +class WebFileChooserCallbackImpl : public WebFileChooserCallback { + public: + WebFileChooserCallbackImpl(PassRefPtr<WebCore::FileChooser> file_chooser) + : file_chooser_(file_chooser) { + } + + void OnFileChoose(const std::wstring& file_name) { + file_chooser_->chooseFile(webkit_glue::StdWStringToString(file_name)); + } + + private: + RefPtr<WebCore::FileChooser> file_chooser_; + DISALLOW_EVIL_CONSTRUCTORS(WebFileChooserCallbackImpl); +}; + +ChromeClientImpl::ChromeClientImpl(WebViewImpl* webview) + : webview_(webview), + toolbars_visible_(true), + statusbar_visible_(true), + scrollbars_visible_(true), + menubar_visible_(true), + resizable_(true) { +} + +ChromeClientImpl::~ChromeClientImpl() { +} + +void ChromeClientImpl::chromeDestroyed() { + delete this; +} + +void ChromeClientImpl::setWindowRect(const WebCore::FloatRect& r) { + WebViewDelegate* d = webview_->delegate(); + if (d) { + WebCore::IntRect ir(r); + d->SetWindowRect(webview_, + gfx::Rect(ir.x(), ir.y(), ir.width(), ir.height())); + } +} + +WebCore::FloatRect ChromeClientImpl::windowRect() { + gfx::Point origin; + if (webview_->delegate()) + webview_->delegate()->GetWindowLocation(webview_, &origin); + const gfx::Size size = webview_->size(); + + return WebCore::FloatRect( + static_cast<float>(origin.x()), + static_cast<float>(origin.y()), + static_cast<float>(size.width()), + static_cast<float>(size.height())); +} + +WebCore::FloatRect ChromeClientImpl::pageRect() { + // We hide the details of the window's border thickness from the web page by + // simple re-using the window position here. So, from the point-of-view of + // the web page, the window has no border. + return windowRect(); +} + +float ChromeClientImpl::scaleFactor() { + // This is supposed to return the scale factor of the web page. It looks like + // the implementor of the graphics layer is responsible for doing most of the + // operations associated with scaling. However, this value is used ins some + // cases by WebCore. For example, this is used as a scaling factor in canvas + // so that things drawn in it are scaled just like the web page is. + // + // We don't currently implement scaling, so just return 1.0 (no scaling). + return 1.0; +} + +void ChromeClientImpl::focus() { + WebViewDelegate* d = webview_->delegate(); + if (d) + d->Focus(webview_); +} + +void ChromeClientImpl::unfocus() { + WebViewDelegate* d = webview_->delegate(); + if (d) + d->Blur(webview_); +} + +bool ChromeClientImpl::canTakeFocus(WebCore::FocusDirection) { + // For now the browser can always take focus if we're not running layout + // tests. + return !webkit_glue::IsLayoutTestMode(); +} + +void ChromeClientImpl::takeFocus(WebCore::FocusDirection direction) { + WebViewDelegate* d = webview_->delegate(); + if (d) { + d->TakeFocus(webview_, + direction == WebCore::FocusDirectionBackward); + } +} + +WebCore::Page* ChromeClientImpl::createWindow( + WebCore::Frame* frame, const WebCore::FrameLoadRequest& r, + const WebCore::WindowFeatures& features) { + WebViewDelegate* d = webview_->delegate(); + if (!d) + return NULL; + + bool userGesture = frame->scriptBridge()->wasRunByUserGesture(); + WebViewImpl* new_view = static_cast<WebViewImpl*>( + d->CreateWebView(webview_, userGesture)); + if (!new_view) + return NULL; + + // The request is empty when we are just being asked to open a blank window. + // This corresponds to window.open(""), for example. + if (!r.resourceRequest().isEmpty()) { + WebRequestImpl request(r); + new_view->main_frame()->LoadRequest(&request); + } + + WebViewImpl* new_view_impl = static_cast<WebViewImpl*>(new_view); + return new_view_impl->page(); +} + +static inline bool CurrentEventShouldCauseBackgroundTab( + const WebInputEvent* input_event) { + if (!input_event) + return false; + + if (input_event->type != WebInputEvent::MOUSE_UP) + return false; + + const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(input_event); + return (mouse_event->button == WebMouseEvent::BUTTON_MIDDLE); +} + +void ChromeClientImpl::show() { + WebViewDelegate* d = webview_->delegate(); + if (d) { + // If our default configuration was modified by a script or wasn't + // created by a user gesture, then show as a popup. Else, let this + // new window be opened as a toplevel window. + // + bool as_popup = + !toolbars_visible_ || + !statusbar_visible_ || + !scrollbars_visible_ || + !menubar_visible_ || + !resizable_ || + !d->WasOpenedByUserGesture(webview_); + + WindowOpenDisposition disposition = NEW_FOREGROUND_TAB; + if (as_popup) + disposition = NEW_POPUP; + if (CurrentEventShouldCauseBackgroundTab(WebViewImpl::current_input_event())) + disposition = NEW_BACKGROUND_TAB; + + d->Show(webview_, disposition); + } +} + +bool ChromeClientImpl::canRunModal() { + return webview_->delegate() != NULL; +} + +void ChromeClientImpl::runModal() { + WebViewDelegate* d = webview_->delegate(); + if (d) + d->RunModal(webview_); +} + +void ChromeClientImpl::setToolbarsVisible(bool value) { + toolbars_visible_ = value; +} + +bool ChromeClientImpl::toolbarsVisible() { + return toolbars_visible_; +} + +void ChromeClientImpl::setStatusbarVisible(bool value) { + statusbar_visible_ = value; +} + +bool ChromeClientImpl::statusbarVisible() { + return statusbar_visible_; +} + +void ChromeClientImpl::setScrollbarsVisible(bool value) { + scrollbars_visible_ = value; + WebFrameImpl* web_frame = + static_cast<WebFrameImpl*>(webview_->GetMainFrame()); + if (web_frame) + web_frame->SetAllowsScrolling(value); +} + +bool ChromeClientImpl::scrollbarsVisible() { + return scrollbars_visible_; +} + +void ChromeClientImpl::setMenubarVisible(bool value) { + menubar_visible_ = value; +} + +bool ChromeClientImpl::menubarVisible() { + return menubar_visible_; +} + +void ChromeClientImpl::setResizable(bool value) { + resizable_ = value; +} + +void ChromeClientImpl::addMessageToConsole(const WebCore::String& message, + unsigned int line_no, + const WebCore::String& source_id) { + WebViewDelegate* d = webview_->delegate(); + if (d) { + std::wstring wstr_message = webkit_glue::StringToStdWString(message); + std::wstring wstr_source_id = webkit_glue::StringToStdWString(source_id); + d->AddMessageToConsole(webview_, wstr_message, line_no, wstr_source_id); + } +} + +bool ChromeClientImpl::canRunBeforeUnloadConfirmPanel() { + return webview_->delegate() != NULL; +} + +bool ChromeClientImpl::runBeforeUnloadConfirmPanel(const WebCore::String& message, + WebCore::Frame* frame) { + WebViewDelegate* d = webview_->delegate(); + if (d) { + std::wstring wstr = webkit_glue::StringToStdWString(message); + return d->RunBeforeUnloadConfirm(webview_, wstr); + } + return false; +} + +void ChromeClientImpl::closeWindowSoon() { + // Make sure this Page can no longer be found by JS. + webview_->page()->setGroupName(WebCore::String()); + + // Make sure that all loading is stopped. Ensures that JS stops executing! + webview_->StopLoading(); + + WebViewDelegate* d = webview_->delegate(); + if (d) + d->CloseWidgetSoon(webview_); +} + +// Although a WebCore::Frame is passed in, we don't actually use it, since we +// already know our own webview_. +void ChromeClientImpl::runJavaScriptAlert(WebCore::Frame* frame, + const WebCore::String& message) { + // Pass the request on to the WebView delegate, for more control. + WebViewDelegate* d = webview_->delegate(); + if (d) { + std::wstring wstr = webkit_glue::StringToStdWString(message); + d->RunJavaScriptAlert(webview_, wstr); + } +} + +// See comments for runJavaScriptAlert(). +bool ChromeClientImpl::runJavaScriptConfirm(WebCore::Frame* frame, + const WebCore::String& message) { + WebViewDelegate* d = webview_->delegate(); + if (d) { + std::wstring wstr = webkit_glue::StringToStdWString(message); + return d->RunJavaScriptConfirm(webview_, wstr); + } + return false; +} + +// See comments for runJavaScriptAlert(). +bool ChromeClientImpl::runJavaScriptPrompt(WebCore::Frame* frame, + const WebCore::String& message, + const WebCore::String& defaultValue, + WebCore::String& result) { + WebViewDelegate* d = webview_->delegate(); + if (d) { + std::wstring wstr_message = webkit_glue::StringToStdWString(message); + std::wstring wstr_default = webkit_glue::StringToStdWString(defaultValue); + std::wstring wstr_result; + bool ok = d->RunJavaScriptPrompt(webview_, + wstr_message, + wstr_default, + &wstr_result); + if (ok) + result = webkit_glue::StdWStringToString(wstr_result); + return ok; + } + return false; +} + +void ChromeClientImpl::setStatusbarText(const WebCore::String&) { + // TODO(mbelshe): implement me +} + +bool ChromeClientImpl::shouldInterruptJavaScript() { + // TODO(mbelshe): implement me + return false; +} + +bool ChromeClientImpl::tabsToLinks() const { + // TODO(pamg): Consider controlling this with a user preference, when we have + // a preference system in place. + // For now Chrome will allow link to take focus if we're not running layout + // tests. + return !webkit_glue::IsLayoutTestMode(); +} + +WebCore::IntRect ChromeClientImpl::windowResizerRect() const { + // TODO(mbelshe): implement me + WebCore::IntRect rv; + return rv; +} + +void ChromeClientImpl::addToDirtyRegion(const WebCore::IntRect& damaged_rect) { + ASSERT_NOT_REACHED(); +} + +void ChromeClientImpl::scrollBackingStore(int dx, int dy, + const WebCore::IntRect& scroll_rect, + const WebCore::IntRect& clip_rect) { + ASSERT_NOT_REACHED(); +} + +void ChromeClientImpl::updateBackingStore() { + ASSERT_NOT_REACHED(); +} + +void ChromeClientImpl::mouseDidMoveOverElement(const WebCore::HitTestResult& result, + unsigned modifierFlags) { + + // Find out if the mouse is over a link, and if so, let our UI know... somehow + WebViewDelegate* d = webview_->delegate(); + if (d) { + if (result.isLiveLink() && !result.absoluteLinkURL().string().isEmpty()) { + d->UpdateTargetURL(webview_, webkit_glue::KURLToGURL(result.absoluteLinkURL())); + } else { + d->UpdateTargetURL(webview_, GURL()); + } + } +} + +void ChromeClientImpl::setToolTip(const WebCore::String& tooltip_text) { + if (webview_->delegate()) { + std::wstring tooltip_text_as_wstring = + webkit_glue::StringToStdWString(tooltip_text); + webview_->delegate()->SetTooltipText(webview_, tooltip_text_as_wstring); + } +} + +void ChromeClientImpl::runFileChooser(const WebCore::String& default_path, + PassRefPtr<WebCore::FileChooser> fileChooser) { + WebViewDelegate* delegate = webview_->delegate(); + if (!delegate) + return; + + std::wstring suggestion = webkit_glue::StringToStdWString(default_path); + WebFileChooserCallbackImpl* chooser = new WebFileChooserCallbackImpl(fileChooser); + delegate->RunFileChooser(suggestion, chooser); +} + +WebCore::IntRect ChromeClientImpl::windowToScreen(const WebCore::IntRect& rect) { + WebCore::IntRect screen_rect(rect); + + WebViewDelegate* d = webview_->delegate(); + if (d) { + gfx::Point window_pos; + d->GetWindowLocation(webview_, &window_pos); + screen_rect.move(window_pos.x(), window_pos.y()); + } + + return screen_rect; +} + +void ChromeClientImpl::print(WebCore::Frame* frame) { + WebViewDelegate* d = webview_->delegate(); + if (d) { + d->ScriptedPrint(WebFrameImpl::FromFrame(frame)); + } +} + +void ChromeClientImpl::exceededDatabaseQuota(WebCore::Frame* frame, + const WebCore::String& databaseName) { + // TODO(tc): If we enable the storage API, we need to implement this function. +} diff --git a/webkit/glue/chrome_client_impl.h b/webkit/glue/chrome_client_impl.h new file mode 100644 index 0000000..8515f15 --- /dev/null +++ b/webkit/glue/chrome_client_impl.h @@ -0,0 +1,137 @@ +// Copyright 2008, 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 WEBKIT_GLUE_CHROME_CLIENT_IMPL_H__ +#define WEBKIT_GLUE_CHROME_CLIENT_IMPL_H__ + +#pragma warning(push, 0) +#include "ChromeClientWin.h" +#pragma warning(pop) + +class WebViewImpl; +namespace WebCore { + class SecurityOrigin; + struct WindowFeatures; +} + +// Handles window-level notifications from WebCore on behalf of a WebView. +class ChromeClientImpl : public WebCore::ChromeClientWin { +public: + ChromeClientImpl(WebViewImpl* webview); + virtual ~ChromeClientImpl(); + + virtual void chromeDestroyed(); + + virtual void setWindowRect(const WebCore::FloatRect&); + virtual WebCore::FloatRect windowRect(); + + virtual WebCore::FloatRect pageRect(); + + virtual float scaleFactor(); + + virtual void focus(); + virtual void unfocus(); + + virtual bool canTakeFocus(WebCore::FocusDirection); + virtual void takeFocus(WebCore::FocusDirection); + + virtual WebCore::Page* createWindow(WebCore::Frame*, + const WebCore::FrameLoadRequest&, + const WebCore::WindowFeatures&); + virtual void show(); + + virtual bool canRunModal(); + virtual void runModal(); + + virtual void setToolbarsVisible(bool); + virtual bool toolbarsVisible(); + + virtual void setStatusbarVisible(bool); + virtual bool statusbarVisible(); + + virtual void setScrollbarsVisible(bool); + virtual bool scrollbarsVisible(); + + virtual void setMenubarVisible(bool); + virtual bool menubarVisible(); + + virtual void setResizable(bool); + + virtual void addMessageToConsole(const WebCore::String& message, + unsigned int lineNumber, + const WebCore::String& sourceID); + + virtual bool canRunBeforeUnloadConfirmPanel(); + virtual bool runBeforeUnloadConfirmPanel(const WebCore::String& message, + WebCore::Frame* frame); + + virtual void closeWindowSoon(); + + virtual void runJavaScriptAlert(WebCore::Frame*, const WebCore::String&); + virtual bool runJavaScriptConfirm(WebCore::Frame*, const WebCore::String&); + virtual bool runJavaScriptPrompt(WebCore::Frame*, const WebCore::String& message, const WebCore::String& defaultValue, WebCore::String& result); + + virtual void setStatusbarText(const WebCore::String&); + virtual bool shouldInterruptJavaScript(); + + // Returns true if anchors should accept keyboard focus with the tab key. + // This method is used in a convoluted fashion by EventHandler::tabsToLinks. + // It's a twisted path (self-evident, but more complicated than seems + // necessary), but the net result is that returning true from here, on a + // platform other than MAC or QT, lets anchors get keyboard focus. + virtual bool tabsToLinks() const; + + virtual WebCore::IntRect windowResizerRect() const; + virtual void addToDirtyRegion(const WebCore::IntRect&); + virtual void scrollBackingStore(int dx, int dy, const WebCore::IntRect& scrollViewRect, const WebCore::IntRect& clipRect); + virtual void updateBackingStore(); + + virtual void mouseDidMoveOverElement(const WebCore::HitTestResult& result, unsigned modifierFlags); + + virtual void setToolTip(const WebCore::String& tooltip_text); + + virtual void runFileChooser(const WebCore::String&, + PassRefPtr<WebCore::FileChooser>); + virtual WebCore::IntRect windowToScreen(const WebCore::IntRect& rect); + + virtual void print(WebCore::Frame*); + + virtual void exceededDatabaseQuota(WebCore::Frame*, + const WebCore::String& databaseName); + +private: + WebViewImpl* webview_; // weak pointer + bool toolbars_visible_; + bool statusbar_visible_; + bool scrollbars_visible_; + bool menubar_visible_; + bool resizable_; +}; + +#endif // WEBKIT_GLUE_CHROME_CLIENT_IMPL_H__ diff --git a/webkit/glue/console_message_level.h b/webkit/glue/console_message_level.h new file mode 100644 index 0000000..aae95fa --- /dev/null +++ b/webkit/glue/console_message_level.h @@ -0,0 +1,40 @@ +// Copyright 2008, 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 WEBKIT_GLUE_CONSOLE_MESSAGE_LEVEL_H__ +#define WEBKIT_GLUE_CONSOLE_MESSAGE_LEVEL_H__ + +enum ConsoleMessageLevel { + MESSAGE_LEVEL_TIP, + MESSAGE_LEVEL_LOG, + MESSAGE_LEVEL_WARNING, + MESSAGE_LEVEL_ERROR +}; + +#endif // WEBKIT_GLUE_CONSOLE_MESSAGE_LEVEL_H__ diff --git a/webkit/glue/context_menu_client_impl.cc b/webkit/glue/context_menu_client_impl.cc new file mode 100644 index 0000000..c5227676 --- /dev/null +++ b/webkit/glue/context_menu_client_impl.cc @@ -0,0 +1,276 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "ContextMenu.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Editor.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HitTestResult.h" +#include "KURL.h" +#include "Widget.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/context_menu_client_impl.h" + +#include "base/string_util.h" +#include "webkit/glue/context_node_types.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webdocumentloader_impl.h" +#include "webkit/glue/webview_impl.h" + +#include "base/word_iterator.h" + +// Helper function to determine whether text is a single word or a sentence. +static bool IsASingleWord(const std::wstring& text) { + WordIterator iter(text, WordIterator::BREAK_WORD); + int word_count = 0; + if (!iter.Init()) return false; + while (iter.Advance()) { + if (iter.IsWord()) { + word_count++; + if (word_count > 1) // More than one word. + return false; + } + } + + // Check for 0 words. + if (!word_count) + return false; + + // Has a single word. + return true; +} + +// 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. +static std::wstring GetMisspelledWord(WebCore::ContextMenu* default_menu, + WebCore::Frame* selected_frame) { + std::wstring misspelled_word_string; + + // First select from selectedText to check for multiple word selection. + misspelled_word_string = CollapseWhitespace( + webkit_glue::StringToStdWString(selected_frame->selectedText()), + false); + + // Don't provide suggestions for multiple words. + if (!misspelled_word_string.empty() && + !IsASingleWord(misspelled_word_string)) + return L""; + + // Expand around the click to see if we clicked a word. + WebCore::Selection selection; + WebCore::VisiblePosition pos(default_menu->hitTestResult().innerNode()-> + renderer()->positionForPoint(default_menu->hitTestResult(). + localPoint())); + + if (pos.isNotNull()) { + selection = WebCore::Selection(pos); + selection.expandUsingGranularity(WebCore::WordGranularity); + } + + if (selection.isRange()) { + selected_frame->setSelectionGranularity(WebCore::WordGranularity); + } + + if (selected_frame->shouldChangeSelection(selection)) + selected_frame->selectionController()->setSelection(selection); + + misspelled_word_string = CollapseWhitespace( + webkit_glue::StringToStdWString(selected_frame->selectedText()), + false); + + return misspelled_word_string; +} + +ContextMenuClientImpl::~ContextMenuClientImpl() { +} + +void ContextMenuClientImpl::contextMenuDestroyed() { + delete this; +} + +// Figure out the URL of a page or subframe. Returns |page_type| as the type, +// which indicates page or subframe, or ContextNode::NONE if the URL could not +// be determined for some reason. +static ContextNode::Type GetTypeAndURLFromFrame(WebCore::Frame* frame, + GURL* url, + ContextNode::Type page_type) { + ContextNode::Type type = ContextNode::NONE; + if (frame) { + WebCore::DocumentLoader* dl = frame->loader()->documentLoader(); + if (dl) { + WebDataSource* ds = static_cast<WebDocumentLoaderImpl*>(dl)-> + GetDataSource(); + if (ds) { + type = page_type; + *url = ds->HasUnreachableURL() ? ds->GetUnreachableURL() + : ds->GetRequest().GetURL(); + } + } + } + return type; +} + +WebCore::PlatformMenuDescription + ContextMenuClientImpl::getCustomMenuFromDefaultItems( + WebCore::ContextMenu* default_menu) { + // Displaying the context menu in this function is a big hack as we don't + // have context, i.e. whether this is being invoked via a script or in + // response to user input (Mouse event WM_RBUTTONDOWN, + // Keyboard events KeyVK_APPS, Shift+F10). Check if this is being invoked + // in response to the above input events before popping up the context menu. + if (!webview_->context_menu_allowed()) + return NULL; + + WebCore::HitTestResult r = default_menu->hitTestResult(); + WebCore::Frame* selected_frame = r.innerNonSharedNode()->document()->frame(); + + WebCore::IntPoint menu_point = + selected_frame->view()->contentsToWindow(r.point()); + + ContextNode::Type type = ContextNode::NONE; + + // Links, Images and Image-Links take preference over all else. + WebCore::KURL link_url = r.absoluteLinkURL(); + std::wstring link_url_string; + if (!link_url.isEmpty()) { + type = ContextNode::LINK; + } + WebCore::KURL image_url = r.absoluteImageURL(); + std::wstring image_url_string; + if (!image_url.isEmpty()) { + type = ContextNode::IMAGE; + } + if (!image_url.isEmpty() && !link_url.isEmpty()) + type = ContextNode::IMAGE_LINK; + + // If it's not a link, an image or an image link, show a selection menu or a + // more generic page menu. + std::wstring selection_text_string; + std::wstring misspelled_word_string; + GURL frame_url; + GURL page_url; + + // Send the frame and page URLs in any case. + ContextNode::Type frame_type = ContextNode::NONE; + ContextNode::Type page_type = + GetTypeAndURLFromFrame(webview_->main_frame()->frame(), + &page_url, + ContextNode::PAGE); + if (selected_frame != webview_->main_frame()->frame()) { + frame_type = GetTypeAndURLFromFrame(selected_frame, + &frame_url, + ContextNode::FRAME); + } + + if (type == ContextNode::NONE) { + if (r.isContentEditable()) { + type = ContextNode::EDITABLE; + if (webview_->FocusedFrameNeedsSpellchecking()) { + misspelled_word_string = GetMisspelledWord(default_menu, + selected_frame); + } + } else if (r.isSelected()) { + type = ContextNode::SELECTION; + selection_text_string = + CollapseWhitespace( + webkit_glue::StringToStdWString(selected_frame->selectedText()), + false); + } else if (selected_frame != webview_->main_frame()->frame()) { + type = frame_type; + } else { + type = page_type; + } + } + + int edit_flags = ContextNode::CAN_DO_NONE; + if (webview_->GetFocusedWebCoreFrame()->editor()->canUndo()) + edit_flags |= ContextNode::CAN_UNDO; + if (webview_->GetFocusedWebCoreFrame()->editor()->canRedo()) + edit_flags |= ContextNode::CAN_REDO; + if (webview_->GetFocusedWebCoreFrame()->editor()->canCut()) + edit_flags |= ContextNode::CAN_CUT; + if (webview_->GetFocusedWebCoreFrame()->editor()->canCopy()) + edit_flags |= ContextNode::CAN_COPY; + if (webview_->GetFocusedWebCoreFrame()->editor()->canPaste()) + edit_flags |= ContextNode::CAN_PASTE; + if (webview_->GetFocusedWebCoreFrame()->editor()->canDelete()) + edit_flags |= ContextNode::CAN_DELETE; + // We can always select all... + edit_flags |= ContextNode::CAN_SELECT_ALL; + + WebViewDelegate* d = webview_->delegate(); + if (d) { + d->ShowContextMenu(webview_, + type, + menu_point.x(), + menu_point.y(), + webkit_glue::KURLToGURL(link_url), + webkit_glue::KURLToGURL(image_url), + page_url, + frame_url, + selection_text_string, + misspelled_word_string, + edit_flags); + } + return NULL; +} + +void ContextMenuClientImpl::contextMenuItemSelected( + WebCore::ContextMenuItem*, const WebCore::ContextMenu*) { +} + +void ContextMenuClientImpl::downloadURL(const WebCore::KURL&) { +} + +void ContextMenuClientImpl::copyImageToClipboard(const WebCore::HitTestResult&) { +} + +void ContextMenuClientImpl::searchWithGoogle(const WebCore::Frame*) { +} + +void ContextMenuClientImpl::lookUpInDictionary(WebCore::Frame*) { +} + +void ContextMenuClientImpl::speak(const WebCore::String&) { +} + +void ContextMenuClientImpl::stopSpeaking() { +} + +bool ContextMenuClientImpl::shouldIncludeInspectElementItem() { + return false; // TODO(jackson): Eventually include the inspector context menu item +} diff --git a/webkit/glue/context_menu_client_impl.h b/webkit/glue/context_menu_client_impl.h new file mode 100644 index 0000000..fd64068 --- /dev/null +++ b/webkit/glue/context_menu_client_impl.h @@ -0,0 +1,65 @@ +// Copyright 2008, 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 WEBKIT_GLUE_CONTEXT_MENU_CLIENT_IMPL_H__ +#define WEBKIT_GLUE_CONTEXT_MENU_CLIENT_IMPL_H__ + +#pragma warning(push, 0) +#include "ContextMenuClient.h" +#pragma warning(pop) + +class WebViewImpl; + +// Handles window-level notifications from WebCore on behalf of a WebView. +class ContextMenuClientImpl : public WebCore::ContextMenuClient { +public: + ContextMenuClientImpl(WebViewImpl* webview) : webview_(webview) { + } + + virtual ~ContextMenuClientImpl(); + virtual void contextMenuDestroyed(); + + virtual WebCore::PlatformMenuDescription getCustomMenuFromDefaultItems( + WebCore::ContextMenu*); + virtual void contextMenuItemSelected(WebCore::ContextMenuItem*, + const WebCore::ContextMenu*); + + virtual void downloadURL(const WebCore::KURL&); + virtual void copyImageToClipboard(const WebCore::HitTestResult&); + virtual void searchWithGoogle(const WebCore::Frame*); + virtual void lookUpInDictionary(WebCore::Frame*); + virtual void speak(const WebCore::String&); + virtual void stopSpeaking(); + virtual bool shouldIncludeInspectElementItem(); + +private: + WebViewImpl* webview_; // weak pointer +}; + +#endif // WEBKIT_GLUE_CONTEXT_MENU_CLIENT_IMPL_H__ diff --git a/webkit/glue/context_menu_unittest.cc b/webkit/glue/context_menu_unittest.cc new file mode 100644 index 0000000..5e78eb6 --- /dev/null +++ b/webkit/glue/context_menu_unittest.cc @@ -0,0 +1,91 @@ +// Copyright 2008, 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. + +// Tests for displaying context menus in corner cases (and swallowing context +// menu events when appropriate) + +#include <vector> + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webview.h" +#include "webkit/tools/test_shell/test_shell_test.h" + + +// Right clicking inside on an iframe should produce a context menu +class ContextMenuCapturing : public TestShellTest { + protected: + void SetUp() { + TestShellTest::SetUp(); + + iframes_data_dir_ = data_dir_; + file_util::AppendToPath(&iframes_data_dir_, L"test_shell"); + file_util::AppendToPath(&iframes_data_dir_, L"iframes"); + ASSERT_TRUE(file_util::PathExists(iframes_data_dir_)); + } + + std::wstring iframes_data_dir_; +}; + + +TEST_F(ContextMenuCapturing, ContextMenuCapturing) { + // Make sure we have no stored mouse event state + WebViewDelegate* raw_delegate = test_shell_->webView()->GetDelegate(); + TestWebViewDelegate* test_delegate = static_cast<TestWebViewDelegate*>(raw_delegate); + test_delegate->clear_captured_context_menu_events(); + EXPECT_EQ(0, test_delegate->captured_context_menu_events().size()); + + std::wstring test_url = GetTestURL(iframes_data_dir_, L"testiframe.html"); + test_shell_->LoadURL(test_url.c_str()); + test_shell_->WaitTestFinished(); + + // Create a right click in the center of the iframe. (I'm hoping this will + // make this a bit more robust in case of some other formatting or other bug.) + WebMouseEvent mouse_event; + mouse_event.type = WebInputEvent::MOUSE_DOWN; + mouse_event.modifiers = 0; + mouse_event.button = WebMouseEvent::BUTTON_RIGHT; + mouse_event.x = 250; + mouse_event.y = 250; + mouse_event.global_x = 250; + mouse_event.global_y = 250; + mouse_event.timestamp_sec = 0; + mouse_event.layout_test_click_count = 0; + + WebView* webview = test_shell_->webView(); + webview->HandleInputEvent(&mouse_event); + + // Now simulate the corresponding up event which should display the menu + mouse_event.type = WebInputEvent::MOUSE_UP; + webview->HandleInputEvent(&mouse_event); + + EXPECT_EQ(1, test_delegate->captured_context_menu_events().size()); +} diff --git a/webkit/glue/context_node_types.h b/webkit/glue/context_node_types.h new file mode 100644 index 0000000..e3c86f3 --- /dev/null +++ b/webkit/glue/context_node_types.h @@ -0,0 +1,87 @@ +// Copyright 2008, 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 WEBKIT_GLUE_CONTEXT_NODE_TYPES_H__ +#define WEBKIT_GLUE_CONTEXT_NODE_TYPES_H__ + +#include "base/basictypes.h" +#include "base/logging.h" + +// This class is for scoping only. +class ContextNode { + public: + // This enumeration defines the type of node that the user may perform a + // contextual action on in the WebView. + enum Type { + + // No node is selected + NONE = 0, + + // The top page is selected + PAGE = 1, + + // A subframe page is selected + FRAME = 2, + + // A link is selected + LINK = 3, + + // An image is selected + IMAGE = 4, + + // An image that is also a link is selected + IMAGE_LINK = 5, + + // There is a textual or mixed selection that is selected + SELECTION = 6, + + // An editiable element is selected + EDITABLE = 7, + + // A misspelled word is selected + MISPELLED_WORD = 8 + }; + + static Type FromInt(int32 type) { + return static_cast<Type>(type); + } + + enum Capability { + CAN_DO_NONE = 0x0, + CAN_UNDO = 0x1, + CAN_REDO = 0x2, + CAN_CUT = 0x4, + CAN_COPY = 0x8, + CAN_PASTE = 0x10, + CAN_DELETE = 0x20, + CAN_SELECT_ALL = 0x40, + }; +}; + +#endif // WEBKIT_GLUE_CONTEXT_NODE_TYPES_H__ diff --git a/webkit/glue/cpp_binding_example.cc b/webkit/glue/cpp_binding_example.cc new file mode 100644 index 0000000..25616d4828 --- /dev/null +++ b/webkit/glue/cpp_binding_example.cc @@ -0,0 +1,119 @@ +// Copyright 2008, 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. + +// This file contains the definition for CppBindingExample, a usage example +// that is not actually used anywhere. See cpp_binding_example.h. + +#include "cpp_binding_example.h" + +CppBindingExample::CppBindingExample() { + // Map properties. It's recommended, but not required, that the JavaScript + // names (used as the keys in this map) match the names of the member + // variables exposed through those names. + BindProperty("my_value", &my_value); + BindProperty("my_other_value", &my_other_value); + + // Map methods. See comment above about names. + BindMethod("echoValue", &CppBindingExample::echoValue); + BindMethod("echoType", &CppBindingExample::echoType); + BindMethod("plus", &CppBindingExample::plus); + + // The fallback method is called when a nonexistent method is called on an + // object. If none is specified, calling a nonexistent method causes an + // exception to be thrown and the JavaScript execution is stopped. + BindFallbackMethod(&CppBindingExample::fallbackMethod); + + my_value.Set(10); + my_other_value.Set("Reinitialized!"); +} + +void CppBindingExample::echoValue(const CppArgumentList& args, + CppVariant* result) { + if (args.size() < 1) { + result->SetNull(); + return; + } + result->Set(args[0]); +} + +void CppBindingExample::echoType(const CppArgumentList& args, + CppVariant* result) { + if (args.size() < 1) { + result->SetNull(); + return; + } + // Note that if args[0] is a string, the following assignment implicitly + // makes a copy of that string, which may have an undesirable impact on + // performance. + CppVariant arg1 = args[0]; + if (arg1.isBool()) + result->Set(true); + else if (arg1.isInt32()) + result->Set(7); + else if (arg1.isDouble()) + result->Set(3.14159); + else if (arg1.isString()) + result->Set("Success!"); +} + +void CppBindingExample::plus(const CppArgumentList& args, + CppVariant* result) { + if (args.size() < 2) { + result->SetNull(); + return; + } + + CppVariant arg1 = args[0]; + CppVariant arg2 = args[1]; + + if (!arg1.isNumber() || !arg2.isNumber()) { + result->SetNull(); + return; + } + + // The value of a CppVariant may be read directly from its NPVariant struct. + // (However, it should only be set using one of the Set() functions.) + double sum = 0.; + if (arg1.isDouble()) + sum += arg1.value.doubleValue; + else if (arg1.isInt32()) + sum += arg1.value.intValue; + + if (arg2.isDouble()) + sum += arg2.value.doubleValue; + else if (arg2.isInt32()) + sum += arg2.value.intValue; + + result->Set(sum); +} + +void CppBindingExample::fallbackMethod(const CppArgumentList& args, + CppVariant* result) { + printf("Error: unknown JavaScript method invoked.\n"); +} diff --git a/webkit/glue/cpp_binding_example.h b/webkit/glue/cpp_binding_example.h new file mode 100644 index 0000000..940618b --- /dev/null +++ b/webkit/glue/cpp_binding_example.h @@ -0,0 +1,100 @@ +// Copyright 2008, 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. + +/* + CppBindingExample class: + This provides an example of how to use the CppBoundClass to create methods + and properties that can be exposed to JavaScript by an appropriately built + embedding client. It is also used by the CppBoundClass unit test. + + Typically, a class intended to be bound to JavaScript will define a + constructor, any methods and properties to be exposed, and optionally a + destructor. An embedding client can then bind the class to a JavaScript + object in a frame's window using the CppBoundClass::BindToJavascript() method, + generally called from the WebView delegate's WindowObjectCleared(). + + Once this class has been bound, say to the name "example", it might be called + from JavaScript in the following way: + + <script> + if (window.example) { + document.writeln(example.echoValue(false)); + document.writeln(example.echoType("Hello world!")); + document.writeln(example.plus(2, 3.1)); + + example.my_value = 15; + example.my_other_value = 2.1; + document.writeln(example.plus(example.my_value, example.my_other_value)); + } + </script> +*/ + +#ifndef CPP_BINDING_EXAMPLE_H__ +#define CPP_BINDING_EXAMPLE_H__ + +#include "webkit/glue/cpp_bound_class.h" + +class CppBindingExample : public CppBoundClass { + public: + // The default constructor initializes the property and method lists needed + // to bind this class to a JS object. + CppBindingExample(); + + // + // These public member variables and methods implement the methods and + // properties that will be exposed to JavaScript. If needed, the class could + // also contain other methods or variables, which will be hidden from JS + // as long as they're not mapped in the property and method lists created in + // the constructor. + // + // The signatures of any methods to be bound must match + // CppBoundClass::Callback. + // + + // Returns the value that was passed in as its first (only) argument. + void echoValue(const CppArgumentList& args, CppVariant* result); + + // Returns a hard-coded value of the same type (bool, number (double), + // string, or null) that was passed in as an argument. + void echoType(const CppArgumentList& args, CppVariant* result); + + // Returns the sum of the (first) two arguments as a double, if they are both + // numbers (integers or doubles). Otherwise returns null. + void plus(const CppArgumentList& args, CppVariant* result); + + // Invoked when a nonexistent method is called on this example object, this + // prints an error message. + void fallbackMethod(const CppArgumentList& args, CppVariant* result); + + // These properties will also be exposed to JavaScript. + CppVariant my_value; + CppVariant my_other_value; +}; + +#endif // CPP_BINDING_EXAMPLE_H__ diff --git a/webkit/glue/cpp_bound_class.cc b/webkit/glue/cpp_bound_class.cc new file mode 100644 index 0000000..66244b5 --- /dev/null +++ b/webkit/glue/cpp_bound_class.cc @@ -0,0 +1,279 @@ +// Copyright 2008, 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. + +// This file contains definitions for CppBoundClass + +// Here's the control flow of a JS method getting forwarded to a class. +// - Something calls our NPObject with a function like "Invoke". +// - CppNPObject's static invoke() function forwards it to its attached +// CppBoundClass's Invoke() method. +// - CppBoundClass has then overridden Invoke() to look up the function +// name in its internal map of methods, and then calls the appropriate +// method. + +#include "webkit/glue/cpp_bound_class.h" + +#include "config.h" +#include "webkit/glue/webframe.h" + +// This is required for the KJS build due to an artifact of the +// npruntime_priv.h file from JavaScriptCore/bindings. +#pragma warning(disable:4067) +#include "npruntime_priv.h" + +#if USE(JAVASCRIPTCORE_BINDINGS) +#pragma warning(push, 0) +#include "JSLock.h" +#pragma warning(pop) +#endif + +// Our special NPObject type. We extend an NPObject with a pointer to a +// CppBoundClass, which is just a C++ interface that we forward all NPObject +// callbacks to. +struct CppNPObject { + NPObject parent; // This must be the first field in the struct. + CppBoundClass* bound_class; + + // + // All following objects and functions are static, and just used to interface + // with NPObject/NPClass. + // + + // An NPClass associates static functions of CppNPObject with the + // function pointers used by the JS runtime. + static NPClass np_class_; + + // Allocate a new NPObject with the specified class. + static NPObject* allocate(NPP npp, NPClass* aClass); + + // Free an object. + static void deallocate(NPObject* obj); + + // Returns true if the C++ class associated with this NPObject exposes the + // given property. Called by the JS runtime. + static bool hasProperty(NPObject *obj, NPIdentifier ident); + + // Returns true if the C++ class associated with this NPObject exposes the + // given method. Called by the JS runtime. + static bool hasMethod(NPObject *obj, NPIdentifier ident); + + // If the given method is exposed by the C++ class associated with this + // NPObject, invokes it with the given args and returns a result. Otherwise, + // returns "undefined" (in the JavaScript sense). Called by the JS runtime. + static bool invoke(NPObject *obj, NPIdentifier ident, + const NPVariant *args, uint32_t arg_count, + NPVariant *result); + + // If the given property is exposed by the C++ class associated with this + // NPObject, returns its value. Otherwise, returns "undefined" (in the + // JavaScript sense). Called by the JS runtime. + static bool getProperty(NPObject *obj, NPIdentifier ident, + NPVariant *result); + + // If the given property is exposed by the C++ class associated with this + // NPObject, sets its value. Otherwise, does nothing. Called by the JS + // runtime. + static bool setProperty(NPObject *obj, NPIdentifier ident, + const NPVariant *value); +}; + +// Build CppNPObject's static function pointers into an NPClass, for use +// in constructing NPObjects for the C++ classes. +NPClass CppNPObject::np_class_ = { + NP_CLASS_STRUCT_VERSION, + CppNPObject::allocate, + CppNPObject::deallocate, + /* NPInvalidateFunctionPtr */ NULL, + CppNPObject::hasMethod, + CppNPObject::invoke, + /* NPInvokeDefaultFunctionPtr */ NULL, + CppNPObject::hasProperty, + CppNPObject::getProperty, + CppNPObject::setProperty, + /* NPRemovePropertyFunctionPtr */ NULL +}; + +/* static */ NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) { + CppNPObject* obj = new CppNPObject; + // obj->parent will be initialized by the NPObject code calling this. + obj->bound_class = NULL; + return &obj->parent; +} + +/* static */ void CppNPObject::deallocate(NPObject* np_obj) { + CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); + delete obj; +} + +/* static */ bool CppNPObject::hasMethod(NPObject* np_obj, + NPIdentifier ident) { + CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); + return obj->bound_class->HasMethod(ident); +} + +/* static */ bool CppNPObject::hasProperty(NPObject* np_obj, + NPIdentifier ident) { + CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); + return obj->bound_class->HasProperty(ident); +} + +/* static */ bool CppNPObject::invoke(NPObject* np_obj, NPIdentifier ident, + const NPVariant* args, uint32_t arg_count, + NPVariant* result) { + CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); + return obj->bound_class->Invoke(ident, args, arg_count, result); +} + +/* static */ bool CppNPObject::getProperty(NPObject* np_obj, + NPIdentifier ident, + NPVariant* result) { + CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); + return obj->bound_class->GetProperty(ident, result); +} + +/* static */ bool CppNPObject::setProperty(NPObject* np_obj, + NPIdentifier ident, + const NPVariant* value) { + CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); + return obj->bound_class->SetProperty(ident, value); +} + +CppBoundClass::~CppBoundClass() { + for (MethodList::iterator i = methods_.begin(); i != methods_.end(); ++i) + delete i->second; + + // Unregister objects we created and bound to a frame. + for (BoundObjectList::iterator i = bound_objects_.begin(); + i != bound_objects_.end(); ++i) { +#if USE(V8_BINDING) + _NPN_UnregisterObject(*i); +#endif + NPN_ReleaseObject(*i); + } +} + +bool CppBoundClass::HasMethod(NPIdentifier ident) { + return (methods_.find(ident) != methods_.end()); +} + +bool CppBoundClass::HasProperty(NPIdentifier ident) { + return (properties_.find(ident) != properties_.end()); +} + +bool CppBoundClass::Invoke(NPIdentifier ident, + const NPVariant* args, + size_t arg_count, + NPVariant* result) { + MethodList::const_iterator method = methods_.find(ident); + Callback* callback; + if (method == methods_.end()) { + if (fallback_callback_.get()) { + callback = fallback_callback_.get(); + } else { + VOID_TO_NPVARIANT(*result); + return false; + } + } else { + callback = (*method).second; + } + + // Build a CppArgumentList argument vector from the NPVariants coming in. + CppArgumentList cpp_args(arg_count); + for (size_t i = 0; i < arg_count; i++) + cpp_args[i].Set(args[i]); + + CppVariant cpp_result; + callback->Run(cpp_args, &cpp_result); + + cpp_result.CopyToNPVariant(result); + return true; +} + +bool CppBoundClass::GetProperty(NPIdentifier ident, NPVariant* result) { + PropertyList::const_iterator prop = properties_.find(ident); + if (prop == properties_.end()) { + VOID_TO_NPVARIANT(*result); + return false; + } + + const CppVariant* cpp_value = (*prop).second; + cpp_value->CopyToNPVariant(result); + return true; +} + +bool CppBoundClass::SetProperty(NPIdentifier ident, + const NPVariant* value) { + PropertyList::iterator prop = properties_.find(ident); + if (prop == properties_.end()) + return false; + + (*prop).second->Set(*value); + return true; +} + +void CppBoundClass::BindCallback(std::string name, Callback* callback) { + // NPUTF8 is a typedef for char, so this cast is safe. + NPIdentifier ident = NPN_GetStringIdentifier((const NPUTF8*)name.c_str()); + MethodList::iterator old_callback = methods_.find(ident); + if (old_callback != methods_.end()) + delete old_callback->second; + methods_[ident] = callback; +} + +void CppBoundClass::BindProperty(std::string name, CppVariant* prop) { + // NPUTF8 is a typedef for char, so this cast is safe. + NPIdentifier ident = NPN_GetStringIdentifier((const NPUTF8*)name.c_str()); + properties_[ident] = prop; +} + +bool CppBoundClass::IsMethodRegistered(std::string name) { + // NPUTF8 is a typedef for char, so this cast is safe. + NPIdentifier ident = NPN_GetStringIdentifier((const NPUTF8*)name.c_str()); + MethodList::iterator callback = methods_.find(ident); + return (callback != methods_.end()); +} + +void CppBoundClass::BindToJavascript(WebFrame* frame, + const std::wstring& classname) { +#if USE(JAVASCRIPTCORE_BINDINGS) + KJS::JSLock lock; +#endif + + // Create an NPObject using our static NPClass. The first argument (a + // plugin's instance handle) is passed through to the allocate function + // directly, and we don't use it, so it's ok to be 0. + NPObject* np_obj = NPN_CreateObject(0, &CppNPObject::np_class_); + CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj); + obj->bound_class = this; + + // BindToWindowObject will (indirectly) retain the np_object. We save it + // so we can release it when we're destroyed. + frame->BindToWindowObject(classname, np_obj); + bound_objects_.push_back(np_obj); +} diff --git a/webkit/glue/cpp_bound_class.h b/webkit/glue/cpp_bound_class.h new file mode 100644 index 0000000..56b4b8e --- /dev/null +++ b/webkit/glue/cpp_bound_class.h @@ -0,0 +1,164 @@ +// Copyright 2008, 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. + +/* + CppBoundClass class: + This base class serves as a parent for C++ classes designed to be bound to + JavaScript objects. + + Subclasses should define the constructor to build the property and method + lists needed to bind this class to a JS object. They should also declare + and define member variables and methods to be exposed to JS through + that object. + + See cpp_binding_example.{h|cc} for an example. +*/ + +#ifndef WEBKIT_GLUE_CPP_BOUNDCLASS_H__ +#define WEBKIT_GLUE_CPP_BOUNDCLASS_H__ + +#include <map> +#include <vector> + +#include "webkit/glue/cpp_variant.h" + +#include "base/task.h" + +class WebFrame; + +typedef std::vector<const CppVariant> CppArgumentList; + +// CppBoundClass lets you map Javascript method calls and property accesses +// directly to C++ method calls and CppVariant* variable access. +class CppBoundClass { + public: + // The constructor should call BindMethod, BindProperty, and + // SetFallbackMethod as needed to set up the methods, properties, and + // fallback method. + CppBoundClass() { } + virtual ~CppBoundClass(); + + // Given a WebFrame, BindToJavascript builds the NPObject that will represent + // the class and binds it to the frame's window under the given name. This + // should generally be called from the WebView delegate's + // WindowObjectCleared(). A class so bound will be accessible to JavaScript + // as window.<classname>. The owner of the CppBoundObject is responsible for + // keeping the object around while the frame is alive, and for destroying it + // afterwards. + void BindToJavascript(WebFrame* frame, const std::wstring& classname); + + // The type of callbacks. + typedef Callback2<const CppArgumentList&, CppVariant*>::Type Callback; + + // Used by a test. Returns true if a method with name |name| exists, + // regardless of whether a fallback is registered. + bool IsMethodRegistered(std::string name); + + protected: + // Bind the Javascript method called |name| to the C++ callback |callback|. + void BindCallback(std::string name, Callback* callback); + + // A wrapper for BindCallback, to simplify the common case of binding a + // method on the current object. Though not verified here, |method| + // must be a method of this CppBoundClass subclass. + template<typename T> + void BindMethod(std::string name, + void (T::*method)(const CppArgumentList&, CppVariant*)) { + Callback* callback = + NewCallback<T, const CppArgumentList&, CppVariant*>( + static_cast<T*>(this), method); + BindCallback(name, callback); + } + + // Bind the Javascript property called |name| to a CppVariant |prop|. + void BindProperty(std::string name, CppVariant* prop); + + // Set the fallback callback, which is called when when a callback is + // invoked that isn't bound. + // If it is NULL (its default value), a JavaScript exception is thrown in + // that case (as normally expected). If non NULL, the fallback method is + // invoked and the script continues its execution. + // Passing NULL for |callback| clears out any existing binding. + // It is used for tests and should probably only be used in such cases + // as it may cause unexpected behaviors (a JavaScript object with a + // fallback always returns true when checked for a method's + // existence). + void BindFallbackCallback(Callback* fallback_callback) { + fallback_callback_.reset(fallback_callback); + } + + // A wrapper for BindFallbackCallback, to simplify the common case of + // binding a method on the current object. Though not verified here, + // |method| must be a method of this CppBoundClass subclass. + // Passing NULL for |method| clears out any existing binding. + template<typename T> + void BindFallbackMethod( + void (T::*method)(const CppArgumentList&, CppVariant*)) { + if (method) { + Callback* callback = + NewCallback<T, const CppArgumentList&, CppVariant*>( + static_cast<T*>(this), method); + BindFallbackCallback(callback); + } else { + BindFallbackCallback(NULL); + } + } + + // Some fields are protected because some tests depend on accessing them, + // but otherwise they should be considered private. + + typedef std::map<NPIdentifier, CppVariant*> PropertyList; + typedef std::map<NPIdentifier, Callback*> MethodList; + // These maps associate names with property and method pointers to be + // exposed to JavaScript. + PropertyList properties_; + MethodList methods_; + + // The callback gets invoked when a call is made to an nonexistent method. + scoped_ptr<Callback> fallback_callback_; + + private: + // NPObject callbacks. + friend struct CppNPObject; + bool HasMethod(NPIdentifier ident); + bool Invoke(NPIdentifier ident, const NPVariant* args, size_t arg_count, + NPVariant* result); + bool HasProperty(NPIdentifier ident); + bool GetProperty(NPIdentifier ident, NPVariant* result); + bool SetProperty(NPIdentifier ident, const NPVariant* value); + + // A list of all NPObjects we created and bound in BindToJavascript(), so we + // can clean them up when we're destroyed. + typedef std::vector<NPObject*> BoundObjectList; + BoundObjectList bound_objects_; + + DISALLOW_EVIL_CONSTRUCTORS(CppBoundClass); +}; + +#endif // CPP_BOUNDCLASS_H__ diff --git a/webkit/glue/cpp_bound_class_unittest.cc b/webkit/glue/cpp_bound_class_unittest.cc new file mode 100644 index 0000000..c47131aa --- /dev/null +++ b/webkit/glue/cpp_bound_class_unittest.cc @@ -0,0 +1,267 @@ +// Copyright 2008, 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. + +// Tests for CppBoundClass, in conjunction with CppBindingExample. Binds +// a CppBindingExample class into JavaScript in a custom test shell and tests +// the binding from the outside by loading JS into the shell. + +#include <vector> + +#include "base/message_loop.h" +#include "webkit/glue/cpp_binding_example.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webview.h" +#include "webkit/tools/test_shell/test_shell_test.h" + +namespace { + +class CppBindingExampleWithOptionalFallback : public CppBindingExample { + public: + CppBindingExampleWithOptionalFallback() { + } + + void set_fallback_method_enabled(bool state) { + BindFallbackMethod(state ? + &CppBindingExampleWithOptionalFallback::fallbackMethod + : NULL); + } + + // The fallback method does nothing, but because of it the JavaScript keeps + // running when a nonexistent method is called on an object. + void fallbackMethod(const CppArgumentList& args, + CppVariant* result) { + } +}; + +class ExampleTestShell : public TestShell { + public: + + ExampleTestShell(bool use_fallback_method) { + example_bound_class_.set_fallback_method_enabled(use_fallback_method); + } + + // When called by WebViewDelegate::WindowObjectCleared method, this binds a + // CppExampleObject to window.example. + virtual void BindJSObjectsToWindow(WebFrame* frame) { + example_bound_class_.BindToJavascript(frame, L"example"); + // We use the layoutTestController binding for notifyDone. + TestShell::BindJSObjectsToWindow(frame); + } + + // This is a public interface to TestShell's protected method, so it + // can be called by our CreateEmptyWindow. + bool PublicInitialize(const std::wstring& startingURL) { + return Initialize(startingURL); + } + + CppBindingExampleWithOptionalFallback example_bound_class_; +}; + +class CppBoundClassTest : public TestShellTest { + protected: + // Adapted from TestShell::CreateNewWindow, this creates an + // ExampleTestShellWindow rather than a regular TestShell. + virtual void CreateEmptyWindow() { + ExampleTestShell* host = new ExampleTestShell(useFallback()); + ASSERT_TRUE(host != NULL); + bool rv = host->PublicInitialize(L"about:blank"); + if (rv) { + test_shell_ = host; + TestShell::windowList()->push_back(host->mainWnd()); + webframe_ = test_shell_->webView()->GetMainFrame(); + ASSERT_TRUE(webframe_ != NULL); + } else { + delete host; + } + } + + // Wraps the given JavaScript snippet in <html><body><script> tags, then + // loads it into a webframe so it is executed. + void ExecuteJavaScript(const std::string& javascript) { + std::string html = "<html><body>"; + html.append(TestShellTest::kJavascriptDelayExitScript); + html.append("<script>"); + html.append(javascript); + html.append("</script></body></html>"); + // The base URL doesn't matter. + webframe_->LoadHTMLString(html, GURL("about:blank")); + + test_shell_->WaitTestFinished(); + } + + // Executes the specified JavaScript and checks to be sure that the resulting + // document text is exactly "SUCCESS". + void CheckJavaScriptSuccess(const std::string& javascript) { + ExecuteJavaScript(javascript); + EXPECT_EQ(L"SUCCESS", webkit_glue::DumpDocumentText(webframe_)); + } + + // Executes the specified JavaScript and checks that the resulting document + // text is empty. + void CheckJavaScriptFailure(const std::string& javascript) { + ExecuteJavaScript(javascript); + EXPECT_EQ(L"", webkit_glue::DumpDocumentText(webframe_)); + } + + // Constructs a JavaScript snippet that evaluates and compares the left and + // right expressions, printing "SUCCESS" to the page if they are equal and + // printing their actual values if they are not. Any strings in the + // expressions should be enclosed in single quotes, and no double quotes + // should appear in either expression (even if escaped). (If a test case + // is added that needs fancier quoting, Json::valueToQuotedString could be + // used here. For now, it's not worth adding the dependency.) + std::string BuildJSCondition(std::string left, std::string right) { + return "var leftval = " + left + ";" + + "var rightval = " + right + ";" + + "if (leftval == rightval) {" + + " document.writeln('SUCCESS');" + + "} else {" + + " document.writeln(\"" + + left + " [\" + leftval + \"] != " + + right + " [\" + rightval + \"]\");" + + "}"; + } + +protected: + virtual bool useFallback() { + return false; + } + +private: + WebFrame* webframe_; +}; + +class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest { +protected: + virtual bool useFallback() { + return true; + } +}; + +} // namespace + +// Ensures that the example object has been bound to JS. +TEST_F(CppBoundClassTest, ObjectExists) { + std::string js = BuildJSCondition("typeof window.example", "'object'"); + CheckJavaScriptSuccess(js); + + // An additional check to test our test. + js = BuildJSCondition("typeof window.invalid_object", "'undefined'"); + CheckJavaScriptSuccess(js); +} + +TEST_F(CppBoundClassTest, PropertiesAreInitialized) { + std::string js = BuildJSCondition("example.my_value", "10"); + CheckJavaScriptSuccess(js); + + js = BuildJSCondition("example.my_other_value", "'Reinitialized!'"); + CheckJavaScriptSuccess(js); +} + +TEST_F(CppBoundClassTest, SetAndGetProperties) { + // The property on the left will be set to the value on the right, then + // checked to make sure it holds that same value. + static const std::string tests[] = { + "example.my_value", "7", + "example.my_value", "'test'", + "example.my_other_value", "3.14", + "example.my_other_value", "false", + "" // Array end marker: insert additional test pairs before this. + }; + + for (int i = 0; tests[i] != ""; i += 2) { + std::string left = tests[i]; + std::string right = tests[i + 1]; + // left = right; + std::string js = left; + js.append(" = "); + js.append(right); + js.append(";"); + js.append(BuildJSCondition(left, right)); + CheckJavaScriptSuccess(js); + } +} + +TEST_F(CppBoundClassTest, InvokeMethods) { + // The expression on the left is expected to return the value on the right. + static const std::string tests[] = { + "example.echoValue(true)", "true", + "example.echoValue(13)", "13", + "example.echoValue(2.718)", "2.718", + "example.echoValue('yes')", "'yes'", + "example.echoValue()", "null", // Too few arguments + + "example.echoType(false)", "true", + "example.echoType(19)", "3.14159", + "example.echoType(9.876)", "3.14159", + "example.echoType('test string')", "'Success!'", + "example.echoType()", "null", // Too few arguments + + // Comparing floats that aren't integer-valued is usually problematic due + // to rounding, but exact powers of 2 should also be safe. + "example.plus(2.5, 18.0)", "20.5", + "example.plus(2, 3.25)", "5.25", + "example.plus(2, 3)", "5", + "example.plus()", "null", // Too few arguments + "example.plus(1)", "null", // Too few arguments + "example.plus(1, 'test')", "null", // Wrong argument type + "example.plus('test', 2)", "null", // Wrong argument type + "example.plus('one', 'two')", "null", // Wrong argument type + "" // Array end marker: insert additional test pairs before this. + }; + + for (int i = 0; tests[i] != ""; i+= 2) { + std::string left = tests[i]; + std::string right = tests[i + 1]; + std::string js = BuildJSCondition(left, right); + CheckJavaScriptSuccess(js); + } + + std::string js = "example.my_value = 3.25; example.my_other_value = 1.25;"; + js.append(BuildJSCondition( + "example.plus(example.my_value, example.my_other_value)", "4.5")); + CheckJavaScriptSuccess(js); +} + +// Tests that invoking a nonexistent method with no fallback method stops the +// script's execution +TEST_F(CppBoundClassTest, + InvokeNonexistentMethodNoFallback) { + std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');"; + CheckJavaScriptFailure(js); +} + +// Ensures existent methods can be invoked successfully when the fallback method +// is used +TEST_F(CppBoundClassWithFallbackMethodTest, + InvokeExistentMethodsWithFallback) { + std::string js = BuildJSCondition("example.echoValue(34)", "34"); + CheckJavaScriptSuccess(js); +} diff --git a/webkit/glue/cpp_variant.cc b/webkit/glue/cpp_variant.cc new file mode 100644 index 0000000..0fb43e1 --- /dev/null +++ b/webkit/glue/cpp_variant.cc @@ -0,0 +1,276 @@ +// Copyright 2008, 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. + +// This file contains definitions for CppVariant. + +#include <limits> +#include "config.h" +#include "webkit/glue/cpp_variant.h" +#include "base/logging.h" +#include "base/string_util.h" + +#include "npruntime_priv.h" // for NPN_InitializeVariantWithStringCopy + +#if USE(JAVASCRIPTCORE_BINDINGS) +#define _NPN_InitializeVariantWithStringCopy NPN_InitializeVariantWithStringCopy +#endif + +CppVariant::CppVariant() { + type = NPVariantType_Null; +} + +// Note that Set() performs a deep copy, which is necessary to safely +// call FreeData() on the value in the destructor. +CppVariant::CppVariant(const CppVariant& original) { + type = NPVariantType_Null; + Set(original); +} + +// See comment for copy constructor, above. +CppVariant& CppVariant::operator=(const CppVariant& original) { + if (&original != this) + Set(original); + return *this; +} + +CppVariant::~CppVariant() { + FreeData(); +} + +void CppVariant::FreeData() { + NPN_ReleaseVariantValue(this); +} + +bool CppVariant::isEqual(const CppVariant& other) const { + if (type != other.type) + return false; + + switch (type) { + case NPVariantType_Bool: { + return (value.boolValue == other.value.boolValue); + } + case NPVariantType_Int32: { + return (value.intValue == other.value.intValue); + } + case NPVariantType_Double: { + return (value.doubleValue == other.value.doubleValue); + } + case NPVariantType_String: { + const NPString *this_value = &value.stringValue; + const NPString *other_value = &other.value.stringValue; + uint32_t len = this_value->UTF8Length; + return (len == other_value->UTF8Length && + !strncmp(this_value->UTF8Characters, other_value->UTF8Characters, + len)); + } + case NPVariantType_Null: + case NPVariantType_Void: { + return true; + } + case NPVariantType_Object: { + NPObject *this_value = value.objectValue; + NPObject *other_value = other.value.objectValue; + return (this_value->_class == other_value->_class && + this_value->referenceCount == other_value->referenceCount); + } + } + return false; +} + +void CppVariant::CopyToNPVariant(NPVariant* result) const { + result->type = type; + switch (type) { + case NPVariantType_Bool: + result->value.boolValue = value.boolValue; + break; + case NPVariantType_Int32: + result->value.intValue = value.intValue; + break; + case NPVariantType_Double: + result->value.doubleValue = value.doubleValue; + break; + case NPVariantType_String: + _NPN_InitializeVariantWithStringCopy(result, &value.stringValue); + break; + case NPVariantType_Null: + case NPVariantType_Void: + // Nothing to set. + break; + case NPVariantType_Object: + result->type = NPVariantType_Object; + result->value.objectValue = NPN_RetainObject(value.objectValue); + break; + } +} + +void CppVariant::Set(const NPVariant& new_value) { + FreeData(); + switch (new_value.type) { + case NPVariantType_Bool: + Set(new_value.value.boolValue); + break; + case NPVariantType_Int32: + Set(new_value.value.intValue); + break; + case NPVariantType_Double: + Set(new_value.value.doubleValue); + break; + case NPVariantType_String: + Set(new_value.value.stringValue); + break; + case NPVariantType_Null: + case NPVariantType_Void: + type = new_value.type; + break; + case NPVariantType_Object: + Set(new_value.value.objectValue); + break; + } +} + +void CppVariant::SetNull() { + FreeData(); + type = NPVariantType_Null; +} + +void CppVariant::Set(bool new_value) { + FreeData(); + type = NPVariantType_Bool; + value.boolValue = new_value; +} + +void CppVariant::Set(int32_t new_value) { + FreeData(); + type = NPVariantType_Int32; + value.intValue = new_value; +} + +void CppVariant::Set(double new_value) { + FreeData(); + type = NPVariantType_Double; + value.doubleValue = new_value; +} + +// The new_value must be a null-terminated string. +void CppVariant::Set(const char* new_value) { + FreeData(); + type = NPVariantType_String; + NPString new_string = {new_value, + static_cast<uint32_t>(strlen(new_value))}; + _NPN_InitializeVariantWithStringCopy(this, &new_string); +} + +void CppVariant::Set(const std::string& new_value) { + FreeData(); + type = NPVariantType_String; + NPString new_string = {new_value.data(), + static_cast<uint32_t>(new_value.size())}; + _NPN_InitializeVariantWithStringCopy(this, &new_string); +} +void CppVariant::Set(const NPString& new_value) { + FreeData(); + type = NPVariantType_String; + _NPN_InitializeVariantWithStringCopy(this, &new_value); +} + +void CppVariant::Set(NPObject* new_value) { + FreeData(); + type = NPVariantType_Object; + value.objectValue = NPN_RetainObject(new_value); +} + +std::string CppVariant::ToString() const { + DCHECK(isString()); + return std::string(value.stringValue.UTF8Characters, + value.stringValue.UTF8Length); +} + +int32_t CppVariant::ToInt32() const { + if (isInt32()) { + return value.intValue; + } else if (isDouble()) { + return static_cast<int32_t>(value.doubleValue); + } else { + NOTREACHED(); + return 0; + } +} + +double CppVariant::ToDouble() const { + if (isInt32()) { + return static_cast<double>(value.intValue); + } else if (isDouble()) { + return value.doubleValue; + } else { + NOTREACHED(); + return 0.0; + } +} + +bool CppVariant::ToBoolean() const { + DCHECK(isBool()); + return value.boolValue; +} + +std::vector<std::wstring> CppVariant::ToStringVector() const { + + DCHECK(isObject()); + std::vector<std::wstring> wstring_vector; + NPObject* np_value = value.objectValue; + NPIdentifier length_id = NPN_GetStringIdentifier("length"); + + if (NPN_HasProperty(NULL, np_value, length_id)) { + NPVariant length_value; + if (NPN_GetProperty(NULL, np_value, length_id, &length_value)) { + int length = 0; + // The length is a double in some cases. + if (NPVARIANT_IS_DOUBLE(length_value)) + length = static_cast<int>(NPVARIANT_TO_DOUBLE(length_value)); + else if (NPVARIANT_IS_INT32(length_value)) + length = NPVARIANT_TO_INT32(length_value); + // For sanity, only allow 100 items. + length = std::min(100, length); + for (int i = 0; i < length; ++i) { + // Get each of the items. + std::string index = StringPrintf("%d", i); + NPIdentifier index_id = NPN_GetStringIdentifier(index.c_str()); + if (NPN_HasProperty(NULL, np_value, index_id)) { + NPVariant index_value; + if (NPN_GetProperty(NULL, np_value, index_id, &index_value) && + NPVARIANT_IS_STRING(index_value)) { + std::string string(NPVARIANT_TO_STRING(index_value).UTF8Characters, + NPVARIANT_TO_STRING(index_value).UTF8Length); + wstring_vector.push_back(UTF8ToWide(string)); + } + } + } + } + } + return wstring_vector; +} diff --git a/webkit/glue/cpp_variant.h b/webkit/glue/cpp_variant.h new file mode 100644 index 0000000..5e57a51 --- /dev/null +++ b/webkit/glue/cpp_variant.h @@ -0,0 +1,128 @@ +// Copyright 2008, 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. + +/* + This file contains the declaration for CppVariant, a type used by C++ classes + that are to be bound to JavaScript objects. + + CppVariant exists primarily as an interface between C++ callers and the + corresponding NPVariant type. CppVariant also provides a number of + convenience constructors and accessors, so that the NPVariantType values + don't need to be exposed, and a destructor to free any memory allocated for + string values. + + For a usage example, see cpp_binding_example.{h|cc}. +*/ + +#ifndef WEBKIT_GLUE_CPP_VARIANT_H__ +#define WEBKIT_GLUE_CPP_VARIANT_H__ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "bindings/npruntime.h" + +class CppVariant : public NPVariant { + public: + CppVariant(); + ~CppVariant(); + void SetNull(); + void Set(bool value); + void Set(int32_t value); + void Set(double value); + + // Note that setting a CppVariant to a string value involves copying the + // string data, which must be freed with a call to FreeData() when the + // CppVariant is set to a different value or is no longer needed. Normally + // this is handled by the other Set() methods and by the destructor. + void Set(const char* value); // Must be a null-terminated string. + void Set(const std::string& value); + void Set(const NPString& new_value); + void Set(const NPVariant& new_value); + + // Note that setting a CppVariant to an NPObject involves ref-counting + // the actual object. FreeData() should only be called if the CppVariant + // is no longer needed. The other Set() methods handle this internally. + // Also, the object's NPClass is expected to be a static object: neither + // the NP runtime nor CPPVariant will ever free it. + void Set(NPObject* new_value); + + // These three methods all perform deep copies of any string data. This + // allows local CppVariants to be released by the destructor without + // corrupting their sources. In performance-critical code, or when strings + // are very long, avoid creating new CppVariants. + // In case of NPObject as the data, the copying involves ref-counting + // as opposed to deep-copying. The ref-counting ensures that sources don't + // get corrupted when the copies get destroyed. + void CopyToNPVariant(NPVariant* result) const; + CppVariant& operator=(const CppVariant& original); + CppVariant(const CppVariant& original); + + // Calls NPN_ReleaseVariantValue, which frees any string data + // held by the object and sets its type to null. + // In case of NPObject, the NPN_ReleaseVariantValue decrements + // the ref-count (releases when ref-count becomes 0) + void FreeData(); + + // Compares this CppVariant's type and value to another's. They must be + // identical in both type and value to be considered equal. For string and + // object types, a deep comparison is performed; that is, the contents of the + // strings, or the classes and refcounts of the objects, must be the same, + // but they need not be the same pointers. + bool isEqual(const CppVariant& other) const; + + // The value of a CppVariant may be read directly from its NPVariant (but + // should only be set using one of the Set() methods above). Although the + // type of a CppVariant is likewise public, it can be accessed through these + // functions rather than directly if a caller wishes to avoid dependence on + // the NPVariantType values. + bool isBool() const { return (type == NPVariantType_Bool); } + bool isInt32() const { return (type == NPVariantType_Int32); } + bool isDouble() const { return (type == NPVariantType_Double); } + bool isNumber() const { return (isInt32() || isDouble()); } + bool isString() const { return (type == NPVariantType_String); } + bool isVoid() const { return (type == NPVariantType_Void); } + bool isNull() const { return (type == NPVariantType_Null); } + bool isEmpty() const { return (isVoid() || isNull()); } + bool isObject() const { return (type == NPVariantType_Object); } + + // Converters. The CppVariant must be of a type convertible to these values. + // For example, ToInteger() works only if isNumber() is true. + std::string ToString() const; + int32_t ToInt32() const; + double ToDouble() const; + bool ToBoolean() const; + // Returns a vector of strings for the specified argument. This is useful + // for converting a JavaScript array of strings into a vector of strings. + std::vector<std::wstring> ToStringVector() const; + +}; + +#endif // WEBKIT_GLUE_CPP_VARIANT_H__ diff --git a/webkit/glue/cpp_variant_unittest.cc b/webkit/glue/cpp_variant_unittest.cc new file mode 100644 index 0000000..bc5f7b7 --- /dev/null +++ b/webkit/glue/cpp_variant_unittest.cc @@ -0,0 +1,457 @@ +// Copyright 2008, 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 "webkit/glue/cpp_variant.h" +#include "testing/gtest/include/gtest/gtest.h" + +#pragma warning(push, 0) +#include "npruntime_priv.h" // for NPN_InitializeVariantWithStringCopy +#pragma warning(pop) + +#if USE(JAVASCRIPTCORE_BINDINGS) +#define _NPN_InitializeVariantWithStringCopy NPN_InitializeVariantWithStringCopy +#endif + +// Creates a std::string from an NPVariant of string type. If the NPVariant +// is not a string, empties the std::string. +void MakeStdString(const NPVariant& np, std::string* std_string) { + if (np.type == NPVariantType_String) { + const char* chars = + reinterpret_cast<const char*>(np.value.stringValue.UTF8Characters); + (*std_string).assign(chars, np.value.stringValue.UTF8Length); + } else { + (*std_string).clear(); + } +} + +// Verifies that the actual NPVariant is a string and that its value matches +// the expected_str. +void CheckString(const std::string& expected_str, const NPVariant& actual) { + EXPECT_EQ(NPVariantType_String, actual.type); + std::string actual_str; + MakeStdString(actual, &actual_str); + EXPECT_EQ(expected_str, actual_str); +} + +// Verifies that both the actual and the expected NPVariants are strings and +// that their values match. +void CheckString(const NPVariant& expected, const NPVariant& actual) { + EXPECT_EQ(NPVariantType_String, expected.type); + std::string expected_str; + MakeStdString(expected, &expected_str); + CheckString(expected_str, actual); +} + +int g_allocate_call_count = 0; +int g_deallocate_call_count = 0; + +void CheckObject(const NPVariant& actual) { + EXPECT_EQ(NPVariantType_Object, actual.type); + EXPECT_TRUE(actual.value.objectValue); + EXPECT_EQ(1, actual.value.objectValue->referenceCount); + EXPECT_EQ(1, g_allocate_call_count); + EXPECT_EQ(0, g_deallocate_call_count); +} + +NPObject* MockNPAllocate(NPP npp, NPClass* aClass) { + // This is a mock allocate method that mimics the behavior + // of NPN_CreateObject when allocate() is NULL + + ++g_allocate_call_count; + // Ignore npp and NPClass + return reinterpret_cast<NPObject*>(malloc(sizeof(NPObject))); +} + +void MockNPDeallocate(NPObject* npobj) { + // This is a mock deallocate method that mimics the behavior + // of NPN_DeallocateObject when deallocate() is NULL + + ++g_deallocate_call_count; + free(npobj); +} + +static NPClass void_class = { NP_CLASS_STRUCT_VERSION, + MockNPAllocate, + MockNPDeallocate, + 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +NPObject* MakeVoidObject() { + g_allocate_call_count = 0; + g_deallocate_call_count = 0; + return NPN_CreateObject(NULL, &void_class); +} + +TEST(CppVariantTest, NewVariantHasNullType) { + CppVariant value; + EXPECT_EQ(NPVariantType_Null, value.type); +} + +TEST(CppVariantTest, SetNullSetsType) { + CppVariant value; + value.Set(17); + value.SetNull(); + EXPECT_EQ(NPVariantType_Null, value.type); +} + +TEST(CppVariantTest, CopyConstructorDoesDeepCopy) { + CppVariant source; + source.Set("test string"); + CppVariant dest = source; + EXPECT_EQ(NPVariantType_String, dest.type); + EXPECT_EQ(NPVariantType_String, source.type); + + // Ensure that the string was copied, not just the pointer. + EXPECT_NE(source.value.stringValue.UTF8Characters, + dest.value.stringValue.UTF8Characters); + + CheckString(source, dest); +} + +TEST(CppVariantTest, CopyConstructorIncrementsRefCount) { + CppVariant source; + NPObject *object = MakeVoidObject(); + source.Set(object); + // 2 references so far. + EXPECT_EQ(2, source.value.objectValue->referenceCount); + + CppVariant dest = source; + EXPECT_EQ(3, dest.value.objectValue->referenceCount); + EXPECT_EQ(1, g_allocate_call_count); + NPN_ReleaseObject(object); + source.SetNull(); + CheckObject(dest); +} + +TEST(CppVariantTest, AssignmentDoesDeepCopy) { + CppVariant source; + source.Set("test string"); + CppVariant dest; + dest = source; + EXPECT_EQ(NPVariantType_String, dest.type); + EXPECT_EQ(NPVariantType_String, source.type); + + // Ensure that the string was copied, not just the pointer. + EXPECT_NE(source.value.stringValue.UTF8Characters, + dest.value.stringValue.UTF8Characters); + + CheckString(source, dest); +} + +TEST(CppVariantTest, AssignmentIncrementsRefCount) { + CppVariant source; + NPObject *object = MakeVoidObject(); + source.Set(object); + // 2 references so far. + EXPECT_EQ(2, source.value.objectValue->referenceCount); + + CppVariant dest; + dest = source; + EXPECT_EQ(3, dest.value.objectValue->referenceCount); + EXPECT_EQ(1, g_allocate_call_count); + + NPN_ReleaseObject(object); + source.SetNull(); + CheckObject(dest); +} + +TEST(CppVariantTest, DestroyingCopyDoesNotCorruptSource) { + CppVariant source; + source.Set("test string"); + std::string before; + MakeStdString(source, &before); + { + CppVariant dest = source; + } + CheckString(before, source); + + NPObject *object = MakeVoidObject(); + source.Set(object); + { + CppVariant dest2 = source; + } + NPN_ReleaseObject(object); + CheckObject(source); +} + +TEST(CppVariantTest, CopiesTypeAndValueToNPVariant) { + NPVariant np; + CppVariant cpp; + + cpp.Set(true); + cpp.CopyToNPVariant(&np); + EXPECT_EQ(cpp.type, np.type); + EXPECT_EQ(cpp.value.boolValue, np.value.boolValue); + NPN_ReleaseVariantValue(&np); + + cpp.Set(17); + cpp.CopyToNPVariant(&np); + EXPECT_EQ(cpp.type, np.type); + EXPECT_EQ(cpp.value.intValue, np.value.intValue); + NPN_ReleaseVariantValue(&np); + + cpp.Set(3.1415); + cpp.CopyToNPVariant(&np); + EXPECT_EQ(cpp.type, np.type); + EXPECT_EQ(cpp.value.doubleValue, np.value.doubleValue); + NPN_ReleaseVariantValue(&np); + + cpp.Set("test value"); + cpp.CopyToNPVariant(&np); + CheckString("test value", np); + NPN_ReleaseVariantValue(&np); + + cpp.SetNull(); + cpp.CopyToNPVariant(&np); + EXPECT_EQ(cpp.type, np.type); + NPN_ReleaseVariantValue(&np); + + NPObject *object = MakeVoidObject(); + cpp.Set(object); + cpp.CopyToNPVariant(&np); + NPN_ReleaseObject(object); + cpp.SetNull(); + CheckObject(np); + NPN_ReleaseVariantValue(&np); +} + +TEST(CppVariantTest, SetsTypeAndValueFromNPVariant) { + NPVariant np; + CppVariant cpp; + + VOID_TO_NPVARIANT(np); + cpp.Set(np); + EXPECT_EQ(np.type, cpp.type); + NPN_ReleaseVariantValue(&np); + + NULL_TO_NPVARIANT(np); + cpp.Set(np); + EXPECT_EQ(np.type, cpp.type); + NPN_ReleaseVariantValue(&np); + + BOOLEAN_TO_NPVARIANT(true, np); + cpp.Set(np); + EXPECT_EQ(np.type, cpp.type); + EXPECT_EQ(np.value.boolValue, cpp.value.boolValue); + NPN_ReleaseVariantValue(&np); + + INT32_TO_NPVARIANT(15, np); + cpp.Set(np); + EXPECT_EQ(np.type, cpp.type); + EXPECT_EQ(np.value.intValue, cpp.value.intValue); + NPN_ReleaseVariantValue(&np); + + DOUBLE_TO_NPVARIANT(2.71828, np); + cpp.Set(np); + EXPECT_EQ(np.type, cpp.type); + EXPECT_EQ(np.value.doubleValue, cpp.value.doubleValue); + NPN_ReleaseVariantValue(&np); + + NPString np_ascii_str = { "1st test value", + static_cast<uint32_t>(strlen("1st test value")) }; + _NPN_InitializeVariantWithStringCopy(&np, &np_ascii_str); + cpp.Set(np); + CheckString("1st test value", cpp); + NPN_ReleaseVariantValue(&np); + + // Test characters represented in 2/3/4 bytes in UTF-8 + // Greek alpha, Chinese number 1 (horizontal bar), + // Deseret letter (similar to 'O') + NPString np_intl_str = { "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84", + static_cast<uint32_t>(strlen( + "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84")) }; + _NPN_InitializeVariantWithStringCopy(&np, &np_intl_str); + cpp.Set(np); + CheckString("\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84", cpp); + NPN_ReleaseVariantValue(&np); + + NPObject *obj = MakeVoidObject(); + OBJECT_TO_NPVARIANT(obj, np); // Doesn't make a copy. + cpp.Set(np); + NPN_ReleaseVariantValue(&np); // or NPN_ReleaseObject but NOT both + CheckObject(cpp); +} + +TEST(CppVariantTest, SetsSimpleTypesAndValues) { + CppVariant cpp; + cpp.Set(true); + EXPECT_EQ(NPVariantType_Bool, cpp.type); + EXPECT_EQ(true, cpp.value.boolValue); + + cpp.Set(5); + EXPECT_EQ(NPVariantType_Int32, cpp.type); + EXPECT_EQ(5, cpp.value.intValue); + + cpp.Set(1.234); + EXPECT_EQ(NPVariantType_Double, cpp.type); + EXPECT_EQ(1.234, cpp.value.doubleValue); + + // C string + cpp.Set("1st test string"); + CheckString("1st test string", cpp); + + // std::string + std::string source("std test string"); + cpp.Set(source); + CheckString("std test string", cpp); + + // NPString + const char *ascii_str = "test NPString"; + NPString np_ascii_str = { "test NPString", + static_cast<uint32_t>(strlen("test NPString")) }; + cpp.Set(np_ascii_str); + std::string expected("test NPString"); + CheckString(expected, cpp); + + // Test characters represented in 2/3/4 bytes in UTF-8 + // Greek alpha, Chinese number 1 (horizontal bar), + // Deseret letter (similar to 'O') + NPString np_intl_str = { "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84", + static_cast<uint32_t>(strlen( + "\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84")) }; + cpp.Set(np_intl_str); + expected = std::string("\xce\xb1\xe4\xb8\x80\xf0\x90\x90\x84"); + CheckString(expected, cpp); + + NPObject* obj = MakeVoidObject(); + cpp.Set(obj); + NPN_ReleaseObject(obj); + CheckObject(cpp); +} + +TEST(CppVariantTest, FreeDataSetsToVoid) { + CppVariant cpp; + EXPECT_EQ(NPVariantType_Null, cpp.type); + cpp.Set(12); + EXPECT_EQ(NPVariantType_Int32, cpp.type); + cpp.FreeData(); + EXPECT_EQ(NPVariantType_Void, cpp.type); +} + +TEST(CppVariantTest, FreeDataReleasesObject) { + CppVariant cpp; + NPObject* object = MakeVoidObject(); + cpp.Set(object); + EXPECT_EQ(2, object->referenceCount); + cpp.FreeData(); + EXPECT_EQ(1, object->referenceCount); + EXPECT_EQ(0, g_deallocate_call_count); + + cpp.Set(object); + NPN_ReleaseObject(object); + EXPECT_EQ(0, g_deallocate_call_count); + cpp.FreeData(); + EXPECT_EQ(1, g_deallocate_call_count); +} + +TEST(CppVariantTest, IsTypeFunctionsWork) { + CppVariant cpp; + // These should not happen in practice, since voids are not supported + // This test must be first since it just clobbers internal data without + // releasing. + VOID_TO_NPVARIANT(cpp); + EXPECT_FALSE(cpp.isBool()); + EXPECT_FALSE(cpp.isInt32()); + EXPECT_FALSE(cpp.isDouble()); + EXPECT_FALSE(cpp.isNumber()); + EXPECT_FALSE(cpp.isString()); + EXPECT_TRUE(cpp.isVoid()); + EXPECT_FALSE(cpp.isNull()); + EXPECT_TRUE(cpp.isEmpty()); + + cpp.Set(true); + EXPECT_TRUE(cpp.isBool()); + EXPECT_FALSE(cpp.isInt32()); + EXPECT_FALSE(cpp.isDouble()); + EXPECT_FALSE(cpp.isNumber()); + EXPECT_FALSE(cpp.isString()); + EXPECT_FALSE(cpp.isVoid()); + EXPECT_FALSE(cpp.isNull()); + EXPECT_FALSE(cpp.isEmpty()); + EXPECT_FALSE(cpp.isObject()); + + cpp.Set(12); + EXPECT_FALSE(cpp.isBool()); + EXPECT_TRUE(cpp.isInt32()); + EXPECT_FALSE(cpp.isDouble()); + EXPECT_TRUE(cpp.isNumber()); + EXPECT_FALSE(cpp.isString()); + EXPECT_FALSE(cpp.isVoid()); + EXPECT_FALSE(cpp.isNull()); + EXPECT_FALSE(cpp.isEmpty()); + EXPECT_FALSE(cpp.isObject()); + + cpp.Set(3.1415); + EXPECT_FALSE(cpp.isBool()); + EXPECT_FALSE(cpp.isInt32()); + EXPECT_TRUE(cpp.isDouble()); + EXPECT_TRUE(cpp.isNumber()); + EXPECT_FALSE(cpp.isString()); + EXPECT_FALSE(cpp.isVoid()); + EXPECT_FALSE(cpp.isNull()); + EXPECT_FALSE(cpp.isEmpty()); + EXPECT_FALSE(cpp.isObject()); + + cpp.Set("a string"); + EXPECT_FALSE(cpp.isBool()); + EXPECT_FALSE(cpp.isInt32()); + EXPECT_FALSE(cpp.isDouble()); + EXPECT_FALSE(cpp.isNumber()); + EXPECT_TRUE(cpp.isString()); + EXPECT_FALSE(cpp.isVoid()); + EXPECT_FALSE(cpp.isNull()); + EXPECT_FALSE(cpp.isEmpty()); + EXPECT_FALSE(cpp.isObject()); + + cpp.SetNull(); + EXPECT_FALSE(cpp.isBool()); + EXPECT_FALSE(cpp.isInt32()); + EXPECT_FALSE(cpp.isDouble()); + EXPECT_FALSE(cpp.isNumber()); + EXPECT_FALSE(cpp.isString()); + EXPECT_FALSE(cpp.isVoid()); + EXPECT_TRUE(cpp.isNull()); + EXPECT_TRUE(cpp.isEmpty()); + EXPECT_FALSE(cpp.isObject()); + + NPObject *obj = MakeVoidObject(); + cpp.Set(obj); + EXPECT_FALSE(cpp.isBool()); + EXPECT_FALSE(cpp.isInt32()); + EXPECT_FALSE(cpp.isDouble()); + EXPECT_FALSE(cpp.isNumber()); + EXPECT_FALSE(cpp.isString()); + EXPECT_FALSE(cpp.isVoid()); + EXPECT_FALSE(cpp.isNull()); + EXPECT_FALSE(cpp.isEmpty()); + EXPECT_TRUE(cpp.isObject()); + NPN_ReleaseObject(obj); + CheckObject(cpp); +} diff --git a/webkit/glue/debugger.cc b/webkit/glue/debugger.cc new file mode 100644 index 0000000..469870a --- /dev/null +++ b/webkit/glue/debugger.cc @@ -0,0 +1,86 @@ +// Copyright 2008, 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" // webkit config for V8_BINDING +#include "base/string_util.h" +#include "webkit/glue/debugger.h" + +#if USE(V8_BINDING) +#define USING_V8 +#include "v8/public/debug.h" +#endif + +void V8DebugMessageHandler(const uint16_t* message, int length, void* data) { + std::wstring out(reinterpret_cast<const wchar_t*>(message), length); + reinterpret_cast<Debugger*>(data)->OutputLater(out); +} + +Debugger::Debugger(Delegate* del) : delegate_(del), attached_(false) { + delegate_loop_ = MessageLoop::current(); +} + +Debugger::~Debugger() { + DCHECK(!attached_); + Detach(); +} + +void Debugger::Attach() { +#ifdef USING_V8 + if (!attached_) { + attached_ = true; + v8::Debug::SetMessageHandler(V8DebugMessageHandler, this); + } +#endif +} + +void Debugger::Detach() { +#ifdef USING_V8 + if (attached_) { + attached_ = false; + v8::Debug::SetMessageHandler(NULL); + } +#endif +} + +void Debugger::OutputLater(const std::wstring& out) { + delegate_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Debugger::Output, out)); +} + +void Debugger::Output(const std::wstring& out) { + delegate_->DebuggerOutput(out); +} + +void Debugger::Command(const std::wstring& cmd) { +#ifdef USING_V8 + DCHECK(attached_); + v8::Debug::SendCommand(reinterpret_cast<const uint16_t*>(cmd.data()), + cmd.length()); +#endif +} diff --git a/webkit/glue/debugger.h b/webkit/glue/debugger.h new file mode 100644 index 0000000..cb10804 --- /dev/null +++ b/webkit/glue/debugger.h @@ -0,0 +1,84 @@ +// Copyright 2008, 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. + +// An interface to the V8 debugger. This is in WebKit in order to isolate +// the renderer from a direct V8 dependency. + +#ifndef WEBKIT_GLUE_DEBUGGER_H +#define WEBKIT_GLUE_DEBUGGER_H + +#include <string> + +#include "base/basictypes.h" +#include "base/message_loop.h" + +#include "v8/public/debug.h" + +void V8DebugMessageHandler(const uint16_t* message, int length, void* data); + +class Debugger : public base::RefCountedThreadSafe<Debugger> { +public: + class Delegate { + public: + virtual void DebuggerOutput(const std::wstring& data) = 0; + }; + // When constructed, the underlying V8 debugger is initialized and connected + // to the internals of this object. The provided delegate received output + // from the debugger. This output may be spontaneous (error messages, + // exceptions, etc.) or the output from a command. + // NOTE: the delegate will be called from another thread + Debugger(Debugger::Delegate* del); + virtual ~Debugger(); + + // Sends a command to the debugger (same as v8 command-line debugger). + // Results from the command come asynchronously. + // TODO(erikkay): link command output to a particular command + void Command(const std::wstring& cmd); + + // Attach and detach from the V8 debug message handler. + void Attach(); + void Detach(); + +private: + friend void V8DebugMessageHandler(const uint16_t* message, int length, void* data); + + // Called by the LocalDebugSession so that the delegate can called in the + // appropriate thread. + void OutputLater(const std::wstring& cmd); + // Calls the delegate. This method is called in the delegate's thread. + void Output(const std::wstring& out); + + Delegate* delegate_; + MessageLoop* delegate_loop_; + bool attached_; + + DISALLOW_EVIL_CONSTRUCTORS(Debugger); +}; + +#endif // WEBKIT_GLUE_DEBUGGER_H diff --git a/webkit/glue/dom_operations.cc b/webkit/glue/dom_operations.cc new file mode 100644 index 0000000..dd89d84 --- /dev/null +++ b/webkit/glue/dom_operations.cc @@ -0,0 +1,791 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "FrameLoader.h" +#include "FrameTree.h" +#include "Document.h" +#include "Element.h" +#include "EventListener.h" +#include "EventNames.h" +#include "HTMLCollection.h" +#include "HTMLElement.h" +#include "HTMLFormElement.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLHeadElement.h" +#include "HTMLInputElement.h" +#include "HTMLLinkElement.h" +#include "HTMLMetaElement.h" +#include "HTMLNames.h" +#include "KURL.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/dom_operations.h" + +#include "base/string_util.h" +#include "webkit/glue/autocomplete_input_listener.h" +#include "webkit/glue/form_data.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/password_autocomplete_listener.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webview_impl.h" + +using WebCore::String; + +namespace { + +// Structure for storage the unique set of all savable resource links for +// making sure that no duplicated resource link in final result. The consumer +// of the SavableResourcesUniqueCheck is responsible for keeping these pointers +// valid for the lifetime of the SavableResourcesUniqueCheck instance. +struct SavableResourcesUniqueCheck { + // Unique set of all sub resource links. + std::set<GURL>* resources_set; + // Unique set of all frame links. + std::set<GURL>* frames_set; + // Collection of all frames we go through when getting all savable resource + // links. + std::vector<WebFrameImpl*>* frames; + + SavableResourcesUniqueCheck() + : resources_set(NULL), + frames_set(NULL), + frames(NULL) {} + + SavableResourcesUniqueCheck(std::set<GURL>* resources_set, + std::set<GURL>* frames_set, std::vector<WebFrameImpl*>* frames) + : resources_set(resources_set), + frames_set(frames_set), + frames(frames) {} +}; + +// Get all savable resource links from current element. One element might +// have more than one resource link. It is possible to have some links +// in one CSS stylesheet. +void GetSavableResourceLinkForElement(WebCore::Element* element, + WebCore::Document* current_doc, SavableResourcesUniqueCheck* unique_check, + webkit_glue::SavableResourcesResult* result) { + // Handle frame and iframe tag. + bool is_frame_element; + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromElement(element, &is_frame_element); + if (is_frame_element) { + if (web_frame) + unique_check->frames->push_back(web_frame); + return; + } + // Check whether the node has sub resource URL or not. + const WebCore::AtomicString* value = + webkit_glue::GetSubResourceLinkFromElement(element); + if (!value) + return; + // Get absolute URL. + GURL u(webkit_glue::KURLToGURL(current_doc->completeURL((*value). + deprecatedString()))); + // ignore invalid URL + if (!u.is_valid()) + return; + // Ignore those URLs which are not standard protocols. Because FTP + // protocol does no have cache mechanism, we will skip all + // sub-resources if they use FTP protocol. + if (!u.SchemeIs("http") && !u.SchemeIs("https") && !u.SchemeIs("file")) + return; + // Ignore duplicated resource link. + if (!unique_check->resources_set->insert(u).second) + return; + result->resources_list->push_back(u); + // Insert referrer for above new resource link. + if (current_doc->frame()) { + GURL u(webkit_glue::KURLToGURL( + current_doc->frame()->loader()->outgoingReferrer(). + deprecatedString())); + result->referrers_list->push_back(u); + } else { + // Insert blank referrer. + result->referrers_list->push_back(GURL()); + } +} + +// Get all savable resource links from current WebFrameImpl object pointer. +void GetAllSavableResourceLinksForFrame(WebFrameImpl* current_frame, + SavableResourcesUniqueCheck* unique_check, + webkit_glue::SavableResourcesResult* result) { + // Get current frame's URL. + const WebCore::KURL& current_frame_kurl = + current_frame->frame()->loader()->url(); + GURL current_frame_gurl(webkit_glue::KURLToGURL(current_frame_kurl)); + + // If url of current frame is invalid or not standard protocol, ignore it. + if (!current_frame_gurl.is_valid()) + return; + if (!current_frame_gurl.SchemeIs("http") && + !current_frame_gurl.SchemeIs("https") && + !current_frame_gurl.SchemeIs("ftp") && + !current_frame_gurl.SchemeIs("file")) + return; + // If find same frame we have recorded, ignore it. + if (!unique_check->frames_set->insert(current_frame_gurl).second) + return; + + // Get current using document. + WebCore::Document* current_doc = current_frame->frame()->document(); + // Go through all descent nodes. + PassRefPtr<WebCore::HTMLCollection> all = current_doc->all(); + // Go through all node in this frame. + for (WebCore::Node* node = all->firstItem(); node != NULL; + node = all->nextItem()) { + // We only save HTML resources. + if (!node->isHTMLElement()) + continue; + WebCore::Element* element = static_cast<WebCore::Element*>(node); + GetSavableResourceLinkForElement(element, + current_doc, + unique_check, + result); + } +} + +} // namespace + +namespace webkit_glue { + +// Map element name to a list of pointers to corresponding elements to simplify +// form filling. +typedef std::map<std::wstring, RefPtr<WebCore::HTMLInputElement> > + FormElementRefMap; + +// Utility struct for form lookup and autofill. When we parse the DOM to lookup +// a form, in addition to action and origin URL's we have to compare all +// necessary form elements. To avoid having to look these up again when we want +// to fill the form, the FindFormElements function stores the pointers +// in a FormElements* result, referenced to ensure they are safe to use. +struct FormElements { + RefPtr<WebCore::HTMLFormElement> form_element; + FormElementRefMap input_elements; + FormElements() : form_element(NULL) { + } +}; + +typedef std::vector<FormElements*> FormElementsList; + +static bool FillFormToUploadFileImpl(WebCore::HTMLFormElement* fe, + const FileUploadData& data) { + std::vector<WebCore::HTMLInputElement*> changed; + PassRefPtr<WebCore::HTMLCollection> elements = fe->elements(); + int i, c; + + bool file_found = false; + bool submit_found = false; + + // We reference the form element itself just in case it is destroyed by one + // of the onLoad() handler. + fe->ref(); + + for (i = 0, c = elements->length(); i < c; ++i) { + WebCore::HTMLInputElement* ie = + static_cast<WebCore::HTMLInputElement*>(elements->item(i)); + + std::wstring name = StringToStdWString(ie->name()); + std::wstring id = StringToStdWString(ie->id()); + + if (!file_found && + ie->inputType() == WebCore::HTMLInputElement::FILE && + (name == data.file_name || id == data.file_name)) { + ie->setValueFromRenderer(StdWStringToString(data.file_path)); + ie->ref(); + changed.push_back(ie); + file_found = true; + } else if (!submit_found && + ie->inputType() == WebCore::HTMLInputElement::SUBMIT && + (name == data.submit_name || id == data.submit_name)) { + ie->setActivatedSubmit(true); + submit_found = true; + } else { + FormValueMap::const_iterator val = data.other_form_values.find(name); + if (val != data.other_form_values.end()) { + ie->setValueFromRenderer(StdWStringToString(val->second)); + ie->ref(); + changed.push_back(ie); + } else { + val = data.other_form_values.find(id); + if (val != data.other_form_values.end()) { + ie->setValueFromRenderer(StdWStringToString(val->second)); + ie->ref(); + changed.push_back(ie); + } + } + } + } + + // Call all the onChange functions. + std::vector<WebCore::HTMLInputElement*>::iterator changed_ie; + for (changed_ie = changed.begin(); changed_ie != changed.end(); + ++changed_ie) { + (*changed_ie)->onChange(); + (*changed_ie)->deref(); + } + + // If we found both the file and the submit button, let's submit. + if (file_found && submit_found) { + fe->submit(); + } + + fe->deref(); + + // This operation is successful if the file input has been + // configured. + return file_found; +} + +bool FillFormToUploadFile(WebView* view, const FileUploadData& data) { + WebFrame* main_frame = view->GetMainFrame(); + if (!main_frame) + return false; + WebFrameImpl* main_frame_impl = static_cast<WebFrameImpl*>(main_frame); + WebCore::Frame* frame = main_frame_impl->frame(); + WebCore::Frame* f; + for (f = frame; f; f = f->tree()->traverseNext()) { + WebCore::Document* doc = f->document(); + if (doc->isHTMLDocument()) { + PassRefPtr<WebCore::HTMLCollection> forms = doc->forms(); + int i, c; + for (i = 0, c = forms->length(); i < c; ++i) { + WebCore::HTMLFormElement* fe = + static_cast<WebCore::HTMLFormElement*>(forms->item(i)); + std::wstring name = StringToStdWString(fe->name()); + std::wstring id = StringToStdWString(fe->id()); + if (data.form_name.empty() || + id == data.form_name || name == data.form_name) { + if (FillFormToUploadFileImpl(fe, data)) + return true; + } + } + } + } + return false; +} + +// Internal implementation of FillForm API. +static bool FillFormImpl(FormElements* fe, const FormData& data, bool submit) { + if (!fe->form_element->autoComplete()) + return false; + + FormValueMap data_map; + for (unsigned int i = 0; i < data.elements.size(); i++) { + data_map[data.elements[i]] = data.values[i]; + } + + bool submit_found = false; + for (FormElementRefMap::iterator it = fe->input_elements.begin(); + it != fe->input_elements.end(); ++it) { + if (it->first == data.submit) { + it->second->setActivatedSubmit(true); + submit_found = true; + continue; + } + it->second->setValue(StdWStringToString(data_map[it->first])); + it->second->setAutofilled(true); + it->second->onChange(); + } + + if (submit && submit_found) { + fe->form_element->submit(); + return true; + } + return false; +} + +// Helper function to cast a Node as an HTMLInputElement. +static WebCore::HTMLInputElement* GetNodeAsInputElement(WebCore::Node* node) { + DCHECK(node->nodeType() == WebCore::Node::ELEMENT_NODE); + DCHECK(static_cast<WebCore::Element*>(node)->hasTagName( + WebCore::HTMLNames::inputTag)); + return static_cast<WebCore::HTMLInputElement*>(node); +} + +// Helper to search the given form element for the specified input elements +// in |data|, and add results to |result|. +static bool FindFormInputElements(WebCore::HTMLFormElement* fe, + const FormData& data, + FormElements* result) { + Vector<RefPtr<WebCore::Node> > temp_elements; + bool found_elements = true; + // Loop through the list of elements we need to find on the form in + // order to autofill it. If we don't find any one of them, abort + // processing this form; it can't be the right one. + for (size_t j = 0; j < data.elements.size(); j++, temp_elements.clear()) { + fe->getNamedElements(StdWStringToString(data.elements[j]), + temp_elements); + if (temp_elements.isEmpty()) { + // We didn't find a required element. This is not the right form. + // Make sure no input elements from a partially matched form + // in this iteration remain in the result set. + // Note: clear will remove a reference from each InputElement. + result->input_elements.clear(); + return false; + } + // This element matched, add it to our temporary result. It's possible + // there are multiple matches, but for purposes of identifying the form + // one suffices and if some function needs to deal with multiple + // matching elements it can get at them through the FormElement*. + // Note: This assignment adds a reference to the InputElement. + result->input_elements[data.elements[j]] = + GetNodeAsInputElement(temp_elements[0].get()); + } + return true; +} + +// Helper to locate form elements identified by |data|. +static void FindFormElements(WebView* view, + const FormData& data, + FormElementsList* results) { + DCHECK(view); + DCHECK(results); + WebFrame* main_frame = view->GetMainFrame(); + if (!main_frame) + return; + + GURL::Replacements rep; + rep.ClearQuery(); + rep.ClearRef(); + WebFrameImpl* main_frame_impl = static_cast<WebFrameImpl*>(main_frame); + WebCore::Frame* frame = main_frame_impl->frame(); + + // Loop through each frame. + for (WebCore::Frame* f = frame; f; f = f->tree()->traverseNext()) { + WebCore::Document* doc = f->document(); + if (!doc->isHTMLDocument()) + continue; + + GURL full_origin(StringToStdWString(doc->documentURI())); + if (data.origin != full_origin.ReplaceComponents(rep)) + continue; + + WebCore::FrameLoader* loader = f->loader(); + if (loader == NULL) + continue; + + PassRefPtr<WebCore::HTMLCollection> forms = doc->forms(); + for (size_t i = 0; i < forms->length(); ++i) { + WebCore::HTMLFormElement* fe = + static_cast<WebCore::HTMLFormElement*>(forms->item(i)); + + // Action URL must match. + GURL full_action(KURLToGURL(loader->completeURL(fe->action()))); + if (data.action != full_action.ReplaceComponents(rep)) + continue; + + scoped_ptr<FormElements> curr_elements(new FormElements); + if (!FindFormInputElements(fe, data, curr_elements.get())) + continue; + + // We found the right element. + // Note: this assignment adds a reference to |fe|. + curr_elements->form_element = fe; + results->push_back(curr_elements.release()); + } + } +} + +bool FillForm(WebView* view, const FormData& data) { + FormElementsList forms; + FindFormElements(view, data, &forms); + bool success = false; + if (!forms.empty()) + success = FillFormImpl(forms[0], data, false); + + // TODO(timsteele): Move STLDeleteElements to base/ and have FormElementsList + // use that. + FormElementsList::iterator iter; + for (iter = forms.begin(); iter != forms.end(); ++iter) + delete *iter; + return success; +} + +void FillPasswordForm(WebView* view, + const PasswordFormDomManager::FillData& data) { + FormElementsList forms; + // We own the FormElements* in forms. + FindFormElements(view, data.basic_data, &forms); + FormElementsList::iterator iter; + for (iter = forms.begin(); iter != forms.end(); ++iter) { + // TODO(timsteele): Move STLDeleteElements to base/ and have + // FormElementsList use that. + scoped_ptr<FormElements> form_elements(*iter); + + // False param to FillFormByAction is so we don't auto-submit password + // forms. If wait_for_username is true, we don't want to initially fill + // the form until the user types in a valid username. + if (!data.wait_for_username) + FillFormImpl(form_elements.get(), data.basic_data, false); + + // Attach autocomplete listener to enable selecting alternate logins. + // First, get pointers to username element. + WebCore::HTMLInputElement* username_element = + form_elements->input_elements[data.basic_data.elements[0]].get(); + + // Get pointer to password element. (We currently only support single + // password forms). + WebCore::HTMLInputElement* password_element = + form_elements->input_elements[data.basic_data.elements[1]].get(); + + AttachForInlineAutocomplete(username_element, + new PasswordAutocompleteListener( + new HTMLInputDelegate(username_element), + new HTMLInputDelegate(password_element), + data)); + } +} + +WebFrameImpl* GetWebFrameImplFromElement(WebCore::Element* element, + bool* is_frame_element) { + *is_frame_element = false; + if (element->hasTagName(WebCore::HTMLNames::iframeTag) || + element->hasTagName(WebCore::HTMLNames::frameTag)) { + *is_frame_element = true; + if (element->isFrameOwnerElement()) { + // Check whether this frame has content. + WebCore::HTMLFrameOwnerElement* frame_element = + static_cast<WebCore::HTMLFrameOwnerElement*>(element); + WebCore::Frame* content_frame = frame_element->contentFrame(); + return content_frame ? WebFrameImpl::FromFrame(content_frame) : NULL; + } + } + return NULL; +} + +const WebCore::AtomicString* GetSubResourceLinkFromElement( + const WebCore::Element* element) { + const WebCore::QualifiedName* attribute_name = NULL; + if (element->hasTagName(WebCore::HTMLNames::imgTag) || + element->hasTagName(WebCore::HTMLNames::scriptTag) || + element->hasTagName(WebCore::HTMLNames::linkTag)) { + // Get value. + if (element->hasTagName(WebCore::HTMLNames::linkTag)) { + // If the link element is not linked to css, ignore it. + const WebCore::HTMLLinkElement* link = + static_cast<const WebCore::HTMLLinkElement*>(element); + if (!link->sheet()) + return NULL; + // TODO(jnd). Add support for extracting links of sub-resources which + // are inside style-sheet such as @import, url(), etc. + // See bug: http://b/issue?id=1111667. + attribute_name = &WebCore::HTMLNames::hrefAttr; + } else { + attribute_name = &WebCore::HTMLNames::srcAttr; + } + } else if (element->hasTagName(WebCore::HTMLNames::inputTag)) { + const WebCore::HTMLInputElement* input = + static_cast<const WebCore::HTMLInputElement*>(element); + if (input->inputType() == WebCore::HTMLInputElement::IMAGE) { + attribute_name = &WebCore::HTMLNames::srcAttr; + } + } else if (element->hasTagName(WebCore::HTMLNames::bodyTag) || + element->hasTagName(WebCore::HTMLNames::tableTag) || + element->hasTagName(WebCore::HTMLNames::trTag) || + element->hasTagName(WebCore::HTMLNames::tdTag)) { + attribute_name = &WebCore::HTMLNames::backgroundAttr; + } else if (element->hasTagName(WebCore::HTMLNames::blockquoteTag) || + element->hasTagName(WebCore::HTMLNames::qTag) || + element->hasTagName(WebCore::HTMLNames::delTag) || + element->hasTagName(WebCore::HTMLNames::insTag)) { + attribute_name = &WebCore::HTMLNames::citeAttr; + } + if (!attribute_name) + return NULL; + const WebCore::AtomicString* value = + &element->getAttribute(*attribute_name); + // If value has content and not start with "javascript:" then return it, + // otherwise return NULL. + if (value && !value->isEmpty() && + !value->domString().startsWith("javascript:", false)) + return value; + + return NULL; +} + +bool ElementHasLegalLinkAttribute(const WebCore::Element* element, + const WebCore::QualifiedName& attr_name) { + if (attr_name == WebCore::HTMLNames::srcAttr) { + // Check src attribute. + if (element->hasTagName(WebCore::HTMLNames::imgTag) || + element->hasTagName(WebCore::HTMLNames::scriptTag) || + element->hasTagName(WebCore::HTMLNames::iframeTag) || + element->hasTagName(WebCore::HTMLNames::frameTag)) + return true; + if (element->hasTagName(WebCore::HTMLNames::inputTag)) { + const WebCore::HTMLInputElement* input = + static_cast<const WebCore::HTMLInputElement*>(element); + if (input->inputType() == WebCore::HTMLInputElement::IMAGE) + return true; + } + } else if (attr_name == WebCore::HTMLNames::hrefAttr) { + // Check href attribute. + if (element->hasTagName(WebCore::HTMLNames::linkTag) || + element->hasTagName(WebCore::HTMLNames::aTag) || + element->hasTagName(WebCore::HTMLNames::areaTag)) + return true; + } else if (attr_name == WebCore::HTMLNames::actionAttr) { + if (element->hasTagName(WebCore::HTMLNames::formTag)) + return true; + } else if (attr_name == WebCore::HTMLNames::backgroundAttr) { + if (element->hasTagName(WebCore::HTMLNames::bodyTag) || + element->hasTagName(WebCore::HTMLNames::tableTag) || + element->hasTagName(WebCore::HTMLNames::trTag) || + element->hasTagName(WebCore::HTMLNames::tdTag)) + return true; + } else if (attr_name == WebCore::HTMLNames::citeAttr) { + if (element->hasTagName(WebCore::HTMLNames::blockquoteTag) || + element->hasTagName(WebCore::HTMLNames::qTag) || + element->hasTagName(WebCore::HTMLNames::delTag) || + element->hasTagName(WebCore::HTMLNames::insTag)) + return true; + } else if (attr_name == WebCore::HTMLNames::classidAttr || + attr_name == WebCore::HTMLNames::dataAttr) { + if (element->hasTagName(WebCore::HTMLNames::objectTag)) + return true; + } else if (attr_name == WebCore::HTMLNames::codebaseAttr) { + if (element->hasTagName(WebCore::HTMLNames::objectTag) || + element->hasTagName(WebCore::HTMLNames::appletTag)) + return true; + } + return false; +} + +WebFrameImpl* GetWebFrameImplFromWebViewForSpecificURL(WebView* view, + const GURL& page_url) { + WebFrame* main_frame = view->GetMainFrame(); + if (!main_frame) + return NULL; + WebFrameImpl* main_frame_impl = static_cast<WebFrameImpl*>(main_frame); + + std::vector<WebFrameImpl*> frames; + // First, process main frame. + frames.push_back(main_frame_impl); + // Collect all frames inside the specified frame. + for (int i = 0; i < static_cast<int>(frames.size()); ++i) { + WebFrameImpl* current_frame = frames[i]; + // Get current using document. + WebCore::Document* current_doc = current_frame->frame()->document(); + // Check whether current frame is target or not. + const WebCore::KURL& current_frame_kurl = + current_frame->frame()->loader()->url(); + GURL current_frame_gurl(KURLToGURL(current_frame_kurl)); + if (page_url == current_frame_gurl) + return current_frame; + // Go through sub-frames. + RefPtr<WebCore::HTMLCollection> all = current_doc->all(); + for (WebCore::Node* node = all->firstItem(); node != NULL; + node = all->nextItem()) { + if (!node->isHTMLElement()) + continue; + WebCore::Element* element = static_cast<WebCore::Element*>(node); + // Check frame tag and iframe tag. + bool is_frame_element; + WebFrameImpl* web_frame = GetWebFrameImplFromElement( + element, &is_frame_element); + if (is_frame_element && web_frame) + frames.push_back(web_frame); + } + } + + return NULL; +} + +// Get all savable resource links from current webview, include main +// frame and sub-frame +bool GetAllSavableResourceLinksForCurrentPage(WebView* view, + const GURL& page_url, SavableResourcesResult* result) { + WebFrame* main_frame = view->GetMainFrame(); + if (!main_frame) + return false; + WebFrameImpl* main_frame_impl = static_cast<WebFrameImpl*>(main_frame); + + std::set<GURL> resources_set; + std::set<GURL> frames_set; + std::vector<WebFrameImpl*> frames; + SavableResourcesUniqueCheck unique_check(&resources_set, + &frames_set, + &frames); + + GURL main_page_gurl(KURLToGURL(main_frame_impl->frame()->loader()->url())); + + // Make sure we are saving same page between embedder and webkit. + // If page has being navigated, embedder will get three empty vector, + // which will make the saving page job ended. + if (page_url != main_page_gurl) + return true; + + // First, process main frame. + frames.push_back(main_frame_impl); + + // Check all resource in this page, include sub-frame. + for (int i = 0; i < static_cast<int>(frames.size()); ++i) { + // Get current frame's all savable resource links. + GetAllSavableResourceLinksForFrame(frames[i], &unique_check, result); + } + + // Since frame's src can also point to sub-resources link, so it is possible + // that some URLs in frames_list are also in resources_list. For those + // URLs, we will remove it from frame_list, only keep them in resources_list. + for (std::set<GURL>::iterator it = frames_set.begin(); + it != frames_set.end(); ++it) { + // Append unique frame source to savable frame list. + if (resources_set.find(*it) == resources_set.end()) + result->frames_list->push_back(*it); + } + + return true; +} + +// Sizes a single size (the width or height) from a 'sizes' attribute. A size +// matches must match the following regex: [1-9][0-9]*. +static int ParseSingleIconSize(const std::wstring& text) { + // Size must not start with 0, and be between 0 and 9. + if (text.empty() || !(text[0] >= L'1' && text[0] <= L'9')) + return 0; + // Make sure all chars are from 0-9. + for (size_t i = 1; i < text.length(); ++i) { + if (!(text[i] >= L'0' && text[i] <= L'9')) + return 0; + } + return _wtoi(text.c_str()); +} + +// Parses an icon size. An icon size must match the following regex: +// [1-9][0-9]*x[1-9][0-9]*. +// If the input couldn't be parsed, a size with a width/height < 0 is returned. +static gfx::Size ParseIconSize(const std::wstring& text) { + std::vector<std::wstring> sizes; + SplitStringDontTrim(text, L'x', &sizes); + if (sizes.size() != 2) + return gfx::Size(); + + return gfx::Size(ParseSingleIconSize(sizes[0]), + ParseSingleIconSize(sizes[1])); +} + +bool ParseIconSizes(const std::wstring& text, + std::vector<gfx::Size>* sizes, + bool* is_any) { + *is_any = false; + std::vector<std::wstring> size_strings; + SplitStringAlongWhitespace(text, &size_strings); + for (size_t i = 0; i < size_strings.size(); ++i) { + if (size_strings[i] == L"any") { + *is_any = true; + } else { + gfx::Size size = ParseIconSize(size_strings[i]); + if (size.width() <= 0 || size.height() <= 0) + return false; // Bogus size. + sizes->push_back(size); + } + } + if (*is_any && !sizes->empty()) { + // If is_any is true, it must occur by itself. + return false; + } + return (*is_any || !sizes->empty()); +} + +static void AddInstallIcon(WebCore::HTMLLinkElement* link, + std::vector<WebApplicationInfo::IconInfo>* icons) { + String href = link->href(); + if (href.isEmpty() || href.isNull()) + return; + + GURL url(webkit_glue::StringToStdWString(href)); + if (!url.is_valid()) + return; + + const String sizes_attr = "sizes"; + if (!link->hasAttribute(sizes_attr)) + return; + + bool is_any = false; + std::vector<gfx::Size> icon_sizes; + if (!ParseIconSizes(webkit_glue::StringToStdWString( + link->getAttribute(sizes_attr)), &icon_sizes, &is_any) || is_any || + icon_sizes.size() != 1) { + return; + } + WebApplicationInfo::IconInfo icon_info; + icon_info.width = icon_sizes[0].width(); + icon_info.height = icon_sizes[0].height(); + icon_info.url = url; + icons->push_back(icon_info); +} + +void GetApplicationInfo(WebView* view, WebApplicationInfo* app_info) { + WebFrame* main_frame = view->GetMainFrame(); + if (!main_frame) + return; + WebFrameImpl* main_frame_impl = static_cast<WebFrameImpl*>(main_frame); + + WebCore::HTMLHeadElement* head; + if (!main_frame_impl->frame() || + !main_frame_impl->frame()->document() || + !(head = main_frame_impl->frame()->document()->head())) { + return; + } + WTF::PassRefPtr<WebCore::HTMLCollection> children = head->children(); + for (unsigned i = 0; i < children->length(); ++i) { + WebCore::Node* child = children->item(i); + WebCore::HTMLLinkElement* link = CastHTMLElement<WebCore::HTMLLinkElement>( + child, WebCore::HTMLNames::linkTag); + if (link) { + if (link->isIcon()) + AddInstallIcon(link, &app_info->icons); + } else { + WebCore::HTMLMetaElement* meta = + CastHTMLElement<WebCore::HTMLMetaElement>( + child, WebCore::HTMLNames::metaTag); + if (meta) { + if (meta->name() == String("application-name")) { + app_info->title = webkit_glue::StringToStdWString(meta->content()); + } else if (meta->name() == String("description")) { + app_info->description = + webkit_glue::StringToStdWString(meta->content()); + } else if (meta->name() == String("application-url")) { + std::wstring url = webkit_glue::StringToStdWString(meta->content()); + GURL main_url = main_frame->GetURL(); + app_info->app_url = main_url.is_valid() ? + main_url.Resolve(url) : GURL(url); + if (!app_info->app_url.is_valid()) + app_info->app_url = GURL(); + } + } + } + } +} + +} // webkit_glue diff --git a/webkit/glue/dom_operations.h b/webkit/glue/dom_operations.h new file mode 100644 index 0000000..777c519 --- /dev/null +++ b/webkit/glue/dom_operations.h @@ -0,0 +1,205 @@ +// Copyright 2008, 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 WEBKIT_GLUE_DOM_OPERATIONS_H__ +#define WEBKIT_GLUE_DOM_OPERATIONS_H__ + +#include <string> +#include <map> + +#include "base/gfx/size.h" +#include "googleurl/src/gurl.h" +#include "webkit/glue/password_form_dom_manager.h" + +struct FormData; +class WebFrameImpl; +class WebView; + +namespace WebCore { +class AtomicString; +class Document; +class Element; +class Node; +class QualifiedName; +class String; +} + +// A collection of operations that access the underlying WebKit DOM directly. +namespace webkit_glue { + +// Automatically fill a form to upload a file. +// +// Look in all frames for a form with the name or ID |form_name|. If the form is +// found, set the input type=file with name or ID equal to |file_name| to +// |file_path|. If |form_name| is empty, look for any form containing the +// provided submit button. +// +// If |submit_name| is non empty and a submit button with a matching name or ID +// exists, the form is submitted using that submit button. If any form input +// has a name or ID matching an |other_form_values| key, it will be set to the +// corresponding value. +// +// Return true if a form was found and processed. +typedef std::map<std::wstring, std::wstring> FormValueMap; +struct FileUploadData { + std::wstring file_path; + std::wstring form_name; + std::wstring file_name; + std::wstring submit_name; + FormValueMap other_form_values; +}; + +bool FillFormToUploadFile(WebView* view, const FileUploadData& data); + +// Fill in a form identified by form |data|. +bool FillForm(WebView* view, const FormData& data); + +// Fill matching password forms and trigger autocomplete in the case of multiple +// matching logins. +void FillPasswordForm(WebView* view, + const PasswordFormDomManager::FillData& data); + +// If node is an HTML node with a tag name of name it is casted to HTMLNodeType +// and returned. If node is not an HTML node or the tag name is not name +// NULL is returned. +template <class HTMLNodeType> +HTMLNodeType* CastHTMLElement(WebCore::Node* node, + const WebCore::QualifiedName& name) { + if (node->isHTMLElement() && + static_cast<WebCore::HTMLElement*>(node)->hasTagName(name)) { + return static_cast<HTMLNodeType*>(node); + } + return NULL; +} + +// If element is HTML:IFrame or HTML:Frame, then return the WebFrameImpl +// object corresponding to the content frame, otherwise return NULL. +// The parameter is_frame_element indicates whether the input element +// is frame/iframe element or not. +WebFrameImpl* GetWebFrameImplFromElement(WebCore::Element* element, + bool* is_frame_element); + + +// If element is img, script or input type=image, then return its link refer +// to the "src" attribute. If element is link, then return its link refer to +// the "href" attribute. If element is body, table, tr, td, then return its +// link refer to the "background" attribute. If element is blockquote, q, del, +// ins, then return its link refer to the "cite" attribute. Otherwise return +// NULL. +const WebCore::AtomicString* GetSubResourceLinkFromElement( + const WebCore::Element* element); + +// 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& attr_name); + +// Get pointer of WebFrameImpl from webview according to specific URL. +WebFrameImpl* GetWebFrameImplFromWebViewForSpecificURL(WebView* view, + const GURL& page_url); + +// Structure for storage the result of getting all savable resource links +// for current page. The consumer of the SavableResourcesResult is responsible +// for keeping these pointers valid for the lifetime of the +// SavableResourcesResult instance. +struct SavableResourcesResult { + // vector which contains all savable links of sub resource. + std::vector<GURL>* resources_list; + // vector which contains corresponding all referral links of sub resource, + // it matched with links one by one. + std::vector<GURL>* referrers_list; + // vector which contains all savable links of main frame and sub frames. + std::vector<GURL>* frames_list; + + // Constructor. + SavableResourcesResult(std::vector<GURL>* resources_list, + std::vector<GURL>* referrers_list, + std::vector<GURL>* frames_list) + : resources_list(resources_list), + referrers_list(referrers_list), + frames_list(frames_list) { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(SavableResourcesResult); +}; + +// Get all savable resource links from current webview, include main frame +// and sub-frame. After collecting all savable resource links, this function +// will send those links to embedder. Return value indicates whether we get +// all saved resource links successfully. +bool GetAllSavableResourceLinksForCurrentPage(WebView* view, + const GURL& page_url, SavableResourcesResult* savable_resources_result); + +// Structure used when installing a web page as an app. Populated via +// GetApplicationInfo. +struct WebApplicationInfo { + struct IconInfo { + GURL url; + int width; + int height; + }; + + // Title of the application. This is set from the meta tag whose name is + // 'application-name'. + std::wstring title; + + // Description of the application. This is set from the meta tag whose name + // is 'description'. + std::wstring description; + + // URL for the app. This is set from the meta tag whose name is + // 'application-url'. + GURL app_url; + + // Set of available icons. This is set for all link tags whose rel=icon. Only + // icons that have a non-zero (width and/or height) are added. + std::vector<IconInfo> icons; +}; + +// Parses the icon's size attribute as defined in the HTML 5 spec. Returns true +// on success, false on errors. On success either all the sizes specified in +// the attribute are added to sizes, or is_any is set to true. +// +// You shouldn't have a need to invoke this directly, it's public for testing. +bool ParseIconSizes(const std::wstring& text, + std::vector<gfx::Size>* sizes, + bool* is_any); + +// Gets the application info for the specified page. See the description of +// WebApplicationInfo for details as to where each field comes from. +void GetApplicationInfo(WebView* view, WebApplicationInfo* app_info); + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_DOM_OPERATIONS_H__ diff --git a/webkit/glue/dom_operations_unittest.cc b/webkit/glue/dom_operations_unittest.cc new file mode 100644 index 0000000..3865ea7 --- /dev/null +++ b/webkit/glue/dom_operations_unittest.cc @@ -0,0 +1,207 @@ +// Copyright 2008, 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 "base/file_util.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "net/base/net_util.h" +#include "net/url_request/url_request_context.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/webview.h" +#include "webkit/glue/webframe.h" +#include "webkit/tools/test_shell/simple_resource_loader_bridge.h" +#include "webkit/tools/test_shell/test_shell_test.h" + +namespace { + +class DomOperationsTests : public TestShellTest { + public: + // Test function GetAllSavableResourceLinksForCurrentPage with a web page. + // We expect result of GetAllSavableResourceLinksForCurrentPage exactly + // matches expected_resources_set. + void GetSavableResourceLinksForPage(const std::wstring& page_file_path, + const std::set<GURL>& expected_resources_set); + + protected: + // testing::Test + virtual void SetUp() { + TestShellTest::SetUp(); + } + + virtual void TearDown() { + TestShellTest::TearDown(); + } +}; + +} // namespace + + +void DomOperationsTests::GetSavableResourceLinksForPage( + const std::wstring& page_file_path, + const std::set<GURL>& expected_resources_set) { + // Convert local file path to file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + // Load the test file. + test_shell_->ResetTestController(); + std::wstring file_wurl = ASCIIToWide(file_url.spec()); + test_shell_->LoadURL(file_wurl.c_str()); + test_shell_->WaitTestFinished(); + // Get all savable resource links for the page. + std::vector<GURL> resources_list; + std::vector<GURL> referrers_list; + std::vector<GURL> frames_list; + webkit_glue::SavableResourcesResult result(&resources_list, + &referrers_list, + &frames_list); + + GURL main_page_gurl(file_wurl); + ASSERT_TRUE(webkit_glue::GetAllSavableResourceLinksForCurrentPage( + test_shell_->webView(), main_page_gurl, &result)); + // Check all links of sub-resource + for (std::vector<GURL>::const_iterator cit = resources_list.begin(); + cit != resources_list.end(); ++cit) { + ASSERT_TRUE(expected_resources_set.find(*cit) != + expected_resources_set.end()); + } + // Check all links of frame. + for (std::vector<GURL>::const_iterator cit = frames_list.begin(); + cit != frames_list.end(); ++cit) { + ASSERT_TRUE(expected_resources_set.find(*cit) != + expected_resources_set.end()); + } +} + +// Test function GetAllSavableResourceLinksForCurrentPage with a web page +// which has valid savable resource links. +TEST_F(DomOperationsTests, GetSavableResourceLinksWithPageHasValidLinks) { + std::set<GURL> expected_resources_set; + // Set directory of test data. + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer"); + + const wchar_t* expected_sub_resource_links[] = { + L"file:///c:/yt/css/base_all-vfl36460.css", + L"file:///c:/yt/js/base_all_with_bidi-vfl36451.js", + L"file:///c:/yt/img/pixel-vfl73.gif" + }; + const wchar_t* expected_frame_links[] = { + L"youtube_1.htm", + L"youtube_2.htm" + }; + // Add all expected links of sub-resource to expected set. + for (int i = 0; i < arraysize(expected_sub_resource_links); ++i) + expected_resources_set.insert(GURL(expected_sub_resource_links[i])); + // Add all expected links of frame to expected set. + for (int i = 0; i < arraysize(expected_frame_links); ++i) { + std::wstring expected_frame_url = page_file_path; + file_util::AppendToPath(&expected_frame_url, expected_frame_links[i]); + expected_resources_set.insert( + net_util::FilePathToFileURL(expected_frame_url)); + } + + file_util::AppendToPath(&page_file_path, std::wstring(L"youtube_1.htm")); + GetSavableResourceLinksForPage(page_file_path, expected_resources_set); +} + +// Test function GetAllSavableResourceLinksForCurrentPage with a web page +// which does not have valid savable resource links. +TEST_F(DomOperationsTests, GetSavableResourceLinksWithPageHasInvalidLinks) { + std::set<GURL> expected_resources_set; + // Set directory of test data. + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer"); + + const wchar_t* expected_frame_links[] = { + L"youtube_2.htm" + }; + // Add all expected links of frame to expected set. + for (int i = 0; i < arraysize(expected_frame_links); ++i) { + std::wstring expected_frame_url = page_file_path; + file_util::AppendToPath(&expected_frame_url, expected_frame_links[i]); + expected_resources_set.insert( + net_util::FilePathToFileURL(expected_frame_url)); + } + + file_util::AppendToPath(&page_file_path, std::wstring(L"youtube_2.htm")); + GetSavableResourceLinksForPage(page_file_path, expected_resources_set); +} + +// Tests ParseIconSizes with various input. +TEST_F(DomOperationsTests, ParseIconSizes) { + struct TestData { + const std::wstring input; + const bool expected_result; + const bool is_any; + const int expected_size_count; + const int width1; + const int height1; + const int width2; + const int height2; + } data[] = { + // Bogus input cases. + { L"10", false, false, 0, 0, 0, 0, 0 }, + { L"10 10", false, false, 0, 0, 0, 0, 0 }, + { L"010", false, false, 0, 0, 0, 0, 0 }, + { L" 010 ", false, false, 0, 0, 0, 0, 0 }, + { L" 10x ", false, false, 0, 0, 0, 0, 0 }, + { L" x10 ", false, false, 0, 0, 0, 0, 0 }, + { L"any 10x10", false, false, 0, 0, 0, 0, 0 }, + { L"", false, false, 0, 0, 0, 0, 0 }, + { L"10ax11", false, false, 0, 0, 0, 0, 0 }, + + // Any. + { L"any", true, true, 0, 0, 0, 0, 0 }, + { L" any", true, true, 0, 0, 0, 0, 0 }, + { L" any ", true, true, 0, 0, 0, 0, 0 }, + + // Sizes. + { L"10x11", true, false, 1, 10, 11, 0, 0 }, + { L" 10x11 ", true, false, 1, 10, 11, 0, 0 }, + { L" 10x11 1x2", true, false, 2, 10, 11, 1, 2 }, + }; + for (size_t i = 0; i < arraysize(data); ++i) { + bool is_any; + std::vector<gfx::Size> sizes; + const bool result = + webkit_glue::ParseIconSizes(data[i].input, &sizes, &is_any); + ASSERT_EQ(result, data[i].expected_result); + if (result) { + ASSERT_EQ(data[i].is_any, is_any); + ASSERT_EQ(data[i].expected_size_count, sizes.size()); + if (sizes.size() > 0) { + ASSERT_EQ(data[i].width1, sizes[0].width()); + ASSERT_EQ(data[i].height1, sizes[0].height()); + } + if (sizes.size() > 1) { + ASSERT_EQ(data[i].width2, sizes[1].width()); + ASSERT_EQ(data[i].height2, sizes[1].height()); + } + } + } +} diff --git a/webkit/glue/dom_serializer.cc b/webkit/glue/dom_serializer.cc new file mode 100644 index 0000000..3f75d27 --- /dev/null +++ b/webkit/glue/dom_serializer.cc @@ -0,0 +1,649 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "DocumentType.h" +#include "FrameLoader.h" +#include "Document.h" +#include "Element.h" +#include "HTMLCollection.h" +#include "HTMLElement.h" +#include "HTMLFormElement.h" +#include "HTMLMetaElement.h" +#include "HTMLNames.h" +#include "KURL.h" +#include "PlatformString.h" +#include "TextEncoding.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/dom_serializer.h" + +#include "base/string_util.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/dom_serializer_delegate.h" +#include "webkit/glue/entity_map.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webframe_impl.h" + +namespace { + +// Default "mark of the web" declaration +static const wchar_t* const kDefaultMarkOfTheWeb = + L"\n<!-- saved from url=(%04d)%s -->\n"; + +// Default meat content for writing correct charset declaration. +static const wchar_t* const kDefaultMetaContent = + L"<META http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">"; + +// Notation of start comment. +static const wchar_t* const kStartCommentNotation = L"<!-- "; + +// Notation of end comment. +static const wchar_t* const kEndCommentNotation = L" -->"; + +// Default XML declaration. +static const wchar_t* const kXMLDeclaration = + L"<?xml version=\"%s\" encoding=\"%s\"%s?>\n"; + +// Default base tag declaration +static const wchar_t* const kBaseTagDeclaration = + L"<BASE href=\".\"%s>"; + +static const wchar_t* const kBaseTargetDeclaration = + L" target=\"%s\""; + +// Maximum length of data buffer which is used to temporary save generated +// html content data. +static const int kHtmlContentBufferLength = 65536; + +// Check whether specified unicode has corresponding html/xml entity name. +// If yes, replace the character with the returned entity notation, if not +// then still use original character. +void ConvertCorrespondingSymbolToEntity(WebCore::String* result, + const WebCore::String& value, + bool in_html_doc) { + unsigned len = value.length(); + const UChar* start_pos = value.characters(); + const UChar* cur_pos = start_pos; + while (len--) { + const char* entity_name = + webkit_glue::EntityMap::GetEntityNameByCode(*cur_pos, in_html_doc); + if (entity_name) { + // Append content before entity code. + if (cur_pos > start_pos) + result->append(start_pos, cur_pos - start_pos); + result->append(entity_name); + start_pos = ++cur_pos; + } else { + cur_pos++; + } + } + // Append the remaining content. + if (cur_pos > start_pos) + result->append(start_pos, cur_pos - start_pos); +} + +} // namespace + +namespace webkit_glue { + +// SerializeDomParam Constructor. +DomSerializer::SerializeDomParam::SerializeDomParam( + const GURL& current_frame_gurl, + const std::wstring& current_frame_wurl, + const WebCore::TextEncoding& text_encoding, + WebCore::Document* doc, + const std::wstring& directory_name) + : current_frame_gurl(current_frame_gurl), + current_frame_wurl(current_frame_wurl), + text_encoding(text_encoding), + doc(doc), + directory_name(directory_name), + has_doctype(false), + has_checked_meta(false), + skip_meta_element(NULL), + is_in_script_or_style_tag(false), + has_doc_declaration(false) { + // Cache the value since we check it lots of times. + is_html_document = doc->isHTMLDocument(); +} + +// Static. +std::wstring DomSerializer::GenerateMarkOfTheWebDeclaration( + const std::wstring& url) { + return StringPrintf(kDefaultMarkOfTheWeb, url.size(), url.c_str()); +} + +// Static. +std::wstring DomSerializer::GenerateBaseTagDeclaration( + const std::wstring& base_target) { + std::wstring target_declaration = base_target.empty() ? L"" : + StringPrintf(kBaseTargetDeclaration, base_target.c_str()); + return StringPrintf(kBaseTagDeclaration, target_declaration.c_str()); +} + +WebCore::String DomSerializer::PreActionBeforeSerializeOpenTag( + const WebCore::Element* element, SerializeDomParam* param, + bool* need_skip) { + WebCore::String result; + + *need_skip = false; + if (param->is_html_document) { + // 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(WebCore::HTMLNames::metaTag)) { + const WebCore::HTMLMetaElement* meta = + static_cast<const WebCore::HTMLMetaElement*>(element); + // Check whether the META tag has declared charset or not. + WebCore::String equiv = meta->httpEquiv(); + if (equalIgnoringCase(equiv, "content-type")) { + WebCore::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->skip_meta_element = element; + *need_skip = true; + } + } + } else if (element->hasTagName(WebCore::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->has_doctype) { + param->has_doctype = true; + WebCore::DocumentType* doc_type = param->doc->doctype(); + if (doc_type) + result += doc_type->toString(); + } + + // Add MOTW declaration before html tag. + // See http://msdn2.microsoft.com/en-us/library/ms537628(VS.85).aspx. + result += GenerateMarkOfTheWebDeclaration(param->current_frame_wurl). + c_str(); + } else if (element->hasTagName(WebCore::HTMLNames::baseTag)) { + // Comment the BASE tag when serializing dom. + result += kStartCommentNotation; + } + } else { + // Write XML declaration. + if (!param->has_doc_declaration) { + param->has_doc_declaration = true; + // Get encoding info. + WebCore::String xml_encoding = param->doc->xmlEncoding(); + if (xml_encoding.isEmpty()) + xml_encoding = param->doc->frame()->loader()->encoding(); + if (xml_encoding.isEmpty()) + xml_encoding = WebCore::UTF8Encoding().name(); + std::wstring str_xml_declaration = + StringPrintf(kXMLDeclaration, + param->doc->xmlVersion().charactersWithNullTermination(), + xml_encoding.charactersWithNullTermination(), + param->doc->xmlStandalone() ? L" standalone=\"yes\"" : + L""); + result += str_xml_declaration.c_str(); + } + // Add doc type declaration if original doc has it. + if (!param->has_doctype) { + param->has_doctype = true; + WebCore::DocumentType* doc_type = param->doc->doctype(); + if (doc_type) + result += doc_type->toString(); + } + } + + return result; +} + +WebCore::String DomSerializer::PostActionAfterSerializeOpenTag( + const WebCore::Element* element, SerializeDomParam* param) { + WebCore::String result; + + if (!param->is_html_document) + return result; + // Check after processing the open tag of HEAD element + if (!param->has_checked_meta && + element->hasTagName(WebCore::HTMLNames::headTag)) { + param->has_checked_meta = 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. + std::wstring str_meta = + StringPrintf(kDefaultMetaContent, + ASCIIToWide(param->text_encoding.name()).c_str()); + result += str_meta.c_str(); + + // Will search each META which has charset declaration, and skip them all + // in PreActionBeforeSerializeOpenTag. + } else if (element->hasTagName(WebCore::HTMLNames::scriptTag) || + element->hasTagName(WebCore::HTMLNames::styleTag)) { + param->is_in_script_or_style_tag = true; + } + + return result; +} + +WebCore::String DomSerializer::PreActionBeforeSerializeEndTag( + const WebCore::Element* element, SerializeDomParam* param, + bool* need_skip) { + WebCore::String result; + + *need_skip = false; + if (!param->is_html_document) + 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 + // skip_meta_element is definitely META tag if it's not NULL. + if (param->skip_meta_element == element) { + *need_skip = true; + } else if (element->hasTagName(WebCore::HTMLNames::scriptTag) || + element->hasTagName(WebCore::HTMLNames::styleTag)) { + DCHECK(param->is_in_script_or_style_tag); + param->is_in_script_or_style_tag = 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. +WebCore::String DomSerializer::PostActionAfterSerializeEndTag( + const WebCore::Element* element, SerializeDomParam* param) { + WebCore::String result; + + if (!param->is_html_document) + return result; + // Comment the BASE tag when serializing DOM. + if (element->hasTagName(WebCore::HTMLNames::baseTag)) { + result += kEndCommentNotation; + // Append a new base tag declaration. + result += GenerateBaseTagDeclaration( + webkit_glue::StringToStdWString(param->doc->baseTarget())).c_str(); + } + + return result; +} + +void DomSerializer::SaveHtmlContentToBuffer(const WebCore::String& result, + SerializeDomParam* param) { + if (!result.length()) + return; + // Convert the unicode content to target encoding + const UChar* ucode = result.characters(); + // If the text encoding can not convert some unicode character to + // corresponding code, we allow using entity notation to replace + // the unicode character. + WebCore::CString encoding_result = param->text_encoding.encode(ucode, + result.length(), true); + + // if the data buffer will be full, then send it out first. + if (encoding_result.length() + data_buffer_.size() > + data_buffer_.capacity()) { + // Send data to delegate, tell it now we are serializing current frame. + delegate_->DidSerializeDataForFrame(param->current_frame_gurl, + data_buffer_, DomSerializerDelegate::CURRENT_FRAME_IS_NOT_FINISHED); + data_buffer_.clear(); + } + + // Append result to data buffer. + data_buffer_.append(CStringToStdString(encoding_result)); +} + +void DomSerializer::OpenTagToString(const WebCore::Element* element, + SerializeDomParam* param) { + bool need_skip; + // Do pre action for open tag. + WebCore::String result = PreActionBeforeSerializeOpenTag(element, + param, + &need_skip); + if (need_skip) + return; + // Add open tag + result += "<" + element->nodeName(); + // Go through all attributes and serialize them. + const WebCore::NamedAttrMap *attrMap = element->attributes(true); + if (attrMap) { + unsigned numAttrs = attrMap->length(); + for (unsigned i = 0; i < numAttrs; i++) { + result += " "; + // Add attribute pair + const WebCore::Attribute *attribute = attrMap->attributeItem(i); + result += attribute->name().toString(); + result += "=\""; + if (!attribute->value().isEmpty()) { + // Check whether we need to replace some resource links + // with local resource paths. + const WebCore::QualifiedName& attr_name = attribute->name(); + // Check whether need to change the attribute which has link + bool need_replace_link = + ElementHasLegalLinkAttribute(element, attr_name); + if (need_replace_link) { + // First, get the absolute link + const WebCore::String& attr_value = attribute->value(); + // For links start with "javascript:", we do not change it. + if (attr_value.startsWith("javascript:", false)) { + result += attr_value; + } else { + WebCore::String str_value = param->doc->completeURL(attr_value); + std::wstring value(str_value.charactersWithNullTermination()); + // Check whether we local files for those link. + LinkLocalPathMap::const_iterator it = local_links_.find(value); + if (it != local_links_.end()) { + // Replace the link when we have local files. + result += param->directory_name.c_str(); + result += (it->second).c_str(); + } else { + // If not found local path, replace it with absolute link. + result += str_value; + } + } + } else { + ConvertCorrespondingSymbolToEntity(&result, attribute->value(), + param->is_html_document); + } + } + result += "\""; + } + } + // Complete the open tag for element when it has child/children. + if (element->hasChildNodes()) + result += ">"; + // Do post action for open tag. + result += PostActionAfterSerializeOpenTag(element, param); + // Save the result to data buffer. + SaveHtmlContentToBuffer(result, param); +} + +// Serialize end tag of an specified element. +void DomSerializer::EndTagToString(const WebCore::Element* element, + SerializeDomParam* param) { + bool need_skip; + // Do pre action for end tag. + WebCore::String result = PreActionBeforeSerializeEndTag(element, + param, + &need_skip); + if (need_skip) + return; + // Write end tag when element has child/children. + if (element->hasChildNodes()) { + result += "</"; + result += element->nodeName(); + result += ">"; + } else { + // Check whether we have to write end tag for empty element. + if (param->is_html_document) { + result += ">"; + const WebCore::HTMLElement* html_element = + static_cast<const WebCore::HTMLElement*>(element); + if (html_element->endTagRequirement() == WebCore::TagStatusRequired) { + // We need to write end tag when it is required. + result += "</"; + result += html_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 DomSerializer::BuildContentForNode(const WebCore::Node* node, + SerializeDomParam* param) { + switch (node->nodeType()) { + case WebCore::Node::ELEMENT_NODE: { + // Process open tag of element. + OpenTagToString(static_cast<const WebCore::Element*>(node), param); + // Walk through the children nodes and process it. + for (const WebCore::Node *child = node->firstChild(); child != NULL; + child = child->nextSibling()) + BuildContentForNode(child, param); + // Process end tag of element. + EndTagToString(static_cast<const WebCore::Element*>(node), param); + break; + } + case WebCore::Node::TEXT_NODE: { + WebCore::String result; + WebCore::String s = node->toString(); + if (param->is_html_document) { + // For html document, do not convert entity notation in code + // block of style tag and script tag. + if (param->is_in_script_or_style_tag) + result += s; + else + ConvertCorrespondingSymbolToEntity(&result, s, + param->is_html_document); + } else { + ConvertCorrespondingSymbolToEntity(&result, s, + param->is_html_document); + } + + SaveHtmlContentToBuffer(result, param); + break; + } + case WebCore::Node::ATTRIBUTE_NODE: + case WebCore::Node::DOCUMENT_NODE: + case WebCore::Node::DOCUMENT_FRAGMENT_NODE: { + // Should not exist. + DCHECK(false); + break; + } + // Document type node can be in DOM? + case WebCore::Node::DOCUMENT_TYPE_NODE: + param->has_doctype = true; + default: { + // For other type node, call default action. + SaveHtmlContentToBuffer(node->toString(), param); + break; + } + } +} + +DomSerializer::DomSerializer(WebFrame* webframe, + bool recursive_serialization, + DomSerializerDelegate* delegate, + const std::vector<std::wstring>& links, + const std::vector<std::wstring>& local_paths, + const std::wstring& local_directory_name) + : recursive_serialization_(recursive_serialization), + delegate_(delegate), + local_directory_name_(local_directory_name), + frames_collected_(false) { + // Must specify available webframe. + DCHECK(webframe); + specified_webframeimpl_ = static_cast<WebFrameImpl*>(webframe); + // Make sure we have not-NULL delegate. + DCHECK(delegate); + // Build local resources map. + DCHECK(links.size() == local_paths.size()); + for (std::vector<std::wstring>::const_iterator link_it = links.begin(), + path_it = local_paths.begin(); link_it != links.end(); + ++link_it, ++path_it) { + bool never_present = local_links_.insert( + LinkLocalPathMap::value_type(*link_it, *path_it)). + second; + DCHECK(never_present); + } + + // Init data buffer. + data_buffer_.reserve(kHtmlContentBufferLength); + DCHECK(data_buffer_.empty()); +} + +void DomSerializer::CollectTargetFrames() { + DCHECK(!frames_collected_); + frames_collected_ = true; + + // First, process main frame. + frames_.push_back(specified_webframeimpl_); + // Return now if user only needs to serialize specified frame, not including + // all sub-frames. + if (!recursive_serialization_) + return; + // Collect all frames inside the specified frame. + for (int i = 0; i < static_cast<int>(frames_.size()); ++i) { + WebFrameImpl* current_frame = frames_[i]; + // Get current using document. + WebCore::Document* current_doc = current_frame->frame()->document(); + // Go through sub-frames. + RefPtr<WebCore::HTMLCollection> all = current_doc->all(); + for (WebCore::Node* node = all->firstItem(); node != NULL; + node = all->nextItem()) { + if (!node->isHTMLElement()) + continue; + WebCore::Element* element = static_cast<WebCore::Element*>(node); + // Check frame tag and iframe tag. + bool is_frame_element; + WebFrameImpl* web_frame = GetWebFrameImplFromElement( + element, &is_frame_element); + if (is_frame_element && web_frame) + frames_.push_back(web_frame); + } + } +} + +bool DomSerializer::SerializeDom() { + // Collect target frames. + if (!frames_collected_) + CollectTargetFrames(); + bool did_serialization = false; + // Get GURL for main frame. + GURL main_page_gurl(KURLToGURL( + specified_webframeimpl_->frame()->loader()->url())); + + // Go through all frames for serializing DOM for whole page, include + // sub-frames. + for (int i = 0; i < static_cast<int>(frames_.size()); ++i) { + // Get current serializing frame. + WebFrameImpl* current_frame = frames_[i]; + // Get current using document. + WebCore::Document* current_doc = current_frame->frame()->document(); + // Get current frame's URL. + const WebCore::KURL& current_frame_kurl = + current_frame->frame()->loader()->url(); + GURL current_frame_gurl(KURLToGURL(current_frame_kurl)); + std::wstring current_frame_wurl = ASCIIToWide(current_frame_gurl.spec()); + + // Check whether we have done this document. + if (local_links_.find(current_frame_wurl) != local_links_.end()) { + // A new document, we will serialize it. + did_serialization = true; + // Get target encoding for current document. + WebCore::String encoding = current_frame->frame()->loader()->encoding(); + // Create the text encoding object with target encoding. + WebCore::TextEncoding text_encoding(encoding); + // Construct serialize parameter for late processing document. + SerializeDomParam param( + current_frame_gurl, + current_frame_wurl, + encoding.length() ? text_encoding : WebCore::UTF8Encoding(), + current_doc, + current_frame_gurl == main_page_gurl ? + local_directory_name_ : L"./"); + + // Process current document. + WebCore::Element* root_element = current_doc->documentElement(); + if (root_element) + BuildContentForNode(root_element, ¶m); + + // Sink the remainder data and finish serializing current frame. + delegate_->DidSerializeDataForFrame(current_frame_gurl, data_buffer_, + DomSerializerDelegate::CURRENT_FRAME_IS_FINISHED); + // Clear the buffer. + data_buffer_.clear(); + } + } + + // We have done call frames, so we send message to embedder to tell it that + // frames are finished serializing. + DCHECK(data_buffer_.empty()); + delegate_->DidSerializeDataForFrame(GURL(), data_buffer_, + DomSerializerDelegate::ALL_FRAMES_ARE_FINISHED); + + return did_serialization; +} + +} // namespace webkit_glue diff --git a/webkit/glue/dom_serializer.h b/webkit/glue/dom_serializer.h new file mode 100644 index 0000000..d9e667b --- /dev/null +++ b/webkit/glue/dom_serializer.h @@ -0,0 +1,196 @@ +// Copyright 2008, 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 WEBKIT_GLUE_DOM_SERIALIZER_H__ +#define WEBKIT_GLUE_DOM_SERIALIZER_H__ + +#include <string> +#include <hash_map> + +#include "googleurl/src/gurl.h" + +class WebFrame; +class WebFrameImpl; + +namespace WebCore { +class Document; +class Element; +class Node; +class String; +class TextEncoding; +} + +namespace webkit_glue { + +class DomSerializerDelegate; + +// 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 DomSerializer { + public: + // Do serialization action. Return false means no available frame has been + // serialized, otherwise return true. + bool SerializeDom(); + // 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. + DomSerializer(WebFrame* webframe, + bool recursive_serialization, + DomSerializerDelegate* delegate, + const std::vector<std::wstring>& links, + const std::vector<std::wstring>& local_paths, + const std::wstring& local_directory_name); + + // Generate the MOTW declaration. + static std::wstring GenerateMarkOfTheWebDeclaration(const std::wstring& url); + // Generate the default base tag declaration. + static std::wstring GenerateBaseTagDeclaration( + const std::wstring& base_target); + + private: + // Specified frame which need to be serialized; + WebFrameImpl* specified_webframeimpl_; + // This hash_map is used to map resource URL of original link to its local + // file path. + typedef stdext::hash_map<std::wstring, std::wstring> LinkLocalPathMap; + // local_links_ include all pair of local resource path and corresponding + // original link. + LinkLocalPathMap local_links_; + // Pointer of DomSerializerDelegate + DomSerializerDelegate* delegate_; + // Data buffer for saving result of serialized DOM data. + std::string data_buffer_; + // 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 recursive_serialization_; + // Flag indicates whether we have collected all frames which need to be + // serialized or not; + bool frames_collected_; + // Local directory name of all local resource files. + const std::wstring& local_directory_name_; + // Vector for saving all frames which need to be serialized. + std::vector<WebFrameImpl*> frames_; + + struct SerializeDomParam { + // Frame URL of current processing document presented by GURL + const GURL& current_frame_gurl; + // Frame URL of current processing document presented by std::wstring. + const std::wstring& current_frame_wurl; + // Current using text encoding object. + const WebCore::TextEncoding& text_encoding; + + // Document object of current frame. + WebCore::Document* doc; + // Local directory name of all local resource files. + const std::wstring& directory_name; + + // Flag indicates current doc is html document or not. It's a cache value + // of Document.isHTMLDocument(). + bool is_html_document; + // Flag which indicate whether we have met document type declaration. + bool has_doctype; + // Flag which indicate whether will process meta issue. + bool has_checked_meta; + // This meta element need to be skipped when serializing DOM. + const WebCore::Element* skip_meta_element; + // Flag indicates we are in script or style tag. + bool is_in_script_or_style_tag; + // Flag indicates whether we have written xml document declaration. + // It is only used in xml document + bool has_doc_declaration; + + // Constructor. + SerializeDomParam( + const GURL& current_frame_gurl, + const std::wstring& current_frame_wurl, + const WebCore::TextEncoding& text_encoding, + WebCore::Document* doc, + const std::wstring& directory_name); + + private: + DISALLOW_EVIL_CONSTRUCTORS(SerializeDomParam); + }; + + // 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* need_skip); + // 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* need_skip); + // 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& result, + SerializeDomParam* param); + // 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); + + DISALLOW_EVIL_CONSTRUCTORS(DomSerializer); +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_DOM_SERIALIZER_H__ diff --git a/webkit/glue/dom_serializer_delegate.h b/webkit/glue/dom_serializer_delegate.h new file mode 100644 index 0000000..1012cd9 --- /dev/null +++ b/webkit/glue/dom_serializer_delegate.h @@ -0,0 +1,77 @@ +// Copyright 2008, 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 WEBKIT_GLUE_DOM_SERIALIZER_DELEGATE_H__ +#define WEBKIT_GLUE_DOM_SERIALIZER_DELEGATE_H__ + +#include <string> + +#include "googleurl/src/gurl.h" + +namespace webkit_glue { + +// This class is used for providing sink interface that can be used to receive +// the individual chunks of data to be saved. +class DomSerializerDelegate { + public: + // This enum indicates This sink interface can receive the individual chunks + // of serialized data to be saved, so we use values of following enum + // definition to indicate the serialization status of serializing all html + // content. If current frame is not complete serialized, call + // DidSerializeDataForFrame with URL of current frame, data, data length and + // flag CURRENT_FRAME_IS_NOT_FINISHED. + // If current frame is complete serialized, call DidSerializeDataForFrame + // with URL of current frame, data, data length and flag + // CURRENT_FRAME_IS_FINISHED. + // If all frames of page are complete serialized, call + // DidSerializeDataForFrame with empty URL, empty data, 0 and flag + // ALL_FRAMES_ARE_FINISHED. + enum PageSavingSerializationStatus { + // Current frame is not finished saving. + CURRENT_FRAME_IS_NOT_FINISHED = 0, + // Current frame is finished saving. + CURRENT_FRAME_IS_FINISHED, + // All frame are finished saving. + ALL_FRAMES_ARE_FINISHED, + }; + + // Receive the individual chunks of serialized data to be saved. + // The parameter frame_url specifies what frame the data belongs. The + // parameter data contains the available data for saving. The parameter + // status indicates the status of data serialization. + virtual void DidSerializeDataForFrame(const GURL& frame_url, + const std::string& data, PageSavingSerializationStatus status) = 0; + + DomSerializerDelegate() { } + virtual ~DomSerializerDelegate() { } +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_DOM_SERIALIZER_DELEGATE_H__ diff --git a/webkit/glue/dom_serializer_unittest.cc b/webkit/glue/dom_serializer_unittest.cc new file mode 100644 index 0000000..afc4db0 --- /dev/null +++ b/webkit/glue/dom_serializer_unittest.cc @@ -0,0 +1,702 @@ +// Copyright 2008, 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 <hash_map> + +#include "config.h" + +#pragma warning(push, 0) +#include "Document.h" +#include "DocumentType.h" +#include "Element.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLHeadElement.h" +#include "HTMLMetaElement.h" +#include "HTMLNames.h" +#include "KURL.h" +#include "SharedBuffer.h" +#include "SubstituteData.h" +#pragma warning(pop) +#undef LOG + +#include "base/file_util.h" +#include "base/string_util.h" +#include "net/base/net_util.h" +#include "net/url_request/url_request_context.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/dom_serializer.h" +#include "webkit/glue/dom_serializer_delegate.h" +#include "webkit/glue/webview.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/tools/test_shell/simple_resource_loader_bridge.h" +#include "webkit/tools/test_shell/test_shell_test.h" + +namespace { + +class DomSerializerTests : public TestShellTest, + public webkit_glue::DomSerializerDelegate { + public: + DomSerializerTests() + : local_directory_name_(L"./dummy_files/") { } + + // DomSerializerDelegate. + void DidSerializeDataForFrame(const GURL& frame_url, + const std::string& data, PageSavingSerializationStatus status) { + // If the all frames are finished saving, check all finish status + if (status == ALL_FRAMES_ARE_FINISHED) { + SerializationFinishStatusMap::iterator it = + serialization_finish_status_.begin(); + for (; it != serialization_finish_status_.end(); ++it) + ASSERT_TRUE(it->second); + serialized_ = true; + return; + } + + std::wstring current_frame_url = UTF8ToWide(frame_url.spec()); + // Check finish status of current frame. + SerializationFinishStatusMap::iterator it = + serialization_finish_status_.find(current_frame_url); + // New frame, set initial status as false. + if (it == serialization_finish_status_.end()) + serialization_finish_status_[current_frame_url] = false; + + it = serialization_finish_status_.find(current_frame_url); + ASSERT_TRUE(it != serialization_finish_status_.end()); + // In process frame, finish status should be false. + ASSERT_FALSE(it->second); + + // Add data to corresponding frame's content. + serialized_frame_map_[current_frame_url] += data; + + // Current frame is completed saving, change the finish status. + if (status == CURRENT_FRAME_IS_FINISHED) + it->second = true; + } + + bool HasSerializedFrame(const std::wstring& frame_url) { + return serialized_frame_map_.find(frame_url) != + serialized_frame_map_.end(); + } + + const std::string& GetSerializedContentForFrame( + const std::wstring& frame_url) { + return serialized_frame_map_[frame_url]; + } + + // Load web page according to specific URL. + void LoadPageFromURL(const std::wstring& page_url) { + // Load the test file. + test_shell_->ResetTestController(); + test_shell_->LoadURL(page_url.c_str()); + test_shell_->WaitTestFinished(); + } + + // Load web page according to input content and relative URLs within + // the document. + void LoadContents(const std::string& contents, + const GURL& base_url, + const WebCore::String encoding_info) { + test_shell_->ResetTestController(); + // If input encoding is empty, use UTF-8 as default encoding. + if (encoding_info.isEmpty()) { + test_shell_->webView()->GetMainFrame()->LoadHTMLString(contents, + base_url); + } else { + // Do not use WebFrame.LoadHTMLString because it assumes that input + // html contents use UTF-8 encoding. + WebFrameImpl* web_frame = + static_cast<WebFrameImpl*>(test_shell_->webView()->GetMainFrame()); + ASSERT_TRUE(web_frame != NULL); + int len = static_cast<int>(contents.size()); + RefPtr<WebCore::SharedBuffer> buf( + new WebCore::SharedBuffer(contents.data(), len)); + + WebCore::SubstituteData subst_data( + buf, WebCore::String("text/html"), encoding_info, WebCore::KURL()); + WebCore::ResourceRequest request(webkit_glue::GURLToKURL(base_url), + WebCore::CString()); + web_frame->frame()->loader()->load(request, subst_data); + } + + test_shell_->WaitTestFinished(); + } + + // Serialize page DOM according to specific page URL. The parameter + // recursive_serialization indicates whether we will serialize all + // sub-frames. + void SerializeDomForURL(const std::wstring& page_url, + bool recursive_serialization) { + // Find corresponding WebFrameImpl according to page_url. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), GURL(page_url)); + ASSERT_TRUE(web_frame != NULL); + // Add input file URl to links_. + links_.push_back(page_url); + // Add dummy file path to local_path_. + local_paths_.push_back(std::wstring(L"c:\\dummy.htm")); + // Start serializing DOM. + webkit_glue::DomSerializer dom_serializer(web_frame, + recursive_serialization, this, links_, local_paths_, + local_directory_name_); + ASSERT_TRUE(dom_serializer.SerializeDom()); + ASSERT_TRUE(serialized_); + } + + private: + // Map frame_url to corresponding serialized_content. + typedef stdext::hash_map<std::wstring, std::string> SerializedFrameContentMap; + SerializedFrameContentMap serialized_frame_map_; + // Map frame_url to corresponding status of serialization finish. + typedef stdext::hash_map<std::wstring, bool> SerializationFinishStatusMap; + SerializationFinishStatusMap serialization_finish_status_; + // Flag indicates whether the process of serializing DOM is finished or not. + bool serialized_; + // The links_ contain dummy original URLs of all saved links. + std::vector<std::wstring> links_; + // The local_paths_ contain dummy corresponding local file paths of all saved + // links, which matched links_ one by one. + std::vector<std::wstring> local_paths_; + // The local_directory_name_ is dummy relative path of directory which + // contain all saved auxiliary files included all sub frames and resources. + const std::wstring local_directory_name_; + + protected: + // testing::Test + virtual void SetUp() { + TestShellTest::SetUp(); + serialized_ = false; + } + + virtual void TearDown() { + TestShellTest::TearDown(); + } +}; + +// Helper function for checking whether input node is META tag. Return true +// means it is META element, otherwise return false. The parameter charset_info +// return actual charset info if the META tag has charset declaration. +bool IsMetaElement(const WebCore::Node* node, WebCore::String* charset_info) { + if (!node->isHTMLElement()) + return false; + if (!(static_cast<const WebCore::HTMLElement*>(node))->hasTagName( + WebCore::HTMLNames::metaTag)) + return false; + charset_info->remove(0, charset_info->length()); + const WebCore::HTMLMetaElement* meta = + static_cast<const WebCore::HTMLMetaElement*>(node); + // Check the META charset declaration. + WebCore::String equiv = meta->httpEquiv(); + if (equalIgnoringCase(equiv, "content-type")) { + WebCore::String content = meta->content(); + int pos = content.find("charset", 0, false); + if (pos > -1) { + // Add a dummy charset declaration to charset_info, which indicates this + // META tag has charset declaration although we do not get correct value + // yet. + charset_info->append("has-charset-declaration"); + int remaining_length = content.length() - pos - 7; + if (!remaining_length) + return true; + const UChar* start_pos = content.characters() + pos + 7; + // Find "=" symbol. + while (remaining_length--) + if (*start_pos++ == L'=') + break; + // Skip beginning space. + while (remaining_length) { + if (*start_pos > 0x0020) + break; + ++start_pos; + --remaining_length; + } + if (!remaining_length) + return true; + const UChar* end_pos = start_pos; + // Now we find out the start point of charset info. Search the end point. + while (remaining_length--) { + if (*end_pos <= 0x0020 || *end_pos == L';') + break; + ++end_pos; + } + // Get actual charset info. + *charset_info = WebCore::String(start_pos, + static_cast<unsigned>(end_pos - start_pos)); + return true; + } + } + return true; +} + +} // namespace + +// If original contents have document type, the serialized contents also have +// document type. +TEST_F(DomSerializerTests, SerialzeHTMLDOMWithDocType) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer/youtube_1.htm"); + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + // Make sure original contents have document type. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), file_url); + ASSERT_TRUE(web_frame != NULL); + WebCore::Document* doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->doctype() != NULL); + // Do serialization. + SerializeDomForURL(page_url, false); + // Load the serialized contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + LoadContents(serialized_contents, GURL(page_url), + web_frame->frame()->loader()->encoding()); + // Make sure serialized contents still have document type. + web_frame = + static_cast<WebFrameImpl*>(test_shell_->webView()->GetMainFrame()); + doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->doctype() != NULL); +} + +// If original contents do not have document type, the serialized contents +// also do not have document type. +TEST_F(DomSerializerTests, SerialzeHTMLDOMWithoutDocType) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer/youtube_2.htm"); + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + // Make sure original contents do not have document type. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), file_url); + ASSERT_TRUE(web_frame != NULL); + WebCore::Document* doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->doctype() == NULL); + // Do serialization. + SerializeDomForURL(page_url, false); + // Load the serialized contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + LoadContents(serialized_contents, GURL(page_url), + web_frame->frame()->loader()->encoding()); + // Make sure serialized contents do not have document type. + web_frame = + static_cast<WebFrameImpl*>(test_shell_->webView()->GetMainFrame()); + doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->doctype() == NULL); +} + +// Serialize XML document which has all 5 built-in entities. After +// finishing serialization, the serialized contents should be same +// with original XML document. +TEST_F(DomSerializerTests, SerialzeXMLDocWithBuiltInEntities) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer/note.xml"); + // Read original contents for later comparison. + std::string orginal_contents; + ASSERT_TRUE(file_util::ReadFileToString(page_file_path, &orginal_contents)); + // Get file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + // Do serialization. + SerializeDomForURL(page_url, false); + // Compare the serialized contents with original contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + ASSERT_EQ(serialized_contents, orginal_contents); +} + +// When serializing DOM, we add MOTW declaration before html tag. +TEST_F(DomSerializerTests, SerialzeHTMLDOMWithAddingMOTW) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer/youtube_2.htm"); + // Read original contents for later comparison . + std::string orginal_contents; + ASSERT_TRUE(file_util::ReadFileToString(page_file_path, &orginal_contents)); + // Get file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Make sure original contents does not have MOTW; + std::wstring motw_declaration = + webkit_glue::DomSerializer::GenerateMarkOfTheWebDeclaration(page_url); + ASSERT_FALSE(motw_declaration.empty()); + // The encoding of original contents is ISO-8859-1, so we convert the MOTW + // declaration to ASCII and search whether original contents has it or not. + ASSERT_TRUE(std::wstring::npos == + orginal_contents.find(WideToASCII(motw_declaration))); + // Load the test file. + LoadPageFromURL(page_url); + // Do serialization. + SerializeDomForURL(page_url, false); + // Make sure the serialized contents have MOTW ; + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + ASSERT_TRUE(std::wstring::npos != + serialized_contents.find(WideToUTF8(motw_declaration))); +} + +// When serializing DOM, we will add the META which have correct charset +// declaration as first child of HEAD element for resolving WebKit bug: +// http://bugs.webkit.org/show_bug.cgi?id=16621 even the original document +// does not have META charset declaration. +TEST_F(DomSerializerTests, SerialzeHTMLDOMWithNoMetaCharsetInOriginalDoc) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer/youtube_1.htm"); + // Get file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + + // Make sure there is no META charset declaration in original document. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), file_url); + ASSERT_TRUE(web_frame != NULL); + WebCore::Document* doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + WebCore::HTMLHeadElement* head_ele = doc->head(); + ASSERT_TRUE(head_ele != NULL); + // Go through all children of HEAD element. + WebCore::String charset_info; + for (const WebCore::Node *child = head_ele->firstChild(); child != NULL; + child = child->nextSibling()) + if (IsMetaElement(child, &charset_info)) + ASSERT_TRUE(charset_info.isEmpty()); + + // Do serialization. + SerializeDomForURL(page_url, false); + + // Load the serialized contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + LoadContents(serialized_contents, GURL(page_url), + web_frame->frame()->loader()->encoding()); + // Make sure the first child of HEAD element is META which has charset + // declaration in serialized contents. + web_frame = + static_cast<WebFrameImpl*>(test_shell_->webView()->GetMainFrame()); + ASSERT_TRUE(web_frame != NULL); + doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + head_ele = doc->head(); + ASSERT_TRUE(head_ele != NULL); + WebCore::Node* meta_node = head_ele->firstChild(); + ASSERT_TRUE(meta_node != NULL); + // Get meta charset info. + ASSERT_TRUE(IsMetaElement(meta_node, &charset_info)); + ASSERT_TRUE(!charset_info.isEmpty()); + ASSERT_TRUE(charset_info == web_frame->frame()->loader()->encoding()); + + // Make sure no more additional META tags which have charset declaration. + for (const WebCore::Node *child = meta_node->nextSibling(); child != NULL; + child = child->nextSibling()) + if (IsMetaElement(child, &charset_info)) + ASSERT_TRUE(charset_info.isEmpty()); +} + +// When serializing DOM, if the original document has multiple META charset +// declaration, we will add the META which have correct charset declaration +// as first child of HEAD element and remove all original META charset +// declarations. +TEST_F(DomSerializerTests, + SerialzeHTMLDOMWithMultipleMetaCharsetInOriginalDoc) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer/youtube_2.htm"); + // Get file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + + // Make sure there are multiple META charset declarations in original + // document. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), file_url); + ASSERT_TRUE(web_frame != NULL); + WebCore::Document* doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + WebCore::HTMLHeadElement* head_ele = doc->head(); + ASSERT_TRUE(head_ele != NULL); + // Go through all children of HEAD element. + int charset_declaration_count = 0; + WebCore::String charset_info; + for (const WebCore::Node *child = head_ele->firstChild(); child != NULL; + child = child->nextSibling()) { + if (IsMetaElement(child, &charset_info) && !charset_info.isEmpty()) + charset_declaration_count++; + } + // The original doc has more than META tags which have charset declaration. + ASSERT(charset_declaration_count > 1); + + // Do serialization. + SerializeDomForURL(page_url, false); + + // Load the serialized contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + LoadContents(serialized_contents, GURL(page_url), + web_frame->frame()->loader()->encoding()); + // Make sure only first child of HEAD element is META which has charset + // declaration in serialized contents. + web_frame = + static_cast<WebFrameImpl*>(test_shell_->webView()->GetMainFrame()); + ASSERT_TRUE(web_frame != NULL); + doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + head_ele = doc->head(); + ASSERT_TRUE(head_ele != NULL); + WebCore::Node* meta_node = head_ele->firstChild(); + ASSERT_TRUE(meta_node != NULL); + // Get meta charset info. + ASSERT_TRUE(IsMetaElement(meta_node, &charset_info)); + ASSERT_TRUE(!charset_info.isEmpty()); + ASSERT_TRUE(charset_info == web_frame->frame()->loader()->encoding()); + + // Make sure no more additional META tags which have charset declaration. + for (const WebCore::Node *child = meta_node->nextSibling(); child != NULL; + child = child->nextSibling()) + if (IsMetaElement(child, &charset_info)) + ASSERT_TRUE(charset_info.isEmpty()); +} + +// Test situation of html entities in text when serializing HTML DOM. +TEST_F(DomSerializerTests, SerialzeHTMLDOMWithEntitiesInText) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, + L"dom_serializer/htmlentities_in_text.htm"); + // Read original contents for later comparison . + std::string orginal_contents; + ASSERT_TRUE(file_util::ReadFileToString(page_file_path, &orginal_contents)); + // Get file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + // Get BODY's text content in DOM. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), file_url); + ASSERT_TRUE(web_frame != NULL); + WebCore::Document* doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + WebCore::HTMLElement* body_ele = doc->body(); + ASSERT_TRUE(body_ele != NULL); + WebCore::Node* text_node = body_ele->firstChild(); + ASSERT_TRUE(text_node->isTextNode()); + ASSERT_TRUE(text_node->toString() == WebCore::String("&<>\"\'")); + // Do serialization. + SerializeDomForURL(page_url, false); + // Compare the serialized contents with original contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + // Because we add MOTW when serializing DOM, so before comparison, we also + // need to add MOTW to original_contents. + std::wstring motw_declaration = + webkit_glue::DomSerializer::GenerateMarkOfTheWebDeclaration(page_url); + orginal_contents = WideToASCII(motw_declaration) + orginal_contents; + ASSERT_EQ(serialized_contents, orginal_contents); +} + +// Test situation of html entities in attribute value when serializing +// HTML DOM. +TEST_F(DomSerializerTests, SerialzeHTMLDOMWithEntitiesInAttributeValue) { + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, + L"dom_serializer/htmlentities_in_attribute_value.htm"); + // Read original contents for later comparison. + std::string orginal_contents; + ASSERT_TRUE(file_util::ReadFileToString(page_file_path, &orginal_contents)); + // Get file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + // Get value of BODY's title attribute in DOM. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), file_url); + ASSERT_TRUE(web_frame != NULL); + WebCore::Document* doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + WebCore::HTMLElement* body_ele = doc->body(); + ASSERT_TRUE(body_ele != NULL); + const WebCore::String& value = body_ele->getAttribute( + WebCore::HTMLNames::titleAttr); + ASSERT_TRUE(value == WebCore::String("&<>\"\'")); + // Do serialization. + SerializeDomForURL(page_url, false); + // Compare the serialized contents with original contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + // Because we add MOTW when serializing DOM, so before comparison, we also + // need to add MOTW to original_contents. + std::wstring motw_declaration = + webkit_glue::DomSerializer::GenerateMarkOfTheWebDeclaration(page_url); + orginal_contents = WideToASCII(motw_declaration) + orginal_contents; + ASSERT_EQ(serialized_contents, orginal_contents); +} + +// Test situation of BASE tag in original document when serializing HTML DOM. +// When serializing, we should comment the BASE tag, append a new BASE tag. +// rewrite all the savable URLs to relative local path, and change other URLs +// to absolute URLs. +TEST_F(DomSerializerTests, SerialzeHTMLDOMWithBaseTag) { + // There are total 2 available base tags in this test file. + const int kTotalBaseTagCountInTestFile = 2; + std::wstring page_file_path = data_dir_; + file_util::AppendToPath(&page_file_path, L"dom_serializer\\"); + // Get page dir URL which is base URL of this file. + GURL path_dir_url = net_util::FilePathToFileURL(page_file_path); + // Get file path. + file_util::AppendToPath(&page_file_path, L"html_doc_has_base_tag.htm"); + // Get file URL. + GURL file_url = net_util::FilePathToFileURL(page_file_path); + ASSERT_TRUE(file_url.SchemeIsFile()); + std::wstring page_url = ASCIIToWide(file_url.spec()); + // Load the test file. + LoadPageFromURL(page_url); + // Since for this test, we assume there is no savable sub-resource links for + // this test file, also all links are relative URLs in this test file, so we + // need to check those relative URLs and make sure document has BASE tag. + WebFrameImpl* web_frame = + webkit_glue::GetWebFrameImplFromWebViewForSpecificURL( + test_shell_->webView(), file_url); + ASSERT_TRUE(web_frame != NULL); + WebCore::Document* doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + // Go through all descent nodes. + RefPtr<WebCore::HTMLCollection> all = doc->all(); + int original_base_tag_count = 0; + for (WebCore::Node* node = all->firstItem(); node != NULL; + node = all->nextItem()) { + if (!node->isHTMLElement()) + continue; + WebCore::Element* element = static_cast<WebCore::Element*>(node); + if (element->hasTagName(WebCore::HTMLNames::baseTag)) { + original_base_tag_count++; + } else { + // Get link. + const WebCore::AtomicString* value = + webkit_glue::GetSubResourceLinkFromElement(element); + if (!value && element->hasTagName(WebCore::HTMLNames::aTag)) { + value = &element->getAttribute(WebCore::HTMLNames::hrefAttr); + if (value->domString().isEmpty()) + value = NULL; + } + // Each link is relative link. + if (value) { + GURL link(webkit_glue::StringToStdWString(value->domString()).c_str()); + ASSERT_TRUE(link.scheme().empty()); + } + } + } + ASSERT_TRUE(original_base_tag_count == kTotalBaseTagCountInTestFile); + // Make sure in original document, the base URL is not equal with the + // |path_dir_url|. + GURL original_base_url( + webkit_glue::DeprecatedStringToStdWString(doc->baseURL()).c_str()); + ASSERT_TRUE(original_base_url != path_dir_url); + + // Do serialization. + SerializeDomForURL(page_url, false); + + // Load the serialized contents. + ASSERT_TRUE(HasSerializedFrame(page_url)); + const std::string& serialized_contents = + GetSerializedContentForFrame(page_url); + LoadContents(serialized_contents, GURL(page_url), + web_frame->frame()->loader()->encoding()); + + // Make sure all links are absolute URLs and doc there are some number of + // BASE tags in serialized HTML data. Each of those BASE tags have same base + // URL which is as same as URL of current test file. + web_frame = + static_cast<WebFrameImpl*>(test_shell_->webView()->GetMainFrame()); + ASSERT_TRUE(web_frame != NULL); + doc = web_frame->frame()->document(); + ASSERT_TRUE(doc->isHTMLDocument()); + // Go through all descent nodes. + all = doc->all(); + int new_base_tag_count = 0; + for (WebCore::Node* node = all->firstItem(); node != NULL; + node = all->nextItem()) { + if (!node->isHTMLElement()) + continue; + WebCore::Element* element = static_cast<WebCore::Element*>(node); + if (element->hasTagName(WebCore::HTMLNames::baseTag)) { + new_base_tag_count++; + } else { + // Get link. + const WebCore::AtomicString* value = + webkit_glue::GetSubResourceLinkFromElement(element); + if (!value && element->hasTagName(WebCore::HTMLNames::aTag)) { + value = &element->getAttribute(WebCore::HTMLNames::hrefAttr); + if (value->domString().isEmpty()) + value = NULL; + } + // Each link is absolute link. + if (value) { + GURL link(webkit_glue::StringToStdWString(value->domString()).c_str()); + ASSERT_FALSE(link.scheme().empty()); + } + } + } + // We have one more added BASE tag which is generated by JavaScript. + ASSERT_TRUE(new_base_tag_count == original_base_tag_count + 1); + // Make sure in new document, the base URL is equal with the |path_dir_url|. + GURL new_base_url( + webkit_glue::DeprecatedStringToStdWString(doc->baseURL()).c_str()); + ASSERT_TRUE(new_base_url == path_dir_url); +} diff --git a/webkit/glue/dragclient_impl.cc b/webkit/glue/dragclient_impl.cc new file mode 100644 index 0000000..f3063fa9 --- /dev/null +++ b/webkit/glue/dragclient_impl.cc @@ -0,0 +1,107 @@ +// Copyright 2008, 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 <objidl.h> + +#pragma warning(push, 0) +#include "ClipboardWin.h" +#include "COMPtr.h" +#include "DragData.h" +#include "Frame.h" +#include "HitTestResult.h" +#include "Image.h" +#include "KURL.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/dragclient_impl.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "webkit/glue/context_node_types.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webdropdata.h" +#include "webkit/glue/webview_delegate.h" +#include "webkit/glue/webview_impl.h" + +void DragClientImpl::willPerformDragDestinationAction( + WebCore::DragDestinationAction, + WebCore::DragData*) { + // FIXME +} + +void DragClientImpl::willPerformDragSourceAction( + WebCore::DragSourceAction, + const WebCore::IntPoint&, + WebCore::Clipboard*) { + // FIXME +} + +WebCore::DragDestinationAction DragClientImpl::actionMaskForDrag( + WebCore::DragData*) { + return WebCore::DragDestinationActionAny; +} + +WebCore::DragSourceAction DragClientImpl::dragSourceActionMaskForPoint( + const WebCore::IntPoint& window_point) { + // We want to handle drag operations for all source types. + return WebCore::DragSourceActionAny; +} + +void DragClientImpl::startDrag(WebCore::DragImageRef drag_image, + const WebCore::IntPoint& drag_image_origin, + const WebCore::IntPoint& event_pos, + WebCore::Clipboard* clipboard, + WebCore::Frame* frame, + bool is_link_drag) { + // Add a ref to the frame just in case a load occurs mid-drag. + RefPtr<WebCore::Frame> frame_protector = frame; + + COMPtr<IDataObject> data_object( + static_cast<WebCore::ClipboardWin*>(clipboard)->dataObject()); + DCHECK(data_object.get()); + WebDropData drop_data; + WebDropData::PopulateWebDropData(data_object.get(), &drop_data); + + webview_->StartDragging(drop_data); +} + +WebCore::DragImageRef DragClientImpl::createDragImageForLink( + WebCore::KURL&, + const WebCore::String& label, + WebCore::Frame*) { + // FIXME + return 0; +} + +void DragClientImpl::dragControllerDestroyed() { + delete this; +} diff --git a/webkit/glue/dragclient_impl.h b/webkit/glue/dragclient_impl.h new file mode 100644 index 0000000..1a2b2a3 --- /dev/null +++ b/webkit/glue/dragclient_impl.h @@ -0,0 +1,79 @@ +// Copyright 2008, 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 WEBKIT_GLUE_DRAGCLIENT_IMPL_H__ +#define WEBKIT_GLUE_DRAGCLIENT_IMPL_H__ + +#include "base/basictypes.h" + +#pragma warning(push, 0) +#include "DragClient.h" +#include "DragActions.h" +#pragma warning(pop) + +namespace WebCore { +class ClipBoard; +class DragData; +class IntPoint; +class KURL; +} + +class WebViewImpl; + +class DragClientImpl : public WebCore::DragClient { +public: + DragClientImpl(WebViewImpl* webview) : webview_(webview) {} + virtual ~DragClientImpl() {} + + virtual void willPerformDragDestinationAction(WebCore::DragDestinationAction, + WebCore::DragData*); + virtual void willPerformDragSourceAction(WebCore::DragSourceAction, + const WebCore::IntPoint&, + WebCore::Clipboard*); + virtual WebCore::DragDestinationAction actionMaskForDrag(WebCore::DragData*); + virtual WebCore::DragSourceAction dragSourceActionMaskForPoint( + const WebCore::IntPoint& window_point); + + virtual void startDrag(WebCore::DragImageRef drag_image, + const WebCore::IntPoint& drag_image_origin, + const WebCore::IntPoint& event_pos, + WebCore::Clipboard* clipboard, + WebCore::Frame* frame, + bool is_link_drag = false); + virtual WebCore::DragImageRef createDragImageForLink( + WebCore::KURL&, const WebCore::String& label, WebCore::Frame*); + + virtual void dragControllerDestroyed(); + +private: + DISALLOW_EVIL_CONSTRUCTORS(DragClientImpl); + WebViewImpl* webview_; +}; + +#endif // #ifndef WEBKIT_GLUE_DRAGCLIENT_IMPL_H__ diff --git a/webkit/glue/editor_client_impl.cc b/webkit/glue/editor_client_impl.cc new file mode 100644 index 0000000..50545d6 --- /dev/null +++ b/webkit/glue/editor_client_impl.cc @@ -0,0 +1,786 @@ +// Copyright 2008, 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. + +// The Mac interface forwards most of these commands to the application layer, +// and I'm not really sure what to do about most of them. + +#include "config.h" +#pragma warning(push, 0) +#include "Document.h" +#include "EditCommand.h" +#include "Editor.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "HTMLInputElement.h" +#include "Frame.h" +#include "KeyboardEvent.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformString.h" +#include "WebView.h" +#pragma warning(pop) + +#undef LOG + +#include "base/string_util.h" +#include "webkit/glue/editor_client_impl.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webview_impl.h" + +// The notImplemented() from NotImplemented.h is now being dragged in via +// webview_impl.h. We want the one from LogWin.h instead. +#undef notImplemented +#include "LogWin.h" + +// Arbitrary depth limit for the undo stack, to keep it from using +// unbounded memory. This is the maximum number of distinct undoable +// actions -- unbroken stretches of typed characters are coalesced +// into a single action. +static const int kMaximumUndoStackDepth = 1000; + +namespace { + +// Record an editor command from the keyDownEntries[] below. We ignore the +// Move* and Insert* commands because they're not that interesting. +void MaybeRecordCommand(WebViewDelegate* d, const char* command_name) { + if (!d) + return; + + const char* move_prefix = "Move"; + const char* insert_prefix = "Insert"; + const char* delete_prefix = "Delete"; + // Ignore all the Move*, Insert*, and Delete* commands. + if (0 == strncmp(command_name, move_prefix, sizeof(move_prefix)) || + 0 == strncmp(command_name, insert_prefix, sizeof(insert_prefix)) || + 0 == strncmp(command_name, delete_prefix, sizeof(delete_prefix))) { + return; + } + d->UserMetricsRecordComputedAction(UTF8ToWide(command_name)); +} + +} + +EditorClientImpl::EditorClientImpl(WebView* web_view) + : web_view_(static_cast<WebViewImpl*>(web_view)), + use_editor_delegate_(false), + in_redo_(false), + preserve_(false), + pending_inline_autocompleted_element_(NULL) { +} + +EditorClientImpl::~EditorClientImpl() { +} + +void EditorClientImpl::pageDestroyed() { + // Called by the Page (which owns the editor client) when the page is going + // away. This should cause us to delete ourselves, which is stupid. The page + // should just delete us when it's going away. Oh well. + delete this; +} + +bool EditorClientImpl::shouldShowDeleteInterface(WebCore::HTMLElement* elem) { + // Normally, we don't care to show WebCore's deletion UI, so we only enable + // it if in testing mode and the test specifically requests it by using this + // magic class name. + return webkit_glue::IsLayoutTestMode() && + elem->className() == "needsDeletionUI"; +} + +bool EditorClientImpl::smartInsertDeleteEnabled() { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + return d->SmartInsertDeleteEnabled(); + } + return true; +} + +bool EditorClientImpl::isContinuousSpellCheckingEnabled() { + // Spell check everything if possible. + // FIXME(brettw) This should be modified to do reasonable defaults depending + // on input type, and probably also allow the user to turn spellchecking on + // for individual fields. + return true; +} + +void EditorClientImpl::toggleContinuousSpellChecking() { + notImplemented(); +} + +bool EditorClientImpl::isGrammarCheckingEnabled() { + notImplemented(); + return false; +} + +void EditorClientImpl::toggleGrammarChecking() { + notImplemented(); +} + +int EditorClientImpl::spellCheckerDocumentTag() { + notImplemented(); + return 0; +} + +bool EditorClientImpl::isEditable() { + notImplemented(); + return false; +} + +bool EditorClientImpl::shouldBeginEditing(WebCore::Range* range) { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + return d->ShouldBeginEditing(web_view_, Describe(range)); + } + return true; +} + +bool EditorClientImpl::shouldEndEditing(WebCore::Range* range) { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + return d->ShouldEndEditing(web_view_, Describe(range)); + } + return true; +} + +bool EditorClientImpl::shouldInsertNode(WebCore::Node* node, + WebCore::Range* range, + WebCore::EditorInsertAction action) { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) { + return d->ShouldInsertNode(web_view_, + Describe(node), + Describe(range), + Describe(action)); + } + } + return true; +} + +bool EditorClientImpl::shouldInsertText(WebCore::String text, + WebCore::Range* range, + WebCore::EditorInsertAction action) { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) { + std::wstring wstr = webkit_glue::StringToStdWString(text); + return d->ShouldInsertText(web_view_, + wstr, + Describe(range), + Describe(action)); + } + } + return true; +} + + +bool EditorClientImpl::shouldDeleteRange(WebCore::Range* range) { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + return d->ShouldDeleteRange(web_view_, Describe(range)); + } + return true; +} + +void EditorClientImpl::PreserveSelection() { + preserve_ = true; +} + +bool EditorClientImpl::shouldChangeSelectedRange(WebCore::Range* fromRange, + WebCore::Range* toRange, + WebCore::EAffinity affinity, + bool stillSelecting) { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) { + return d->ShouldChangeSelectedRange(web_view_, + Describe(fromRange), + Describe(toRange), + Describe(affinity), + stillSelecting); + } + } + // Have we been told to preserve the selection? + // (See comments for PreserveSelection in header). + if (preserve_) { + preserve_ = false; + return false; + } + return true; +} + +bool EditorClientImpl::shouldApplyStyle(WebCore::CSSStyleDeclaration* style, + WebCore::Range* range) { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + return d->ShouldApplyStyle(web_view_, Describe(style), Describe(range)); + } + return true; +} + +bool EditorClientImpl::shouldMoveRangeAfterDelete( + WebCore::Range* /*range*/, + WebCore::Range* /*rangeToBeReplaced*/) { + notImplemented(); + return true; +} + +void EditorClientImpl::didBeginEditing() { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + d->DidBeginEditing(); + } +} + +void EditorClientImpl::respondToChangedSelection() { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + d->DidChangeSelection(); + } +} + +void EditorClientImpl::respondToChangedContents() { + // Ugly Hack. (See also webkit bug #16976). + // Something is wrong with webcore's focusController in that when selection + // is set to a region within a text element when handling an input event, if + // you don't re-focus the node then it only _APPEARS_ to have successfully + // changed the selection (the UI "looks" right) but in reality there is no + // selection of text. And to make matters worse, you can't just re-focus it, + // you have to re-focus it in code executed after the entire event listener + // loop has finished; and hence here we are. Oh, and to make matters worse, + // this sequence of events _doesn't_ happen when you debug through the code + // -- in that case it works perfectly fine -- because swapping to the debugger + // causes the refocusing we artificially reproduce here. + // TODO (timsteele): Clean this up once root webkit problem is identified and + // the bug is patched. + if (pending_inline_autocompleted_element_) { + pending_inline_autocompleted_element_->blur(); + pending_inline_autocompleted_element_->focus(); + pending_inline_autocompleted_element_ = NULL; + } + + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + d->DidChangeContents(); + } +} + +void EditorClientImpl::didEndEditing() { + if (use_editor_delegate_) { + WebViewDelegate* d = web_view_->delegate(); + if (d) + d->DidEndEditing(); + } +} + +void EditorClientImpl::didWriteSelectionToPasteboard() { + notImplemented(); +} + +void EditorClientImpl::didSetSelectionTypesForPasteboard() { + notImplemented(); +} + +void EditorClientImpl::registerCommandForUndo( + PassRefPtr<WebCore::EditCommand> command) { + if (undo_stack_.size() == kMaximumUndoStackDepth) + undo_stack_.pop_front(); // drop oldest item off the far end + if (!in_redo_) + redo_stack_.clear(); + undo_stack_.push_back(command); +} + +void EditorClientImpl::registerCommandForRedo( + PassRefPtr<WebCore::EditCommand> command) { + redo_stack_.push_back(command); +} + +void EditorClientImpl::clearUndoRedoOperations() { + undo_stack_.clear(); + redo_stack_.clear(); +} + +bool EditorClientImpl::canUndo() const { + return !undo_stack_.empty(); +} + +bool EditorClientImpl::canRedo() const { + return !redo_stack_.empty(); +} + +void EditorClientImpl::undo() { + if (canUndo()) { + RefPtr<WebCore::EditCommand> command(undo_stack_.back()); + undo_stack_.pop_back(); + command->unapply(); + // unapply will call us back to push this command onto the redo stack. + } +} + +void EditorClientImpl::redo() { + if (canRedo()) { + RefPtr<WebCore::EditCommand> command(redo_stack_.back()); + redo_stack_.pop_back(); + + ASSERT(!in_redo_); + in_redo_ = true; + command->reapply(); + // reapply will call us back to push this command onto the undo stack. + in_redo_ = false; + } +} + +// Get the distance we should go (in pixels) when doing a pageup/pagedown. +static int GetVerticalPageDistance(WebCore::KeyboardEvent* event) { + if (event->target()) { + WebCore::Node* node = event->target()->toNode(); + if (node && node->renderer()) + return node->renderer()->contentHeight(); + } + + return 0; +} + +// The below code was adapted from the WebKit file webview.cpp +// provided by Apple, Inc, and is subject to the following copyright +// notice and disclaimer. + +/* + * Copyright (C) 2006, 2007 Apple, 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ + +static const unsigned CtrlKey = 1 << 0; +static const unsigned AltKey = 1 << 1; +static const unsigned ShiftKey = 1 << 2; + +// Keys with special meaning. These will be delegated to the editor using +// the execCommand() method +struct KeyDownEntry { + unsigned virtualKey; + unsigned modifiers; + const char* name; +}; + +struct KeyPressEntry { + unsigned charCode; + unsigned modifiers; + const char* name; +}; + +static const KeyDownEntry keyDownEntries[] = { + { VK_LEFT, 0, "MoveLeft" }, + { VK_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, + { VK_LEFT, CtrlKey, "MoveWordLeft" }, + { VK_LEFT, CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection" }, + { VK_RIGHT, 0, "MoveRight" }, + { VK_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, + { VK_RIGHT, CtrlKey, "MoveWordRight" }, + { VK_RIGHT, CtrlKey | ShiftKey, "MoveWordRightAndModifySelection" }, + { VK_UP, 0, "MoveUp" }, + { VK_UP, ShiftKey, "MoveUpAndModifySelection" }, + { VK_PRIOR, ShiftKey, "MovePageUpAndModifySelection" }, + { VK_DOWN, 0, "MoveDown" }, + { VK_DOWN, ShiftKey, "MoveDownAndModifySelection" }, + { VK_NEXT, ShiftKey, "MovePageDownAndModifySelection" }, + { VK_PRIOR, 0, "MovePageUp" }, + { VK_NEXT, 0, "MovePageDown" }, + { VK_HOME, 0, "MoveToBeginningOfLine" }, + { VK_HOME, ShiftKey, "MoveToBeginningOfLineAndModifySelection" }, + { VK_HOME, CtrlKey, "MoveToBeginningOfDocument" }, + { VK_HOME, CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" }, + + { VK_END, 0, "MoveToEndOfLine" }, + { VK_END, ShiftKey, "MoveToEndOfLineAndModifySelection" }, + { VK_END, CtrlKey, "MoveToEndOfDocument" }, + { VK_END, CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection" }, + + { VK_BACK, 0, "DeleteBackward" }, + { VK_BACK, ShiftKey, "DeleteBackward" }, + { VK_DELETE, 0, "DeleteForward" }, + { VK_BACK, CtrlKey, "DeleteWordBackward" }, + { VK_DELETE, CtrlKey, "DeleteWordForward" }, + + { 'B', CtrlKey, "ToggleBold" }, + { 'I', CtrlKey, "ToggleItalic" }, + { 'U', CtrlKey, "ToggleUnderline" }, + + { VK_ESCAPE, 0, "Cancel" }, + { VK_OEM_PERIOD, CtrlKey, "Cancel" }, + { VK_TAB, 0, "InsertTab" }, + { VK_TAB, ShiftKey, "InsertBacktab" }, + { VK_RETURN, 0, "InsertNewline" }, + { VK_RETURN, CtrlKey, "InsertNewline" }, + { VK_RETURN, AltKey, "InsertNewline" }, + { VK_RETURN, AltKey | ShiftKey, "InsertNewline" }, + { VK_RETURN, ShiftKey, "InsertLineBreak" }, + + { 'C', CtrlKey, "Copy" }, + { VK_INSERT, CtrlKey, "Copy" }, + { 'V', CtrlKey, "Paste" }, + { VK_INSERT, ShiftKey, "Paste" }, + { 'V', CtrlKey | ShiftKey, "PasteAndMatchStyle" }, + { 'X', CtrlKey, "Cut" }, + { VK_DELETE, ShiftKey, "Cut" }, + { 'A', CtrlKey, "SelectAll" }, + { 'Z', CtrlKey, "Undo" }, + { 'Z', CtrlKey | ShiftKey, "Redo" }, + { 'Y', CtrlKey, "Redo" }, +}; + +static const KeyPressEntry keyPressEntries[] = { + { '\t', 0, "InsertTab" }, + { '\t', ShiftKey, "InsertBacktab" }, + { '\r', 0, "InsertNewline" }, + { '\r', CtrlKey, "InsertNewline" }, + { '\r', ShiftKey, "InsertLineBreak" }, + { '\r', AltKey, "InsertNewline" }, + { '\r', AltKey | ShiftKey, "InsertNewline" }, +}; + +const char* EditorClientImpl::interpretKeyEvent( + const WebCore::KeyboardEvent* evt) { + const WebCore::PlatformKeyboardEvent* keyEvent = evt->keyEvent(); + if (!keyEvent) + return ""; + + static HashMap<int, const char*>* keyDownCommandsMap = 0; + static HashMap<int, const char*>* keyPressCommandsMap = 0; + + if (!keyDownCommandsMap) { + keyDownCommandsMap = new HashMap<int, const char*>; + keyPressCommandsMap = new HashMap<int, const char*>; + + for (unsigned i = 0; i < _countof(keyDownEntries); i++) { + keyDownCommandsMap->set( + keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, + keyDownEntries[i].name); + } + + for (unsigned i = 0; i < _countof(keyPressEntries); i++) { + keyPressCommandsMap->set( + keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, + keyPressEntries[i].name); + } + } + + unsigned modifiers = 0; + if (keyEvent->shiftKey()) + modifiers |= ShiftKey; + if (keyEvent->altKey()) + modifiers |= AltKey; + if (keyEvent->ctrlKey()) + modifiers |= CtrlKey; + + if (evt->type() == WebCore::EventNames::keydownEvent) { + int mapKey = modifiers << 16 | evt->keyCode(); + return mapKey ? keyDownCommandsMap->get(mapKey) : 0; + } + + int mapKey = modifiers << 16 | evt->charCode(); + return mapKey ? keyPressCommandsMap->get(mapKey) : 0; +} + +bool EditorClientImpl::handleEditingKeyboardEvent( + WebCore::KeyboardEvent* evt) { + const WebCore::PlatformKeyboardEvent* keyEvent = evt->keyEvent(); + // do not treat this as text input if it's a system key event + if (!keyEvent || keyEvent->isSystemKey()) + return false; + + WebCore::Frame* frame = evt->target()->toNode()->document()->frame(); + if (!frame) + return false; + + const char* command_name = interpretKeyEvent(evt); + WebCore::Editor::Command command = frame->editor()->command(command_name); + + if (keyEvent->type() == WebCore::PlatformKeyboardEvent::RawKeyDown) { + // WebKit doesn't have enough information about mode to decide how + // commands that just insert text if executed via Editor should be treated, + // so we leave it upon WebCore to either handle them immediately + // (e.g. Tab that changes focus) or let a keypress event be generated + // (e.g. Tab that inserts a Tab character, or Enter). + if (command.isTextInsertion() || !command_name) + return false; + if (command.execute(evt)) { + WebViewDelegate* d = web_view_->delegate(); + MaybeRecordCommand(d, command_name); + return true; + } + return false; + } + + if (command.execute(evt)) { + WebViewDelegate* d = web_view_->delegate(); + MaybeRecordCommand(d, command_name); + return true; + } + + if (evt->keyEvent()->text().length() == 1) { + UChar ch = evt->keyEvent()->text()[0]; + + // Don't insert null or control characters as they can result in + // unexpected behaviour + if (ch < ' ') + return false; + } + + return frame->editor()->insertText(evt->keyEvent()->text(), evt); +} + +// +// End of code block subject to Apple, Inc. copyright +// + +void EditorClientImpl::handleKeyboardEvent(WebCore::KeyboardEvent* evt) { + if (handleEditingKeyboardEvent(evt)) + evt->setDefaultHandled(); +} + +void EditorClientImpl::handleInputMethodKeydown(WebCore::KeyboardEvent* keyEvent) { + notImplemented(); +} + +void EditorClientImpl::textFieldDidBeginEditing(WebCore::Element*) { + notImplemented(); +} + +void EditorClientImpl::textFieldDidEndEditing(WebCore::Element*) { + // Notification that focus was lost. + // Be careful with this, it's also sent when the page is being closed. + notImplemented(); +} + +void EditorClientImpl::textDidChangeInTextField(WebCore::Element* element) { + // Track the element so we can blur/focus it in respondToChangedContents + // so that the selected range is properly set. (See respondToChangedContents). + if (static_cast<WebCore::HTMLInputElement*>(element)->autofilled()) + pending_inline_autocompleted_element_ = element; +} + +bool EditorClientImpl::doTextFieldCommandFromEvent(WebCore::Element*, + WebCore::KeyboardEvent*) { + // The Mac code appears to use this method as a hook to implement special + // keyboard commands specific to Safari's auto-fill implementation. We + // just return false to allow the default action. + return false; +} + +void EditorClientImpl::textWillBeDeletedInTextField(WebCore::Element*) { + notImplemented(); +} + +void EditorClientImpl::textDidChangeInTextArea(WebCore::Element*) { + notImplemented(); +} + +void EditorClientImpl::ignoreWordInSpellDocument(const WebCore::String&) { + notImplemented(); +} + +void EditorClientImpl::learnWord(const WebCore::String&) { + notImplemented(); +} + +void EditorClientImpl::checkSpellingOfString(const UChar* str, int length, + int* misspellingLocation, + int* misspellingLength) { + // SpellCheckWord will write (0, 0) into the output vars, which is what our + // caller expects if the word is spelled correctly. + int spell_location = -1; + int spell_length = 0; + WebViewDelegate* d = web_view_->delegate(); + if (web_view_->FocusedFrameNeedsSpellchecking() && d) { + std::wstring word(str, length); + d->SpellCheck(word, spell_location, spell_length); + } else { + spell_location = 0; + spell_length = 0; + } + + // Note: the Mac code checks if the pointers are NULL before writing to them, + // so we do too. + if (misspellingLocation) + *misspellingLocation = spell_location; + if (misspellingLength) + *misspellingLength = spell_length; +} + +void EditorClientImpl::checkGrammarOfString(const UChar*, int length, + WTF::Vector<WebCore::GrammarDetail>&, + int* badGrammarLocation, + int* badGrammarLength) { + notImplemented(); + if (badGrammarLocation) + *badGrammarLocation = 0; + if (badGrammarLength) + *badGrammarLength = 0; +} + +void EditorClientImpl::updateSpellingUIWithGrammarString(const WebCore::String&, + const WebCore::GrammarDetail& detail) { + notImplemented(); +} + +void EditorClientImpl::updateSpellingUIWithMisspelledWord(const WebCore::String&) { + notImplemented(); +} + +void EditorClientImpl::showSpellingUI(bool show) { + notImplemented(); +} + +bool EditorClientImpl::spellingUIIsShowing() { + return false; +} + +void EditorClientImpl::getGuessesForWord(const WebCore::String&, + WTF::Vector<WebCore::String>& guesses) { + notImplemented(); +} + +void EditorClientImpl::setInputMethodState(bool enabled) { + WebViewDelegate* d = web_view_->delegate(); + if (d) { + d->SetInputMethodState(enabled); + } +} + + +std::wstring EditorClientImpl::DescribeOrError(int number, + WebCore::ExceptionCode ec) { + if (ec) + return L"ERROR"; + + wchar_t buffer[128]; + _itow_s(number, buffer, arraysize(buffer), 10); + return std::wstring(buffer); +} + +std::wstring EditorClientImpl::DescribeOrError(WebCore::Node* node, + WebCore::ExceptionCode ec) { + if (ec) + return L"ERROR"; + + return Describe(node); +} + +// These Describe() functions match the output expected by the layout tests. +std::wstring EditorClientImpl::Describe(WebCore::Range* range) { + if (range) { + WebCore::ExceptionCode exception = 0; + std::wstring str = L"range from "; + int offset = range->startOffset(exception); + str.append(DescribeOrError(offset, exception)); + str.append(L" of "); + WebCore::Node* container = range->startContainer(exception); + str.append(DescribeOrError(container, exception)); + str.append(L" to "); + offset = range->endOffset(exception); + str.append(DescribeOrError(offset, exception)); + str.append(L" of "); + container = range->endContainer(exception); + str.append(DescribeOrError(container, exception)); + return str; + } + return L"(null)"; +} + +// See comment for Describe(), above. +std::wstring EditorClientImpl::Describe(WebCore::Node* node) { + if (node) { + std::wstring str = webkit_glue::StringToStdWString(node->nodeName()); + WebCore::Node* parent = node->parentNode(); + if (parent) { + str.append(L" > "); + str.append(Describe(parent)); + } + return str; + } + return L"(null)"; +} + +// See comment for Describe(), above. +std::wstring EditorClientImpl::Describe(WebCore::EditorInsertAction action) { + switch (action) { + case WebCore::EditorInsertActionTyped: + return L"WebViewInsertActionTyped"; + case WebCore::EditorInsertActionPasted: + return L"WebViewInsertActionPasted"; + case WebCore::EditorInsertActionDropped: + return L"WebViewInsertActionDropped"; + } + return L"(UNKNOWN ACTION)"; +} + +// See comment for Describe(), above. +std::wstring EditorClientImpl::Describe(WebCore::EAffinity affinity) { + switch (affinity) { + case WebCore::UPSTREAM: + return L"NSSelectionAffinityUpstream"; + case WebCore::DOWNSTREAM: + return L"NSSelectionAffinityDownstream"; + } + return L"(UNKNOWN AFFINITY)"; +} + +std::wstring EditorClientImpl::Describe(WebCore::CSSStyleDeclaration* style) { + // TODO(pamg): Implement me. It's not clear what WebKit produces for this + // (their [style description] method), and none of the layout tests provide + // an example. But because none of them use it, it's not yet important. + return std::wstring(); +} diff --git a/webkit/glue/editor_client_impl.h b/webkit/glue/editor_client_impl.h new file mode 100644 index 0000000..23a640a --- /dev/null +++ b/webkit/glue/editor_client_impl.h @@ -0,0 +1,164 @@ +// Copyright 2008, 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 WEBKIT_GLUE_EDITOR_CLIENT_IMPL_H__ +#define WEBKIT_GLUE_EDITOR_CLIENT_IMPL_H__ + +#include <deque> + +#pragma warning(push, 0) +#include "EditorClient.h" +#pragma warning(pop) + +namespace WebCore { +class Frame; +class Node; +class PlatformKeyboardEvent; +} + +class WebView; +class WebViewImpl; + +class EditorClientImpl : public WebCore::EditorClient { + public: + EditorClientImpl(WebView* web_view); + virtual ~EditorClientImpl(); + virtual void pageDestroyed(); + + virtual bool shouldShowDeleteInterface(WebCore::HTMLElement*); + virtual bool smartInsertDeleteEnabled(); + virtual bool isContinuousSpellCheckingEnabled(); + virtual void toggleContinuousSpellChecking(); + virtual bool isGrammarCheckingEnabled(); + virtual void toggleGrammarChecking(); + virtual int spellCheckerDocumentTag(); + + virtual bool isEditable(); + + virtual bool shouldBeginEditing(WebCore::Range* range); + virtual bool shouldEndEditing(WebCore::Range* range); + virtual bool shouldInsertNode(WebCore::Node* node, WebCore::Range* range, + WebCore::EditorInsertAction action); + virtual bool shouldInsertText(WebCore::String text, WebCore::Range* range, + WebCore::EditorInsertAction action); + virtual bool shouldDeleteRange(WebCore::Range* range); + virtual bool shouldChangeSelectedRange(WebCore::Range* fromRange, + WebCore::Range* toRange, + WebCore::EAffinity affinity, + bool stillSelecting); + virtual bool shouldApplyStyle(WebCore::CSSStyleDeclaration* style, + WebCore::Range* range); + virtual bool shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*); + + virtual void didBeginEditing(); + virtual void respondToChangedContents(); + virtual void respondToChangedSelection(); + virtual void didEndEditing(); + virtual void didWriteSelectionToPasteboard(); + virtual void didSetSelectionTypesForPasteboard(); + + virtual void registerCommandForUndo(PassRefPtr<WebCore::EditCommand>); + virtual void registerCommandForRedo(PassRefPtr<WebCore::EditCommand>); + virtual void clearUndoRedoOperations(); + + virtual bool canUndo() const; + virtual bool canRedo() const; + + virtual void undo(); + virtual void redo(); + + virtual const char* interpretKeyEvent(const WebCore::KeyboardEvent*); + virtual bool handleEditingKeyboardEvent(WebCore::KeyboardEvent*); + virtual void handleKeyboardEvent(WebCore::KeyboardEvent*); + virtual void handleInputMethodKeydown(WebCore::KeyboardEvent*); + + virtual void textFieldDidBeginEditing(WebCore::Element*); + virtual void textFieldDidEndEditing(WebCore::Element*); + virtual void textDidChangeInTextField(WebCore::Element*); + virtual bool doTextFieldCommandFromEvent(WebCore::Element*,WebCore::KeyboardEvent*); + virtual void textWillBeDeletedInTextField(WebCore::Element*); + virtual void textDidChangeInTextArea(WebCore::Element*); + + virtual void ignoreWordInSpellDocument(const WebCore::String&); + virtual void learnWord(const WebCore::String&); + virtual void checkSpellingOfString(const UChar*, int length, + int* misspellingLocation, + int* misspellingLength); + virtual void checkGrammarOfString(const UChar*, int length, + WTF::Vector<WebCore::GrammarDetail>&, + int* badGrammarLocation, + int* badGrammarLength); + virtual void updateSpellingUIWithGrammarString(const WebCore::String&, + const WebCore::GrammarDetail& detail); + virtual void updateSpellingUIWithMisspelledWord(const WebCore::String&); + virtual void showSpellingUI(bool show); + virtual bool spellingUIIsShowing(); + virtual void getGuessesForWord(const WebCore::String&, + WTF::Vector<WebCore::String>& guesses); + virtual void setInputMethodState(bool enabled); + + void SetUseEditorDelegate(bool value) { use_editor_delegate_ = value; } + // HACK for webkit bug #16976. + // TODO (timsteele): Clean this up once webkit bug 16976 is fixed. + void PreserveSelection(); + + // It would be better to add these methods to the objects they describe, but + // those are in WebCore and therefore inaccessible. + virtual std::wstring DescribeOrError(int number, + WebCore::ExceptionCode ec); + virtual std::wstring DescribeOrError(WebCore::Node* node, + WebCore::ExceptionCode ec); + virtual std::wstring Describe(WebCore::Range* range); + virtual std::wstring Describe(WebCore::Node* node); + virtual std::wstring Describe(WebCore::EditorInsertAction action); + virtual std::wstring Describe(WebCore::EAffinity affinity); + virtual std::wstring Describe(WebCore::CSSStyleDeclaration* style); + + private: + void ModifySelection(WebCore::Frame* frame, + WebCore::KeyboardEvent* event); + + protected: + WebViewImpl* web_view_; + bool use_editor_delegate_; + bool in_redo_; + + // Should preserve the selection in next call to shouldChangeSelectedRange. + bool preserve_; + + // Points to an HTMLInputElement that was just autocompleted (else NULL), + // for use by respondToChangedContents(). + WebCore::Element* pending_inline_autocompleted_element_; + + typedef std::deque<WTF::RefPtr<WebCore::EditCommand> > EditCommandStack; + EditCommandStack undo_stack_; + EditCommandStack redo_stack_; +}; + +#endif // WEBKIT_GLUE_EDITOR_CLIENT_IMPL_H__ diff --git a/webkit/glue/entity_map.cc b/webkit/glue/entity_map.cc new file mode 100644 index 0000000..771be72 --- /dev/null +++ b/webkit/glue/entity_map.cc @@ -0,0 +1,118 @@ +// Copyright 2008, 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 "webkit/glue/entity_map.h" + +#include <hash_map> + +#include "HTMLEntityCodes.c" + +#include "base/string_util.h" + +namespace webkit_glue { + +typedef stdext::hash_map<wchar_t, const char*> EntityMapType; + +class EntityMapData { + public: + EntityMapData(const EntityCode* entity_codes, int entity_codes_length, + bool user_number_entity_apos) + : entity_codes_(entity_codes), + entity_codes_length_(entity_codes_length), + user_number_entity_apos_(user_number_entity_apos), + map_(NULL) { + } + ~EntityMapData() { delete map_; } + const EntityMapType* GetEntityMapData(); + + private: + // Data structure which saves all pairs of Unicode character and its + // corresponding entity notation. + const EntityCode* entity_codes_; + const int entity_codes_length_; + // IE does not support '''(html entity name for symbol ') as HTML entity + // (but it does support ' as xml entity), so if user_number_entity_apos_ + // is true, we will use entity number 0x0027 instead of using ''' when + // doing serialization work. + const bool user_number_entity_apos_; + // Map the Unicode character to corresponding entity notation. + EntityMapType* map_; + + DISALLOW_EVIL_CONSTRUCTORS(EntityMapData); +}; + +const EntityMapType* EntityMapData::GetEntityMapData() { + if (!map_) { + // lazily create the entity map. + map_ = new EntityMapType; + const EntityCode* entity_code = &entity_codes_[0]; + for (int i = 0; i < entity_codes_length_; ++i, ++entity_code) + (*map_)[entity_code->code] = entity_code->name; + if (user_number_entity_apos_) + (*map_)[0x0027] = "'"; + } + return map_; +} + +static const EntityCode xml_built_in_entity_codes[] = { + {0x003c, "<"}, + {0x003e, ">"}, + {0x0026, "&"}, + {0x0027, "'"}, + {0x0022, """} +}; + +static const int xml_entity_codes_length = + arraysize(xml_built_in_entity_codes); + +static EntityMapData html_entity_map_singleton(entity_codes, + entity_codes_length, + true); +static EntityMapData xml_entity_map_singleton(xml_built_in_entity_codes, + xml_entity_codes_length, + false); + +const char* EntityMap::GetEntityNameByCode(wchar_t code, bool is_html) { + const EntityMapType* entity_map; + if (is_html) + entity_map = html_entity_map_singleton.GetEntityMapData(); + else + entity_map = xml_entity_map_singleton.GetEntityMapData(); + + // Check entity name according to unicode. + EntityMapType::const_iterator i = entity_map->find(code); + if (i == entity_map->end()) + // Not found, return NULL. + return NULL; + else + // Found, return entity notation. + return i->second; +} + +} // namespace webkit_glue diff --git a/webkit/glue/entity_map.h b/webkit/glue/entity_map.h new file mode 100644 index 0000000..8dfea5d --- /dev/null +++ b/webkit/glue/entity_map.h @@ -0,0 +1,53 @@ +// Copyright 2008, 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 WEBKIT_GLUE_ENTITY_MAP_H__ +#define WEBKIT_GLUE_ENTITY_MAP_H__ + +#include <string> + +#include "base/basictypes.h" + +namespace webkit_glue { + +class EntityMap { + public: + // Check whether specified unicode has corresponding html or xml built-in + // entity name. If yes, return the entity notation, if not then return NULL. + // Parameter is_html indicates check the code in html entity map or in xml + // entity map. THIS FUNCTION IS NOT THREADSAFE. + static const char* GetEntityNameByCode(wchar_t code, bool is_html); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(EntityMap); +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_ENTITY_MAP_H__ diff --git a/webkit/glue/event_conversion.cc b/webkit/glue/event_conversion.cc new file mode 100644 index 0000000..6ec7034 --- /dev/null +++ b/webkit/glue/event_conversion.cc @@ -0,0 +1,199 @@ +// Copyright 2008, 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 <windows.h> + +#include "StringImpl.h" // This is so that the KJS build works +#pragma warning(push, 0) +// HACK: make it possible to initialize these classes... +// TODO(darin): send this change to webkit.org +#define private protected +#include "PlatformKeyboardEvent.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#undef private +#include "Widget.h" +#pragma warning(pop) + +#undef LOG +#include "base/gfx/point.h" +#include "base/logging.h" +#include "webkit/glue/event_conversion.h" +#include "webkit/glue/webinputevent.h" +#include "webkit/glue/webkit_glue.h" + +using namespace WebCore; + +// MakePlatformMouseEvent ----------------------------------------------------- + +int MakePlatformMouseEvent::last_click_count_ = 0; +long MakePlatformMouseEvent::last_click_time_ = 0; + +MakePlatformMouseEvent::MakePlatformMouseEvent(Widget* widget, + const WebMouseEvent& e) + : PlatformMouseEvent(NULL, 0, 0, 0, false /* TODO(darin): do we care? */) { + // TODO(mpcomplete): widget is always toplevel, unless it's a popup. We + // may be able to get rid of this once we abstract popups into a WebKit API. + m_position = widget->convertFromContainingWindow(IntPoint(e.x, e.y)); + m_globalPosition = IntPoint(e.global_x, e.global_y); + m_button = static_cast<MouseButton>(e.button); + m_shiftKey = (e.modifiers & WebInputEvent::SHIFT_KEY) != 0; + m_ctrlKey = (e.modifiers & WebInputEvent::CTRL_KEY) != 0; + m_altKey = (e.modifiers & WebInputEvent::ALT_KEY) != 0; + m_metaKey = (e.modifiers & WebInputEvent::META_KEY) != 0; + m_modifierFlags = e.modifiers; + m_timestamp = e.timestamp_sec; + + // This differs slightly from the WebKit code in WebKit/win/WebView.cpp where + // their original code looks buggy. + static IntPoint last_click_position; + static MouseButton last_click_button = LeftButton; + + const DWORD current_time = static_cast<DWORD>(m_timestamp * 1000); + const bool cancel_previous_click = + (abs(last_click_position.x() - m_position.x()) > + (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) || + (abs(last_click_position.y() - m_position.y()) > + (GetSystemMetrics(SM_CYDOUBLECLK) / 2)) || + ((current_time - last_click_time_) > GetDoubleClickTime()); + + switch (e.type) { + case WebInputEvent::MOUSE_MOVE: + case WebInputEvent::MOUSE_LEAVE: // synthesize a move event + if (cancel_previous_click) { + last_click_count_ = 0; + last_click_position = IntPoint(); + last_click_time_ = 0; + } + setClickCount(last_click_count_); + m_eventType = MouseEventMoved; + break; + + case WebInputEvent::MOUSE_DOWN: + case WebInputEvent::MOUSE_DOUBLE_CLICK: + if (!cancel_previous_click && (m_button == last_click_button)) { + ++last_click_count_; + } else { + last_click_count_ = 1; + last_click_position = m_position; + } + last_click_time_ = current_time; + last_click_button = m_button; + setClickCount(last_click_count_); + m_eventType = MouseEventPressed; + break; + + case WebInputEvent::MOUSE_UP: + setClickCount(last_click_count_); + m_eventType = MouseEventReleased; + break; + + default: + NOTREACHED() << "unexpected mouse event type"; + } + + if (webkit_glue::IsLayoutTestMode()) { + setClickCount(e.layout_test_click_count); + } +} + +// MakePlatformWheelEvent ----------------------------------------------------- + +MakePlatformWheelEvent::MakePlatformWheelEvent(Widget* widget, + const WebMouseWheelEvent& e) + : PlatformWheelEvent(NULL, 0, 0, false) { // TODO(jackson): Check if it's a horizontal event + m_position = widget->convertFromContainingWindow(IntPoint(e.x, e.y)); + m_globalPosition = IntPoint(e.global_x, e.global_y); + m_deltaX = static_cast<float>(e.delta_x); + m_deltaY = static_cast<float>(e.delta_y); + m_shiftKey = (e.modifiers & WebInputEvent::SHIFT_KEY) != 0; + m_ctrlKey = (e.modifiers & WebInputEvent::CTRL_KEY) != 0; + m_altKey = (e.modifiers & WebInputEvent::ALT_KEY) != 0; + m_metaKey = (e.modifiers & WebInputEvent::META_KEY) != 0; +} + +static inline const PlatformKeyboardEvent::Type platformKeyTypeForWebInputEventType(WebInputEvent::Type type) { + switch (type) { + case WebInputEvent::KEY_UP: + return PlatformKeyboardEvent::KeyUp; + case WebInputEvent::KEY_DOWN: + return PlatformKeyboardEvent::KeyDown; + case WebInputEvent::CHAR: + return PlatformKeyboardEvent::Char; + } + ASSERT_NOT_REACHED(); + return PlatformKeyboardEvent::KeyDown; +} + +// MakePlatformKeyboardEvent -------------------------------------------------- + +MakePlatformKeyboardEvent::MakePlatformKeyboardEvent(const WebKeyboardEvent& e) + : PlatformKeyboardEvent(NULL, e.key_code, e.key_data, + platformKeyTypeForWebInputEventType(e.type), + e.system_key) { + m_autoRepeat = (e.modifiers & WebInputEvent::IS_AUTO_REPEAT) != 0; + m_isKeypad = (e.modifiers & WebInputEvent::IS_KEYPAD) != 0; + m_shiftKey = (e.modifiers & WebInputEvent::SHIFT_KEY) != 0; + m_ctrlKey = (e.modifiers & WebInputEvent::CTRL_KEY) != 0; + m_altKey = (e.modifiers & WebInputEvent::ALT_KEY) != 0; + m_metaKey = (e.modifiers & WebInputEvent::META_KEY) != 0; +} + +void MakePlatformKeyboardEvent::SetKeyType(Type type) { + // According to the behavior of Webkit in Windows platform, + // we need to convert KeyDown to RawKeydown and Char events + // See WebKit/WebKit/Win/WebView.cpp + ASSERT(m_type == KeyDown); + ASSERT(type == RawKeyDown || type == Char); + m_type = type; + + if (type == RawKeyDown) { + m_text = String(); + m_unmodifiedText = String(); + } else { + m_keyIdentifier = String(); + m_windowsVirtualKeyCode = 0; + } +} + +// Please refer to bug http://b/issue?id=961192, which talks about Webkit +// keyboard event handling changes. It also mentions the list of keys +// which don't have associated character events. +bool MakePlatformKeyboardEvent::IsCharacterKey() const { + switch (windowsVirtualKeyCode()) { + case VK_BACK: + case VK_ESCAPE: + return false; + + default: + break; + } + return true; +} diff --git a/webkit/glue/event_conversion.h b/webkit/glue/event_conversion.h new file mode 100644 index 0000000..2c08459 --- /dev/null +++ b/webkit/glue/event_conversion.h @@ -0,0 +1,75 @@ +// Copyright 2008, 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 WEBKIT_GLUE_EVENT_CONVERSION_H__ +#define WEBKIT_GLUE_EVENT_CONVERSION_H__ + +#pragma warning(push, 0) +#include "PlatformKeyboardEvent.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#pragma warning(pop) + +namespace WebCore { + class Widget; +} + +class WebMouseEvent; +class WebMouseWheelEvent; +class WebKeyboardEvent; + +// These classes are used to convert from WebInputEvent subclasses to +// corresponding WebCore events. + +class MakePlatformMouseEvent : public WebCore::PlatformMouseEvent { + public: + MakePlatformMouseEvent(WebCore::Widget* widget, const WebMouseEvent& e); + + static void ResetLastClick() { + last_click_time_ = last_click_count_ = 0; + } + + private: + static int last_click_count_; + static long last_click_time_; +}; + +class MakePlatformWheelEvent : public WebCore::PlatformWheelEvent { + public: + MakePlatformWheelEvent(WebCore::Widget* widget, const WebMouseWheelEvent& e); +}; + +class MakePlatformKeyboardEvent : public WebCore::PlatformKeyboardEvent { + public: + MakePlatformKeyboardEvent(const WebKeyboardEvent& e); + void SetKeyType(Type type); + bool IsCharacterKey() const; +}; + +#endif // WEBKIT_GLUE_EVENT_CONVERSION_H__ diff --git a/webkit/glue/feed_preview.cc b/webkit/glue/feed_preview.cc new file mode 100644 index 0000000..82c9270 --- /dev/null +++ b/webkit/glue/feed_preview.cc @@ -0,0 +1,105 @@ +// Copyright 2008, 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 "webkit/glue/feed_preview.h" + +#pragma warning(push, 0) +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "ResourceHandle.h" +#pragma warning(pop) + +#undef LOG + +#include "base/logging.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webkit_resources.h" + +namespace WebCore { + +// Generate a preview for a feed. |url| is the (http://) URL of the feed, +// and |data| are the bytes we received in response to the HTTP request. +// Returns an HTML string. +static std::string MakeFeedPreview(const std::string& url, + const std::string& data) { + // TODO(evanm): this is just a placeholder. + // Maybe we should make this parse the feed data and display a preview? + // Yuck. Seems like a lot of effort for a pretty minor feature. + + // The feed preview template has {{URL}} in place of where the URL should go. + const std::string kUrlTemplate = "{{URL}}"; + std::string feed_template(webkit_glue::GetDataResource(IDR_FEED_PREVIEW)); + std::string::size_type template_offset = feed_template.find(kUrlTemplate); + DCHECK(template_offset != std::string::npos); + // TODO(evanm): URL-escape URL! + return feed_template.substr(0, template_offset) + url + + feed_template.substr(template_offset + kUrlTemplate.size()); +} + +FeedClientProxy::FeedClientProxy(ResourceHandleClient* client) + : client_(client), do_feed_preview_(false) { +} + +void FeedClientProxy::didReceiveResponse(ResourceHandle* handle, + const ResourceResponse& response) { + if (response.httpStatusCode() == 200) { + ResourceResponse new_response(response); + // Our feed preview has mime type text/html. + new_response.setMimeType("text/html"); + do_feed_preview_ = true; + client_->didReceiveResponse(handle, new_response); + } else { + client_->didReceiveResponse(handle, response); + } +} + +void FeedClientProxy::didReceiveData(ResourceHandle*, const char* data, + int data_len, int length_received) { + length_received_ = length_received; + data_.append(data, data_len); +} + +void FeedClientProxy::didFinishLoading(ResourceHandle* handle) { + const std::string url = + webkit_glue::KURLToGURL(handle->request().url()).spec(); + const std::string& data = + do_feed_preview_ ? MakeFeedPreview(url, data_) : data_; + client_->didReceiveData(handle, data.data(), data.size(), length_received_); + client_->didFinishLoading(handle); +} + +void FeedClientProxy::didFail(ResourceHandle* handle, + const ResourceError& error) { + client_->didFail(handle, error); +} + +} // namespace WebCore diff --git a/webkit/glue/feed_preview.h b/webkit/glue/feed_preview.h new file mode 100644 index 0000000..7b62693 --- /dev/null +++ b/webkit/glue/feed_preview.h @@ -0,0 +1,79 @@ +// Copyright 2008, 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. + +// URLs of the form "feed://foo" are implemented by handing an "http://" +// URL up to the resource fetching code, then generating a preview at this +// layer and handing that back to WebKit. + +#ifndef WEBKIT_GLUE_FEED_PREVIEW_H__ +#define WEBKIT_GLUE_FEED_PREVIEW_H__ + +#include <string> + +#pragma warning(push, 0) +#include "ResourceHandleClient.h" +#pragma warning(pop) + +namespace WebCore { + +// FeedClientProxy serves as a ResourceHandleClient that forwards calls to +// a "real" ResourceHandleClient, buffering the response so it can provide +// a feed preview if the underlying resource request succeeds. +class FeedClientProxy : public ResourceHandleClient { + public: + FeedClientProxy(ResourceHandleClient* client); + virtual ~FeedClientProxy() { } + + // ResourceHandleClient overrides. + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int); + virtual void didFinishLoading(ResourceHandle*); + virtual void didFail(ResourceHandle*, const ResourceError&); + + private: + // The "real" ResourceHandleClient that we're forwarding responses to. + ResourceHandleClient* client_; + + // Whether we should insert a feed preview -- only if the request came + // back ok. + bool do_feed_preview_; + + // The response data, which we can parse for the feed preview. + std::string data_; + + // The value of the mystery lengthReceived parameter. We accept this via + // didReceiveData() and forward it along unmodified. + // TODO(evanm): do the right thing here, once we know what that is. + // (See TODOs in resource_handle_win.cc.) + int length_received_; +}; + +} // namespace WebCore + +#endif // WEBKIT_GLUE_FEED_PREVIEW_H__ diff --git a/webkit/glue/find_in_page_request.h b/webkit/glue/find_in_page_request.h new file mode 100644 index 0000000..f72a991 --- /dev/null +++ b/webkit/glue/find_in_page_request.h @@ -0,0 +1,55 @@ +// Copyright 2008, 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 WEBKIT_GLUE_FIND_IN_PAGE_REQUEST_H__ +#define WEBKIT_GLUE_FIND_IN_PAGE_REQUEST_H__ + +#include <string> + +// Parameters for a find in page request. +struct FindInPageRequest { + // The id for the request, this message is passed in by the caller and + // returned back with the reply so that the caller can start a new search and + // choose to ignore packets that belonged to the last search. + int request_id; + + // The word(s) to find on the page. + std::wstring search_string; + + // Whether to search forward or backward within the page. + bool forward; + + // Whether search should be Case sensitive. + bool match_case; + + // Whether this operation is first request (Find) or a follow-up (FindNext). + bool find_next; +}; + +#endif // WEBKIT_GLUE_FIND_IN_PAGE_REQUEST_H__ diff --git a/webkit/glue/form_data.h b/webkit/glue/form_data.h new file mode 100644 index 0000000..32a7553 --- /dev/null +++ b/webkit/glue/form_data.h @@ -0,0 +1,51 @@ +// Copyright 2008, 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 WEBKIT_GLUE_FORM_DATA_H__ +#define WEBKIT_GLUE_FORM_DATA_H__ + +#include <vector> + +#include "googleurl/src/gurl.h" + +// Holds information about a form to be filled and/or submitted. +struct FormData { + // The URL (minus query parameters) containing the form + GURL origin; + // The action target of the form + GURL action; + // A list of element names to be filled + std::vector<std::wstring> elements; + // A list of element values to be filled + std::vector<std::wstring> values; + // The name of the submit button to be used to submit (optional) + std::wstring submit; +}; + +#endif // WEBKIT_GLUE_FORM_DATA_H__ diff --git a/webkit/glue/glue_serialize.cc b/webkit/glue/glue_serialize.cc new file mode 100644 index 0000000..8ea4286 --- /dev/null +++ b/webkit/glue/glue_serialize.cc @@ -0,0 +1,385 @@ +// Copyright 2008, 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 <string> + +#pragma warning(push, 0) +#include "HistoryItem.h" +#include "PlatformString.h" +#include "ResourceRequest.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/glue_serialize.h" + +#include "base/pickle.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webkit_glue.h" + +using namespace WebCore; + +namespace webkit_glue { + +struct SerializeObject { + SerializeObject() : iter(NULL) {} + SerializeObject(const char* data, int len) : pickle(data, len), iter(NULL) {} + + std::string GetAsString() { + return std::string(static_cast<const char*>(pickle.data()), pickle.size()); + } + + Pickle pickle; + mutable void* iter; + mutable int version; +}; + +// TODO(mpcomplete): obsolete versions 1 and 2 after 1/1/2008. +// Version ID used in reading/writing history items. +// 1: Initial revision. +// 2: Added case for NULL string versus "". Version 2 code can read Version 1 +// data, but not vice versa. +// 3: Version 2 was broken, it stored number of UChars, not number of bytes. +// This version checks and reads v1 and v2 correctly. +// Should be const, but unit tests may modify it. +int kVersion = 3; + +// A bunch of convenience functions to read/write to SerializeObjects. +// The serializers assume the input data is in the correct format and so does +// no error checking. +inline void WriteData(const void* data, int length, SerializeObject* obj) { + obj->pickle.WriteData(static_cast<const char*>(data), length); +} + +inline void ReadData(const SerializeObject* obj, const void** data, + int* length) { + const char* tmp; + obj->pickle.ReadData(&obj->iter, &tmp, length); + *data = tmp; +} + +inline bool ReadBytes(const SerializeObject* obj, const void** data, + int length) { + const char *tmp; + if (!obj->pickle.ReadBytes(&obj->iter, &tmp, length)) + return false; + *data = tmp; + return true; +} + +inline void WriteInteger(int data, SerializeObject* obj) { + obj->pickle.WriteInt(data); +} + +inline int ReadInteger(const SerializeObject* obj) { + int tmp; + obj->pickle.ReadInt(&obj->iter, &tmp); + return tmp; +} + +inline void WriteReal(double data, SerializeObject* obj) { + WriteData(&data, sizeof(double), obj); +} + +inline double ReadReal(const SerializeObject* obj) { + const void* tmp; + int length; + ReadData(obj, &tmp, &length); + return *static_cast<const double*>(tmp); +} + +inline void WriteBoolean(bool data, SerializeObject* obj) { + obj->pickle.WriteInt(data ? 1 : 0); +} + +inline bool ReadBoolean(const SerializeObject* obj) { + bool tmp; + obj->pickle.ReadBool(&obj->iter, &tmp); + return tmp; +} + +// Read/WriteString pickle the String as <int length><UChar* data>. +// If length == -1, then the String itself is NULL (String()). +// Otherwise the length is the number of UChars (not bytes) in the String. +inline void WriteString(const String& data, SerializeObject* obj) { + switch (kVersion) { + case 1: + // Version 1 writes <length in bytes><string data>. + // It saves String() and "" as "". + obj->pickle.WriteInt(data.length() * sizeof(UChar)); + obj->pickle.WriteBytes(data.characters(), data.length() * sizeof(UChar)); + break; + case 2: + // Version 2 writes <length in UChars><string data>. + // It uses -1 in the length field to mean String(). + if (data.isNull()) { + obj->pickle.WriteInt(-1); + } else { + obj->pickle.WriteInt(data.length()); + obj->pickle.WriteBytes(data.characters(), + data.length() * sizeof(UChar)); + } + break; + case 3: + // Version 3 writes <length in bytes><string data>. + // It uses -1 in the length field to mean String(). + if (data.isNull()) { + obj->pickle.WriteInt(-1); + } else { + obj->pickle.WriteInt(data.length() * sizeof(UChar)); + obj->pickle.WriteBytes(data.characters(), + data.length() * sizeof(UChar)); + } + break; + default: + NOTREACHED(); + break; + } +} + +// This reads a serialized String from obj. If a string can't be read, +// String() is returned. +inline String ReadString(const SerializeObject* obj) { + int length; + + // Versions 1, 2, and 3 all start with an integer. + if (!obj->pickle.ReadInt(&obj->iter, &length)) + return String(); + + // Starting with version 2, -1 means String(). + if (length == -1) + return String(); + + // In version 2, the length field was the length in UChars. + // In version 1 and 3 it is the length in bytes. + int bytes = ((obj->version == 2) ? length * sizeof(UChar) : length); + + const void* data; + if (!ReadBytes(obj, &data, bytes)) + return String(); + return String(static_cast<const UChar*>(data), bytes / sizeof(UChar)); +} + +// Writes a Vector of Strings into a SerializeObject for serialization. +static void WriteStringVector(const Vector<String>& data, SerializeObject* obj) { + WriteInteger(static_cast<int>(data.size()), obj); + for (size_t i = 0, c = data.size(); i < c; ++i) { + unsigned ui = static_cast<unsigned>(i); // sigh + WriteString(data[ui], obj); + } +} + +static void ReadStringVector(const SerializeObject* obj, Vector<String>* data) { + int num_elements = ReadInteger(obj); + data->reserveCapacity(num_elements); + for (int i = 0; i < num_elements; ++i) { + data->append(ReadString(obj)); + } +} + +// Writes a FormData object into a SerializeObject for serialization. +static void WriteFormData(const FormData* form_data, SerializeObject* obj) { + if (form_data == NULL) { + WriteInteger(0, obj); + return; + } + + WriteInteger(static_cast<int>(form_data->elements().size()), obj); + for (size_t i = 0, c = form_data->elements().size(); i < c; ++i) { + const FormDataElement& e = form_data->elements().at(i); + WriteInteger(e.m_type, obj); + + if (e.m_type == FormDataElement::data) { + WriteData(e.m_data.data(), static_cast<int>(e.m_data.size()), obj); + } else { + WriteString(e.m_filename, obj); + } + } +} + +static FormData* ReadFormData(const SerializeObject* obj) { + int num_elements = ReadInteger(obj); + if (num_elements == 0) + return NULL; + + FormData* form_data = new FormData(); + + for (int i = 0; i < num_elements; ++i) { + int type = ReadInteger(obj); + if (type == FormDataElement::data) { + const void* data; + int length; + ReadData(obj, &data, &length); + form_data->appendData(static_cast<const char*>(data), length); + } else { + form_data->appendFile(ReadString(obj)); + } + } + + return form_data; +} + +// Writes the HistoryItem data into the SerializeObject object for +// serialization. +static void WriteHistoryItem(const HistoryItem* item, SerializeObject* obj) { + // WARNING: This data may be persisted for later use. As such, care must be + // taken when changing the serialized format. If a new field needs to be + // written, only adding at the end will make it easier to deal with loading + // older versions. Similarly, this should NOT save fields with sensitive + // data, such as password fields. + WriteInteger(kVersion, obj); + WriteString(item->urlString(), obj); + WriteString(item->originalURLString(), obj); + WriteString(item->target(), obj); + WriteString(item->parent(), obj); + WriteString(item->title(), obj); + WriteString(item->alternateTitle(), obj); + WriteReal(item->lastVisitedTime(), obj); + WriteInteger(item->scrollPoint().x(), obj); + WriteInteger(item->scrollPoint().y(), obj); + WriteBoolean(item->isTargetItem(), obj); + WriteInteger(item->visitCount(), obj); + WriteString(item->rssFeedReferrer(), obj); + + WriteStringVector(item->documentState(), obj); + + // No access to formData through a const HistoryItem = lame. + WriteFormData(const_cast<HistoryItem*>(item)->formData(), obj); + WriteString(item->formContentType(), obj); + WriteString(item->formReferrer(), obj); + + // Subitems + WriteInteger(static_cast<int>(item->children().size()), obj); + for (size_t i = 0, c = item->children().size(); i < c; ++i) + WriteHistoryItem(item->children().at(i).get(), obj); +} + +// Creates a new HistoryItem tree based on the serialized string. +// Assumes the data is in the format returned by WriteHistoryItem. +static HistoryItem* ReadHistoryItem(const SerializeObject* obj) { + // See note in WriteHistoryItem. on this. + obj->version = ReadInteger(obj); + + if (obj->version > kVersion) + return NULL; + + HistoryItem* item = new HistoryItem(); + + item->setURLString(ReadString(obj)); + item->setOriginalURLString(ReadString(obj)); + item->setTarget(ReadString(obj)); + item->setParent(ReadString(obj)); + item->setTitle(ReadString(obj)); + item->setAlternateTitle(ReadString(obj)); + item->setLastVisitedTime(ReadReal(obj)); + int x = ReadInteger(obj); + int y = ReadInteger(obj); + item->setScrollPoint(IntPoint(x, y)); + item->setIsTargetItem(ReadBoolean(obj)); + item->setVisitCount(ReadInteger(obj)); + item->setRSSFeedReferrer(ReadString(obj)); + + Vector<String> document_state; + ReadStringVector(obj, &document_state); + item->setDocumentState(document_state); + + // Form data. If there is any form data, we assume POST, otherwise GET. + // ResourceRequest takes ownership of the new FormData, then gives it to + // HistoryItem. + ResourceRequest dummy_request; // only way to initialize HistoryItem + dummy_request.setHTTPBody(ReadFormData(obj)); + dummy_request.setHTTPContentType(ReadString(obj)); + dummy_request.setHTTPReferrer(ReadString(obj)); + if (dummy_request.httpBody()) + dummy_request.setHTTPMethod("POST"); + item->setFormInfoFromRequest(dummy_request); + + // Subitems + int num_children = ReadInteger(obj); + for (int i = 0; i < num_children; ++i) + item->addChildItem(ReadHistoryItem(obj)); + + return item; +} + +// Serialize a HistoryItem to a string, using our JSON Value serializer. +void HistoryItemToString(PassRefPtr<HistoryItem> item, + std::string* serialized_item) { + if (!item) { + serialized_item->clear(); + return; + } + + SerializeObject obj; + WriteHistoryItem(item.get(), &obj); + *serialized_item = obj.GetAsString(); +} + +// Reconstruct a HistoryItem from a string, using our JSON Value deserializer. +// This assumes that the given serialized string has all the required key,value +// pairs, and does minimal error checking. +PassRefPtr<HistoryItem> HistoryItemFromString( + const std::string& serialized_item) { + if (serialized_item.empty()) + return NULL; + + SerializeObject obj(serialized_item.data(), + static_cast<int>(serialized_item.length())); + return ReadHistoryItem(&obj); +} + +// For testing purposes only. +void HistoryItemToVersionedString(PassRefPtr<HistoryItem> item, int version, + std::string* serialized_item) { + if (!item) { + serialized_item->clear(); + return; + } + + // Temporarily change the version. + int real_version = kVersion; + kVersion = version; + + SerializeObject obj; + WriteHistoryItem(item.get(), &obj); + *serialized_item = obj.GetAsString(); + + kVersion = real_version; +} + +std::string CreateHistoryStateForURL(const GURL& url) { + RefPtr<HistoryItem> item(new HistoryItem(GURLToKURL(url), String())); + std::string data; + HistoryItemToString(item, &data); + return data; +} + +} // namespace webkit_glue diff --git a/webkit/glue/glue_serialize.h b/webkit/glue/glue_serialize.h new file mode 100644 index 0000000..dd1bddc --- /dev/null +++ b/webkit/glue/glue_serialize.h @@ -0,0 +1,61 @@ +// Copyright 2008, 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. +// +// This file contains (de)serialization (or if you like python, pickling) +// methods for various objects that we want to persist. +// In serialization, we write an object's state to a string in some opaque +// format. Deserialization reconstructs the object's state from such a string. + +#ifndef WEBKIT_GLUE_GLUE_SERIALIZE_H__ +#define WEBKIT_GLUE_GLUE_SERIALIZE_H__ + +#include "PassRefPtr.h" +#include "RefPtr.h" + +namespace WebCore { +class String; +class HistoryItem; +} + +namespace webkit_glue { +// HistoryItem serialization. The returned HistoryItem will have a ref count +// of 0, so the first RefPtr it is assigned to will take ownership. The empty +// string corresponds with a NULL HistoryItem. +void HistoryItemToString( + PassRefPtr<WebCore::HistoryItem> item, std::string* serialized_item); +PassRefPtr<WebCore::HistoryItem> HistoryItemFromString( + const std::string& serialized_item); + +// For testing purposes only. +void HistoryItemToVersionedString( + PassRefPtr<WebCore::HistoryItem> item, int version, + std::string* serialized_item); +} + +#endif // #ifndef WEBKIT_GLUE_GLUE_SERIALIZE_H__ diff --git a/webkit/glue/glue_serialize_unittest.cc b/webkit/glue/glue_serialize_unittest.cc new file mode 100644 index 0000000..a71f9b2 --- /dev/null +++ b/webkit/glue/glue_serialize_unittest.cc @@ -0,0 +1,202 @@ +// Copyright 2008, 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 <string> + +#include "webkit/glue/glue_serialize.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "base/pickle.h" + +#pragma warning(push, 0) +#include "CString.h" +#include "FormData.h" +#include "HistoryItem.h" +#include "PlatformString.h" +#include "ResourceRequest.h" +#pragma warning(pop) + +using namespace std; +using namespace WebCore; +using namespace webkit_glue; + +// These exist only to support the gTest assertion macros, and +// shouldn't be used in normal program code. +static std::ostream& operator<<(std::ostream& out, const String& str) +{ + return out << str.latin1().data(); +} + +static std::ostream& operator<<(std::ostream& out, const FormDataElement& e) +{ + return out << e.m_type << ": " << + e.m_data.size() << + " <" << string(e.m_data.data() ? e.m_data.data() : "", e.m_data.size()) << "> " << + e.m_filename; +} + +template<typename T> +static std::ostream& operator<<(std::ostream& out, const Vector<T>& v) +{ + for (size_t i = 0; i < v.size(); ++i) + out << "[" << v[i] << "] "; + + return out; +} + +static std::ostream& operator<<(std::ostream& out, const FormData& data) +{ + return out << data.elements(); +} + +static std::ostream& operator<<(std::ostream& out, const IntPoint& pt) +{ + return out << "(" << pt.x() << "," << pt.y() << ")"; +} + +namespace { +class GlueSerializeTest : public testing::Test { + public: + // Makes a FormData with some random data. + FormData* MakeFormData() { + FormData* form_data = new FormData(); + + char d1[] = "first data block"; + form_data->appendData(d1, sizeof(d1)-1); + + form_data->appendFile("file.txt"); + + char d2[] = "data the second"; + form_data->appendData(d2, sizeof(d2)-1); + + return form_data; + } + + // Constructs a HistoryItem with some random data and an optional child. + PassRefPtr<HistoryItem> MakeHistoryItem(bool with_form_data, bool pregnant) { + RefPtr<HistoryItem> item = new HistoryItem(); + + item->setURLString("urlString"); + item->setOriginalURLString("originalURLString"); + item->setTarget("target"); + item->setParent("parent"); + item->setTitle("title"); + item->setAlternateTitle("alternateTitle"); + item->setLastVisitedTime(13.37); + item->setScrollPoint(IntPoint(42, -42)); + item->setIsTargetItem(true); + item->setVisitCount(42*42); + item->setRSSFeedReferrer("rssFeedReferrer"); + + Vector<String> document_state; + document_state.append("state1"); + document_state.append("state2"); + document_state.append("state AWESOME"); + item->setDocumentState(document_state); + + // Form Data + ResourceRequest dummy_request; // only way to initialize HistoryItem + if (with_form_data) { + FormData* form_data = MakeFormData(); + dummy_request.setHTTPBody(form_data); + dummy_request.setHTTPContentType("formContentType"); + dummy_request.setHTTPReferrer("formReferrer"); + dummy_request.setHTTPMethod("POST"); + } + item->setFormInfoFromRequest(dummy_request); + + // Children + if (pregnant) + item->addChildItem(MakeHistoryItem(false, false)); + + return item; + } + + // Checks that a == b. + void HistoryItemExpectEqual(HistoryItem* a, HistoryItem* b) { + EXPECT_EQ(a->urlString(), b->urlString()); + EXPECT_EQ(a->originalURLString(), b->originalURLString()); + EXPECT_EQ(a->target(), b->target()); + EXPECT_EQ(a->parent(), b->parent()); + EXPECT_EQ(a->title(), b->title()); + EXPECT_EQ(a->alternateTitle(), b->alternateTitle()); + EXPECT_EQ(a->lastVisitedTime(), b->lastVisitedTime()); + EXPECT_EQ(a->scrollPoint(), b->scrollPoint()); + EXPECT_EQ(a->isTargetItem(), b->isTargetItem()); + EXPECT_EQ(a->visitCount(), b->visitCount()); + EXPECT_EQ(a->rssFeedReferrer(), b->rssFeedReferrer()); + EXPECT_EQ(a->documentState(), b->documentState()); + + // Form Data + EXPECT_EQ(a->formData() != NULL, b->formData() != NULL); + if (a->formData() && b->formData()) + EXPECT_EQ(*a->formData(), *b->formData()); + EXPECT_EQ(a->formReferrer(), b->formReferrer()); + EXPECT_EQ(a->formContentType(), b->formContentType()); + + // Children + EXPECT_EQ(a->children().size(), b->children().size()); + for (size_t i = 0, c = a->children().size(); i < c; ++i) { + HistoryItemExpectEqual(a->children().at(i).get(), + b->children().at(i).get()); + } + } +}; + +// Test old versions of serialized data to ensure that newer versions of code +// can still read history items written by previous versions. +TEST_F(GlueSerializeTest, BackwardsCompatibleTest) { + RefPtr<HistoryItem> item = MakeHistoryItem(false, false); + + // Make sure version 3 (current version) can read versions 1 and 2. + for (int i = 1; i <= 2; i++) { + string serialized_item; + HistoryItemToVersionedString(item.get(), i, &serialized_item); + RefPtr<HistoryItem> deserialized_item = HistoryItemFromString(serialized_item); + ASSERT_FALSE(item == NULL); + ASSERT_FALSE(deserialized_item == NULL); + HistoryItemExpectEqual(item.get(), deserialized_item.get()); + } +} + +// Makes sure that a HistoryItem remains intact after being serialized and +// deserialized. +TEST_F(GlueSerializeTest, HistoryItemSerializeTest) { + RefPtr<HistoryItem> item = MakeHistoryItem(true, true); + string serialized_item; + HistoryItemToString(item, &serialized_item); + RefPtr<HistoryItem> deserialized_item = HistoryItemFromString(serialized_item); + + ASSERT_FALSE(item == NULL); + ASSERT_FALSE(deserialized_item == NULL); + HistoryItemExpectEqual(item.get(), deserialized_item.get()); +} + + +} // namespace
\ No newline at end of file diff --git a/webkit/glue/glue_util.cc b/webkit/glue/glue_util.cc new file mode 100644 index 0000000..b2eebb3 --- /dev/null +++ b/webkit/glue/glue_util.cc @@ -0,0 +1,107 @@ +// Copyright 2008, 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 <string> + +#include "webkit/glue/glue_util.h" +#include "base/string_util.h" + +#pragma warning(push, 0) +#include "CString.h" +#include "DeprecatedString.h" +#include "PlatformString.h" +#pragma warning(pop) + +#include "KURL.h" + +namespace webkit_glue { + +// String conversions ---------------------------------------------------------- + +std::string CStringToStdString(const WebCore::CString& str) { + const char* chars = str.data(); + return std::string(chars ? chars : "", str.length()); +} + +WebCore::CString StdStringToCString(const std::string& str) { + return WebCore::CString(str.data(), str.size()); +} + +std::wstring StringToStdWString(const WebCore::String& str) { + const UChar* chars = str.characters(); + return std::wstring(chars ? chars : L"", str.length()); +} + +WebCore::String StdWStringToString(const std::wstring& str) { + return WebCore::String(str.data(), static_cast<unsigned>(str.length())); +} + +WebCore::String StdStringToString(const std::string& str) { + return WebCore::String(str.data(), static_cast<unsigned>(str.length())); +} + +WebCore::DeprecatedString StdWStringToDeprecatedString( + const std::wstring& str) { + return WebCore::DeprecatedString( + reinterpret_cast<const WebCore::DeprecatedChar*>(str.c_str()), + static_cast<int>(str.size())); +} + +std::wstring DeprecatedStringToStdWString( + const WebCore::DeprecatedString& dep) { + return std::wstring(reinterpret_cast<const wchar_t*>(dep.unicode()), + dep.length()); +} + +// URL conversions ------------------------------------------------------------- + +GURL KURLToGURL(const WebCore::KURL& url) { +#ifdef USE_GOOGLE_URL_LIBRARY + const WebCore::CString& spec = url.utf8String(); + if (spec.isNull()) + return GURL(); + return GURL(spec.data(), spec.length(), url.parsed(), url.isValid()); +#else + return GURL(WideToUTF8(DeprecatedStringToStdWString(spec))); +#endif +} + +WebCore::KURL GURLToKURL(const GURL& url) { + const std::string& spec = url.possibly_invalid_spec(); +#ifdef USE_GOOGLE_URL_LIBRARY + // Convert using the internal structures to avoid re-parsing. + return WebCore::KURL(spec.c_str(), static_cast<int>(spec.length()), + url.parsed_for_possibly_invalid_spec(), url.is_valid()); +#else + return WebCore::KURL(StdWStringToDeprecatedString(UTF8ToWide(spec))); +#endif +} + +} // namespace webkit_glue diff --git a/webkit/glue/glue_util.h b/webkit/glue/glue_util.h new file mode 100644 index 0000000..f4de7a4 --- /dev/null +++ b/webkit/glue/glue_util.h @@ -0,0 +1,59 @@ +// Copyright 2008, 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 WEBKIT_GLUE_GLUE_UTIL_H__ +#define WEBKIT_GLUE_GLUE_UTIL_H__ + +#include <string> + +#include "googleurl/src/gurl.h" + +namespace WebCore { + class CString; + class DeprecatedString; + class KURL; + class String; +} + +namespace webkit_glue { + std::string CStringToStdString(const WebCore::CString& str); + WebCore::CString StdStringToCString(const std::string& str); + std::wstring StringToStdWString(const WebCore::String& str); + + WebCore::String StdWStringToString(const std::wstring& str); + WebCore::String StdStringToString(const std::string& str); + + WebCore::DeprecatedString StdWStringToDeprecatedString(const std::wstring& str); + std::wstring DeprecatedStringToStdWString(const WebCore::DeprecatedString& dep); + + GURL KURLToGURL(const WebCore::KURL& url); + WebCore::KURL GURLToKURL(const GURL& url); +} + +#endif // #ifndef WEBKIT_GLUE_GLUE_UTIL_H__ diff --git a/webkit/glue/iframe_redirect_unittest.cc b/webkit/glue/iframe_redirect_unittest.cc new file mode 100644 index 0000000..e10198e --- /dev/null +++ b/webkit/glue/iframe_redirect_unittest.cc @@ -0,0 +1,67 @@ +// Copyright 2008, 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 <string> + +#undef LOG + +#include "base/file_util.h" +#include "base/string_util.h" +#include "webkit/glue/webdatasource.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webview.h" +#include "webkit/tools/test_shell/test_shell_test.h" + +typedef TestShellTest IFrameRedirectTest; + +// Tests that loading a page in an iframe from javascript results in +// a redirect from about:blank. +TEST_F(IFrameRedirectTest, Test) { + std::wstring iframes_data_dir_ = data_dir_; + + file_util::AppendToPath(&iframes_data_dir_, L"test_shell"); + file_util::AppendToPath(&iframes_data_dir_, L"iframe_redirect"); + ASSERT_TRUE(file_util::PathExists(iframes_data_dir_)); + + std::wstring test_url = GetTestURL(iframes_data_dir_, L"main.html"); + + test_shell_->LoadURL(test_url.c_str()); + test_shell_->WaitTestFinished(); + + WebFrame* iframe = test_shell_->webView()->GetFrameWithName(L"ifr"); + ASSERT_TRUE(iframe != NULL); + WebDataSource* iframe_ds = iframe->GetDataSource(); + ASSERT_TRUE(iframe_ds != NULL); + const std::vector<GURL>& redirects = iframe_ds->GetRedirectChain(); + ASSERT_FALSE(redirects.empty()); + ASSERT_TRUE(redirects[0] == GURL("about:blank")); +} diff --git a/webkit/glue/image_decoder.cc b/webkit/glue/image_decoder.cc new file mode 100644 index 0000000..34a56b8 --- /dev/null +++ b/webkit/glue/image_decoder.cc @@ -0,0 +1,71 @@ +// Copyright 2008, 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 "webkit/glue/image_decoder.h" + +#pragma warning(push, 0) +#include "ImageSourceSkia.h" +#include "IntSize.h" +#include "RefPtr.h" +#include "SharedBuffer.h" +#pragma warning(pop) + +#include "SkBitmap.h" + +namespace webkit_glue { + +ImageDecoder::ImageDecoder() : desired_icon_size_(0, 0) { +} + +ImageDecoder::ImageDecoder(const gfx::Size& desired_icon_size) + : desired_icon_size_(desired_icon_size) { +} + +ImageDecoder::~ImageDecoder() { +} + +SkBitmap ImageDecoder::Decode(const unsigned char* data, size_t size) { + WebCore::ImageSourceSkia source; + WTF::RefPtr<WebCore::SharedBuffer> buffer(new WebCore::SharedBuffer( + data, static_cast<int>(size))); + source.setData(buffer.get(), true, + WebCore::IntSize(desired_icon_size_.width(), + desired_icon_size_.height())); + + if (!source.isSizeAvailable()) + return SkBitmap(); + + WebCore::NativeImagePtr frame0 = source.createFrameAtIndex(0); + if (!frame0) + return SkBitmap(); + + return *reinterpret_cast<SkBitmap*>(frame0); +} + +} // namespace webkit_glue diff --git a/webkit/glue/image_decoder.h b/webkit/glue/image_decoder.h new file mode 100644 index 0000000..e81e9da --- /dev/null +++ b/webkit/glue/image_decoder.h @@ -0,0 +1,63 @@ +// Copyright 2008, 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 "base/basictypes.h" +#include "base/gfx/size.h" +#include "base/scoped_ptr.h" + +class SkBitmap; + +namespace webkit_glue { + +// Provides an interface to WebKit's image decoders. +// +// Note to future: This class should be deleted. We should have our own nice +// image decoders in base/gfx, and our port should use those. Currently, it's +// the other way around. +class ImageDecoder { + public: + // Use the constructor with desired_size when you think you may have an .ico + // format and care about which size you get back. Otherwise, use the 0-arg + // constructor. + ImageDecoder(); + ImageDecoder(const gfx::Size& desired_icon_size); + ~ImageDecoder(); + + // Call this function to decode the image. If successful, the decoded image + // will be returned. Otherwise, an empty bitmap will be returned. + SkBitmap Decode(const unsigned char* data, size_t size); + + private: + // Size will be empty to get the largest possible size. + gfx::Size desired_icon_size_; + + DISALLOW_EVIL_CONSTRUCTORS(ImageDecoder); +}; + +} // namespace webkit_glue diff --git a/webkit/glue/image_resource_fetcher.cc b/webkit/glue/image_resource_fetcher.cc new file mode 100644 index 0000000..1279206 --- /dev/null +++ b/webkit/glue/image_resource_fetcher.cc @@ -0,0 +1,81 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "ImageSourceSkia.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/image_resource_fetcher.h" + +#include "base/gfx/size.h" +#include "webkit/glue/image_decoder.h" +#include "webkit/glue/webview_impl.h" +#include "skia/include/SkBitmap.h" + +ImageResourceFetcher::ImageResourceFetcher( + WebViewImpl* web_view, + int id, + const GURL& image_url, + int image_size) + : web_view_(web_view), + id_(id), + image_url_(image_url), + image_size_(image_size) { + fetcher_.reset(new ResourceFetcher(image_url, + web_view->main_frame()->frame(), + this)); +} + +ImageResourceFetcher::~ImageResourceFetcher() { + if (!fetcher_->completed()) + fetcher_->Cancel(); +} + +void ImageResourceFetcher::OnURLFetchComplete( + const WebCore::ResourceResponse& response, + const std::string& data) { + SkBitmap image; + bool errored = false; + if (response.isNull()) { + errored = true; + } else if (response.httpStatusCode() == 200) { + // Request succeeded, try to convert it to an image. + webkit_glue::ImageDecoder decoder(gfx::Size(image_size_, image_size_)); + image = decoder.Decode( + reinterpret_cast<const unsigned char*>(data.data()), data.size()); + } // else case: + // If we get here, it means no image from server or couldn't decode the + // response as an image. Need to notify the webview though, otherwise the + // browser will keep trying to download favicon (when this is used to + // download the favicon). + web_view_->ImageResourceDownloadDone(this, errored, image); +} diff --git a/webkit/glue/image_resource_fetcher.h b/webkit/glue/image_resource_fetcher.h new file mode 100644 index 0000000..66c7bcb --- /dev/null +++ b/webkit/glue/image_resource_fetcher.h @@ -0,0 +1,86 @@ +// Copyright 2008, 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 WEBKIT_GLUE_IMAGE_RESOURCE_FETCHER_H__ +#define WEBKIT_GLUE_IMAGE_RESOURCE_FETCHER_H__ + +#include "base/basictypes.h" +#include "webkit/glue/resource_fetcher.h" + +class SkBitmap; +class WebCore::ResourceResponse; +class WebViewImpl; + +// ImageResourceFetcher handles downloading an image for a webview. Once +// downloading is done the hosting WebViewImpl is notified. ImageResourceFetcher +// is used to download the favicon and images for web apps. +class ImageResourceFetcher : public ResourceFetcher::Delegate { + public: + ImageResourceFetcher(WebViewImpl* web_view, + int id, + const GURL& image_url, + int image_size); + + virtual ~ImageResourceFetcher(); + + // ResourceFetcher::Delegate method. Decodes the image and invokes one of + // DownloadFailed or DownloadedImage. + virtual void OnURLFetchComplete(const WebCore::ResourceResponse& response, + const std::string& data); + + // URL of the image we're downloading. + const GURL& image_url() const { return image_url_; } + + // Hosting WebView. + WebViewImpl* web_view() const { return web_view_; } + + // Unique identifier for the request. + int id() const { return id_; } + + private: + WebViewImpl* web_view_; + + // Unique identifier for the request. + const int id_; + + // URL of the image. + const GURL image_url_; + + // The size of the image. This is only a hint that is used if the image + // contains multiple sizes. A value of 0 results in using the first frame + // of the image. + const int image_size_; + + // Does the actual download. + scoped_ptr<ResourceFetcher> fetcher_; + + DISALLOW_EVIL_CONSTRUCTORS(ImageResourceFetcher); +}; + +#endif // WEBKIT_GLUE_IMAGE_RESOURCE_FETCHER_H__ diff --git a/webkit/glue/inspector_client_impl.cc b/webkit/glue/inspector_client_impl.cc new file mode 100644 index 0000000..9aeb981 --- /dev/null +++ b/webkit/glue/inspector_client_impl.cc @@ -0,0 +1,192 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "FloatRect.h" +#include "InspectorController.h" +#include "Page.h" +#include "Settings.h" +#pragma warning(pop) + +#undef LOG +#include "webkit/glue/inspector_client_impl.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webview_impl.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_util.h" + +using namespace WebCore; + +static const float kDefaultInspectorXPos = 10; +static const float kDefaultInspectorYPos = 50; +static const float kDefaultInspectorHeight = 640; +static const float kDefaultInspectorWidth = 480; + +WebInspectorClient::WebInspectorClient(WebView* webView) + : inspected_web_view_(webView) + , inspector_web_view_(0) { + ASSERT(inspected_web_view_); +} + +WebInspectorClient::~WebInspectorClient() { +} + +void WebInspectorClient::inspectorDestroyed() { + delete this; +} + +Page* WebInspectorClient::createPage() { + WebCore::Page* page; + + if (inspector_web_view_ != NULL) { + page = static_cast<WebViewImpl*>(inspector_web_view_)->page(); + ASSERT(page != NULL); + if (page != NULL) + return page; + } + + WebViewDelegate* delegate = inspected_web_view_->GetDelegate(); + if (!delegate) + return NULL; + inspector_web_view_ = delegate->CreateWebView(inspected_web_view_, true); + if (!inspector_web_view_) + return NULL; + + GURL inspector_url(webkit_glue::GetInspectorURL()); + scoped_ptr<WebRequest> request(WebRequest::Create(inspector_url)); + WebViewImpl* inspector_web_view_impl = + static_cast<WebViewImpl*>(inspector_web_view_); + inspector_web_view_impl->main_frame()->LoadRequest(request.get()); + + page = inspector_web_view_impl->page(); + + page->chrome()->setToolbarsVisible(false); + page->chrome()->setStatusbarVisible(false); + page->chrome()->setScrollbarsVisible(false); + page->chrome()->setMenubarVisible(false); + page->chrome()->setResizable(true); + + // Don't allow inspection of inspector. + page->settings()->setDeveloperExtrasEnabled(false); + page->settings()->setPrivateBrowsingEnabled(true); + page->settings()->setPluginsEnabled(false); + page->settings()->setJavaEnabled(false); + + FloatRect windowRect = page->chrome()->windowRect(); + FloatSize pageSize = page->chrome()->pageRect().size(); + windowRect.setX(kDefaultInspectorXPos); + windowRect.setY(kDefaultInspectorYPos); + windowRect.setWidth(kDefaultInspectorHeight); + windowRect.setHeight(kDefaultInspectorWidth); + page->chrome()->setWindowRect(windowRect); + + page->chrome()->show(); + + return page; +} + +void WebInspectorClient::showWindow() { + WebViewImpl* impl = static_cast<WebViewImpl*>(inspected_web_view_.get()); + InspectorController* inspector = impl->page()->inspectorController(); + inspector->setWindowVisible(true); + + // Notify the webview delegate of how many resources we're inspecting. + WebViewDelegate* d = impl->delegate(); + DCHECK(d); + d->WebInspectorOpened(inspector->resources().size()); +} + +void WebInspectorClient::closeWindow() { + inspector_web_view_ = NULL; + WebViewImpl* impl = static_cast<WebViewImpl*>(inspected_web_view_.get()); + WebFrameImpl* frame = static_cast<WebFrameImpl*>(impl->GetMainFrame()); + + if (frame && frame->inspected_node()) + hideHighlight(); + + if (impl->page()) + impl->page()->inspectorController()->setWindowVisible(false); +} + +bool WebInspectorClient::windowVisible() { + if (inspector_web_view_ != NULL) { + Page* page = static_cast<WebViewImpl*>(inspector_web_view_)->page(); + ASSERT(page != NULL); + if (page != NULL) + return true; + } + return false; +} + +void WebInspectorClient::attachWindow() { + // TODO(jackson): Implement this +} + +void WebInspectorClient::detachWindow() { + // TODO(jackson): Implement this +} + +static void invalidateNodeBoundingRect(WebViewImpl* web_view) { + // TODO(ojan): http://b/1143996 Is it important to just invalidate the rect + // of the node region given that this is not on a critical codepath? + // In order to do so, we'd have to take scrolling into account. + gfx::Size size = web_view->size(); + gfx::Rect damaged_rect(0, 0, size.width(), size.height()); + web_view->GetDelegate()->DidInvalidateRect(web_view, damaged_rect); +} + +void WebInspectorClient::highlight(Node* node) { + WebViewImpl* web_view = static_cast<WebViewImpl*>(inspected_web_view_.get()); + WebFrameImpl* frame = static_cast<WebFrameImpl*>(web_view->GetMainFrame()); + + if (frame->inspected_node()) + hideHighlight(); + + invalidateNodeBoundingRect(web_view); + frame->selectNodeFromInspector(node); +} + +void WebInspectorClient::hideHighlight() { + WebViewImpl* web_view = static_cast<WebViewImpl*>(inspected_web_view_.get()); + WebFrameImpl* frame = static_cast<WebFrameImpl*>(web_view->GetMainFrame()); + + invalidateNodeBoundingRect(web_view); + frame->selectNodeFromInspector(NULL); +} + +void WebInspectorClient::inspectedURLChanged(const String& newURL) { + // TODO(jackson): Implement this +} + +String WebInspectorClient::localizedStringsURL() { + notImplemented(); + return String(); +} diff --git a/webkit/glue/inspector_client_impl.h b/webkit/glue/inspector_client_impl.h new file mode 100644 index 0000000..e2135c7 --- /dev/null +++ b/webkit/glue/inspector_client_impl.h @@ -0,0 +1,70 @@ +// Copyright 2008, 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 WEBKIT_GLUE_INSPECTOR_CLIENT_IMPL_H__ +#define WEBKIT_GLUE_INSPECTOR_CLIENT_IMPL_H__ + +#include "InspectorClient.h" +#include "base/ref_counted.h" + +class WebNodeHighlight; +class WebView; + +class WebInspectorClient : public WebCore::InspectorClient { +public: + WebInspectorClient(WebView*); + + // InspectorClient + virtual void inspectorDestroyed(); + + virtual WebCore::Page* createPage(); + virtual WebCore::String localizedStringsURL(); + virtual void showWindow(); + virtual void closeWindow(); + virtual bool windowVisible(); + + virtual void attachWindow(); + virtual void detachWindow(); + + virtual void highlight(WebCore::Node*); + virtual void hideHighlight(); + + virtual void inspectedURLChanged(const WebCore::String& newURL); + +private: + ~WebInspectorClient(); + + // The WebView of the page being inspected; gets passed to the constructor + scoped_refptr<WebView> inspected_web_view_; + + // The WebView of the Inspector popup window + WebView* inspector_web_view_; +}; + +#endif // WEBKIT_GLUE_INSPECTOR_CLIENT_IMPL_H__ diff --git a/webkit/glue/localized_strings.cc b/webkit/glue/localized_strings.cc new file mode 100644 index 0000000..094e9c1 --- /dev/null +++ b/webkit/glue/localized_strings.cc @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2007 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "LocalizedStrings.h" +#include "PlatformString.h" +#include "IntSize.h" + +#undef LOG +#include "webkit/glue/webkit_glue.h" +#include "base/logging.h" +#include "base/string_util.h" + +#include "webkit_strings.h" + +using namespace WebCore; + +inline String GetLocalizedString(int message_id) { + const std::wstring& str(webkit_glue::GetLocalizedString(message_id)); + return String(str.c_str()); +} + +String WebCore::searchableIndexIntroduction() { + return GetLocalizedString(IDS_SEARCHABLE_INDEX_INTRO); +} +String WebCore::submitButtonDefaultLabel() { + return GetLocalizedString(IDS_FORM_SUBMIT_LABEL); +} +String WebCore::inputElementAltText() { + return GetLocalizedString(IDS_FORM_INPUT_ALT); +} +String WebCore::resetButtonDefaultLabel() { + return GetLocalizedString(IDS_FORM_RESET_LABEL); +} +String WebCore::fileButtonChooseFileLabel() { + return GetLocalizedString(IDS_FORM_FILE_BUTTON_LABEL); +} +String WebCore::fileButtonNoFileSelectedLabel() { + return GetLocalizedString(IDS_FORM_FILE_NO_FILE_LABEL); +} + +// TODO(tc): Do we actually plan on implementing search menu items? +String WebCore::searchMenuNoRecentSearchesText() { + return GetLocalizedString(IDS_RECENT_SEARCHES_NONE); +} +String WebCore::searchMenuRecentSearchesText() { + return GetLocalizedString(IDS_RECENT_SEARCHES); +} +String WebCore::searchMenuClearRecentSearchesText() { + return GetLocalizedString(IDS_RECENT_SEARCHES_CLEAR); +} + +// A11y strings. +String WebCore::AXWebAreaText() { + return GetLocalizedString(IDS_AX_ROLE_WEB_AREA); +} +String WebCore::AXLinkText() { + return GetLocalizedString(IDS_AX_ROLE_LINK); +} +String WebCore::AXListMarkerText() { + return GetLocalizedString(IDS_AX_ROLE_LIST_MARKER); +} +String WebCore::AXImageMapText() { + return GetLocalizedString(IDS_AX_ROLE_IMAGE_MAP); +} +String WebCore::AXHeadingText() { + return GetLocalizedString(IDS_AX_ROLE_HEADING); +} +String WebCore::AXButtonActionVerb() { + return GetLocalizedString(IDS_AX_BUTTON_ACTION_VERB); +} +String WebCore::AXRadioButtonActionVerb() { + return GetLocalizedString(IDS_AX_RADIO_BUTTON_ACTION_VERB); +} +String WebCore::AXTextFieldActionVerb() { + return GetLocalizedString(IDS_AX_TEXT_FIELD_ACTION_VERB); +} +String WebCore::AXCheckedCheckBoxActionVerb() { + return GetLocalizedString(IDS_AX_CHECKED_CHECK_BOX_ACTION_VERB); +} +String WebCore::AXUncheckedCheckBoxActionVerb() { + return GetLocalizedString(IDS_AX_UNCHECKED_CHECK_BOX_ACTION_VERB); +} +String WebCore::AXLinkActionVerb() { + return GetLocalizedString(IDS_AX_LINK_ACTION_VERB); +} + +// The following two functions are not declared in LocalizedStrings.h. +// They are used by the menu for the HTML keygen tag. +namespace WebCore { +String keygenMenuHighGradeKeySize() { + return GetLocalizedString(IDS_KEYGEN_HIGH_GRADE_KEY); +} +String keygenMenuMediumGradeKeySize() { + return GetLocalizedString(IDS_KEYGEN_MED_GRADE_KEY); +} +} //namespace WebCore + +// We don't use these strings, so they return an empty String. We can't just +// make them asserts because webcore still calls them. +String WebCore::contextMenuItemTagOpenLinkInNewWindow() { return String(); } +String WebCore::contextMenuItemTagDownloadLinkToDisk() { return String(); } +String WebCore::contextMenuItemTagCopyLinkToClipboard() { return String(); } +String WebCore::contextMenuItemTagOpenImageInNewWindow() { return String(); } +String WebCore::contextMenuItemTagDownloadImageToDisk() { return String(); } +String WebCore::contextMenuItemTagCopyImageToClipboard() { return String(); } +String WebCore::contextMenuItemTagOpenFrameInNewWindow() { return String(); } +String WebCore::contextMenuItemTagCopy() { return String(); } +String WebCore::contextMenuItemTagGoBack() { return String(); } +String WebCore::contextMenuItemTagGoForward() { return String(); } +String WebCore::contextMenuItemTagStop() { return String(); } +String WebCore::contextMenuItemTagReload() { return String(); } +String WebCore::contextMenuItemTagCut() { return String(); } +String WebCore::contextMenuItemTagPaste() { return String(); } +String WebCore::contextMenuItemTagNoGuessesFound() { return String(); } +String WebCore::contextMenuItemTagIgnoreSpelling() { return String(); } +String WebCore::contextMenuItemTagLearnSpelling() { return String(); } +String WebCore::contextMenuItemTagSearchWeb() { return String(); } +String WebCore::contextMenuItemTagLookUpInDictionary() { return String(); } +String WebCore::contextMenuItemTagOpenLink() { return String(); } +String WebCore::contextMenuItemTagIgnoreGrammar() { return String(); } +String WebCore::contextMenuItemTagSpellingMenu() { return String(); } +String WebCore::contextMenuItemTagCheckSpelling() { return String(); } +String WebCore::contextMenuItemTagCheckSpellingWhileTyping() { return String(); } +String WebCore::contextMenuItemTagCheckGrammarWithSpelling() { return String(); } +String WebCore::contextMenuItemTagFontMenu() { return String(); } +String WebCore::contextMenuItemTagBold() { return String(); } +String WebCore::contextMenuItemTagItalic() { return String(); } +String WebCore::contextMenuItemTagUnderline() { return String(); } +String WebCore::contextMenuItemTagOutline() { return String(); } +String WebCore::contextMenuItemTagWritingDirectionMenu() { return String(); } +String WebCore::contextMenuItemTagDefaultDirection() { return String(); } +String WebCore::contextMenuItemTagLeftToRight() { return String(); } +String WebCore::contextMenuItemTagRightToLeft() { return String(); } +String WebCore::contextMenuItemTagInspectElement() { return String(); } +String WebCore::contextMenuItemTagShowSpellingPanel(bool show) { return String(); } diff --git a/webkit/glue/mimetype_unittest.cc b/webkit/glue/mimetype_unittest.cc new file mode 100644 index 0000000..e9eb394 --- /dev/null +++ b/webkit/glue/mimetype_unittest.cc @@ -0,0 +1,111 @@ +// Copyright 2008, 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 <string> + +#undef LOG + +#include "base/string_util.h" +#include "net/url_request/url_request_unittest.h" +#include "webkit/glue/unittest_test_server.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webview.h" +#include "webkit/tools/test_shell/test_shell_test.h" + +namespace { + +class MimeTypeTests : public TestShellTest { + public: + void LoadURL(const GURL& url) { + test_shell_->LoadURL(UTF8ToWide(url.spec()).c_str()); + test_shell_->WaitTestFinished(); + } + + void CheckMimeType(const char* mimetype, const std::wstring& expected) { + std::string path("contenttype?"); + GURL url = server_->TestServerPage(path + mimetype); + LoadURL(url); + WebFrame* frame = test_shell_->webView()->GetMainFrame(); + EXPECT_EQ(expected, webkit_glue::DumpDocumentText(frame)); + } + + scoped_ptr<UnittestTestServer> server_; +}; + +TEST_F(MimeTypeTests, MimeTypeTests) { + server_.reset(new UnittestTestServer); + + std::wstring expected_src(L"<html>\n<body>\n" + L"<p>HTML text</p>\n</body>\n</html>\n"); + + // These files should all be displayed as plain text. + const char* plain_text[] = { + "text/css", + "text/foo", + "text/richext", + "text/rtf", + "application/x-javascript", + }; + for (int i = 0; i < arraysize(plain_text); ++i) { + CheckMimeType(plain_text[i], expected_src); + } + + // These should all be displayed as html content. + const char* html_src[] = { + "text/html", + "text/xml", + "text/xsl", + "application/xhtml+xml", + }; + for (int i = 0; i < arraysize(html_src); ++i) { + CheckMimeType(html_src[i], L"HTML text"); + } + + // These shouldn't be rendered as text or HTML, but shouldn't download + // either. + const char* not_text[] = { + "image/png", + "image/gif", + "image/jpeg", + "image/bmp", + }; + for (int i = 0; i < arraysize(not_text); ++i) { + CheckMimeType(not_text[i], L""); + test_shell_->webView()->StopLoading(); + } + + // TODO(tc): make sure other mime types properly go to download (e.g., + // image/foo). + + server_.reset(NULL); +} + +} // namespace diff --git a/webkit/glue/multipart_response_delegate.cc b/webkit/glue/multipart_response_delegate.cc new file mode 100644 index 0000000..e9cc258 --- /dev/null +++ b/webkit/glue/multipart_response_delegate.cc @@ -0,0 +1,255 @@ +// Copyright 2008, 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 <string> + +#include "config.h" +#pragma warning(push, 0) +#include "HTTPHeaderMap.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "String.h" +#pragma warning(pop) + +#undef LOG +#include "base/logging.h" +#include "base/string_util.h" +#include "webkit/glue/multipart_response_delegate.h" +#include "webkit/glue/glue_util.h" +#include "net/base/net_util.h" + +MultipartResponseDelegate::MultipartResponseDelegate( + WebCore::ResourceHandleClient* client, + WebCore::ResourceHandle* job, + const WebCore::ResourceResponse& response, + const std::string& boundary) + : client_(client), + job_(job), + original_response_(response), + boundary_("--"), + first_received_data_(true), + processing_headers_(false), + stop_sending_(false) { + boundary_.append(boundary); +} + +void MultipartResponseDelegate::OnReceivedData(const char* data, int data_len) { + // stop_sending_ means that we've already received the final boundary token. + // The server should stop sending us data at this point, but if it does, we + // just throw it away. + if (stop_sending_) + return; + + // TODO(tc): Figure out what to use for length_received. Maybe we can just + // pass the value on from our caller. See note in + // resource_handle_win.cc:ResourceHandleInternal::OnReceivedData. + int length_received = -1; + + data_.append(data, data_len); + if (first_received_data_) { + // Some servers don't send a boundary token before the first chunk of + // data. We handle this case anyway (Gecko does too). + first_received_data_ = false; + + // Eat leading \r\n + int pos = PushOverLine(data_, 0); + if (pos) + data_ = data_.substr(pos); + + if (data_.length() < boundary_.length() + 2) { + // We don't have enough data yet to make a boundary token. Just wait + // until the next chunk of data arrives. + first_received_data_ = true; + return; + } + + if (data_.substr(0, boundary_.length()) != boundary_) { + data_ = boundary_ + "\n" + data_; + } + } + DCHECK(!first_received_data_); + + // Headers + if (processing_headers_) { + if (ParseHeaders()) { + // Successfully parsed headers. + processing_headers_ = false; + } else { + // Get more data before trying again. + return; + } + } + DCHECK(!processing_headers_); + + int token_line_feed = 1; + size_t boundary_pos; + while ((boundary_pos = FindBoundary()) != std::string::npos) { + if (boundary_pos > 0) { + // Send the last data chunk. + client_->didReceiveData(job_, data_.substr(0, boundary_pos).data(), + static_cast<int>(boundary_pos), length_received); + } + size_t boundary_end_pos = boundary_pos + boundary_.length(); + if (boundary_end_pos < data_.length() && '-' == data_[boundary_end_pos]) { + // This was the last boundary so we can stop processing. + stop_sending_ = true; + data_.clear(); + return; + } + + // We can now throw out data up through the boundary + int offset = PushOverLine(data_, boundary_end_pos); + data_ = data_.substr(boundary_end_pos + offset); + + // Ok, back to parsing headers + if (!ParseHeaders()) { + processing_headers_ = true; + break; + } + } +} + +void MultipartResponseDelegate::OnCompletedRequest() { + // If we have any pending data and we're not in a header, go ahead and send + // it to WebCore. + if (!processing_headers_ && !data_.empty()) { + // TODO(tc): Figure out what to use for length_received. Maybe we can just + // pass the value on from our caller. + int length_received = -1; + client_->didReceiveData(job_, data_.data(), + static_cast<int>(data_.length()), length_received); + } +} + +int MultipartResponseDelegate::PushOverLine(const std::string& data, size_t pos) { + int offset = 0; + if (pos < data.length() && (data[pos] == '\r' || data[pos] == '\n')) { + ++offset; + if (pos + 1 < data.length() && data[pos + 1] == '\n') + ++offset; + } + return offset; +} + +bool MultipartResponseDelegate::ParseHeaders() { + int line_feed_increment = 1; + + // Grab the headers being liberal about line endings. + size_t line_start_pos = 0; + size_t line_end_pos = data_.find('\n'); + while (line_end_pos != std::string::npos) { + // Handle CRLF + if (line_end_pos > line_start_pos && data_[line_end_pos - 1] == '\r') { + line_feed_increment = 2; + --line_end_pos; + } else { + line_feed_increment = 1; + } + if (line_start_pos == line_end_pos) { + // A blank line, end of headers + line_end_pos += line_feed_increment; + break; + } + // Find the next header line. + line_start_pos = line_end_pos + line_feed_increment; + line_end_pos = data_.find('\n', line_start_pos); + } + // Truncated in the middle of a header, stop parsing. + if (line_end_pos == std::string::npos) + return false; + + // Eat headers + std::string headers("\n"); + headers.append(data_.substr(0, line_end_pos)); + data_ = data_.substr(line_end_pos); + + // Create a ResourceResponse based on the original set of headers + the + // replacement headers. We only replace the same few headers that gecko + // does. See netwerk/streamconv/converters/nsMultiMixedConv.cpp. + std::string mime_type = net_util::GetSpecificHeader(headers, + "content-type"); + std::string charset = net_util::GetHeaderParamValue(mime_type, + "charset"); + WebCore::ResourceResponse response(original_response_.url(), + webkit_glue::StdStringToString(mime_type.c_str()), + -1, + charset.c_str(), + WebCore::String()); + const WebCore::HTTPHeaderMap& orig_headers = + original_response_.httpHeaderFields(); + for (WebCore::HTTPHeaderMap::const_iterator it = orig_headers.begin(); + it != orig_headers.end(); ++it) { + if (!(equalIgnoringCase("content-type", it->first) || + equalIgnoringCase("content-length", it->first) || + equalIgnoringCase("content-disposition", it->first) || + equalIgnoringCase("content-range", it->first) || + equalIgnoringCase("range", it->first) || + equalIgnoringCase("set-cookie", it->first))) { + response.setHTTPHeaderField(it->first, it->second); + } + } + static const char* replace_headers[] = { + "Content-Type", + "Content-Length", + "Content-Disposition", + "Content-Range", + "Range", + "Set-Cookie" + }; + for (int i = 0; i < arraysize(replace_headers); ++i) { + std::string name(replace_headers[i]); + std::string value = net_util::GetSpecificHeader(headers, name); + if (!value.empty()) { + response.setHTTPHeaderField(webkit_glue::StdStringToString(name.c_str()), + webkit_glue::StdStringToString(value.c_str())); + } + } + // Send the response! + client_->didReceiveResponse(job_, response); + + return true; +} + +// Boundaries are supposed to be preceeded with --, but it looks like gecko +// doesn't require the dashes to exist. See nsMultiMixedConv::FindToken. +size_t MultipartResponseDelegate::FindBoundary() { + size_t boundary_pos = data_.find(boundary_); + if (boundary_pos != std::string::npos) { + // Back up over -- for backwards compat + // TODO(tc): Don't we only want to do this once? Gecko code doesn't seem + // to care. + if (boundary_pos >= 2) { + if ('-' == data_[boundary_pos - 1] && '-' == data_[boundary_pos - 2]) { + boundary_pos -= 2; + boundary_ = "--" + boundary_; + } + } + } + return boundary_pos; +} diff --git a/webkit/glue/multipart_response_delegate.h b/webkit/glue/multipart_response_delegate.h new file mode 100644 index 0000000..9b7c084 --- /dev/null +++ b/webkit/glue/multipart_response_delegate.h @@ -0,0 +1,139 @@ +// Copyright 2008, 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. +// +// A delegate class of ResourceHandleInternal (resource_handle_win) that +// handles multipart/x-mixed-replace data. We special case +// multipart/x-mixed-replace because WebCore expects a separate +// didReceiveResponse for each new message part. +// +// Most of the logic and edge case handling are based on the Mozilla's +// implementation in netwerk/streamconv/converters/nsMultiMixedConv.cpp. +// This seems like a derivative work, so here's the original license: + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <string> + +#include "config.h" +#pragma warning(push, 0) +#include "ResourceResponse.h" +#pragma warning(pop) + +namespace WebCore { + class ResourceHandle; + class ResourceHandleClient; +} + +class MultipartResponseDelegate { + friend class MultipartResponseTest_Functions_Test; // For unittests. + + public: + MultipartResponseDelegate(WebCore::ResourceHandleClient* client, + WebCore::ResourceHandle* job, + const WebCore::ResourceResponse& response, + const std::string& boundary); + + // Passed through from ResourceHandleInternal + void OnReceivedData(const char* data, int data_len); + void OnCompletedRequest(); + + private: + // Pointers back to our owning object so we can make callbacks as we parse + // pieces of data. + WebCore::ResourceHandleClient* client_; + WebCore::ResourceHandle* job_; + + // The original resource response for this request. We use this as a + // starting point for each parts response. + WebCore::ResourceResponse original_response_; + + // Checks to see if data[pos] character is a line break; handles crlf, lflf, + // lf, or cr. Returns the number of characters to skip over (0, 1 or 2). + int PushOverLine(const std::string& data, size_t pos); + + // Tries to parse http headers from the start of data_. Returns true if it + // succeeds and sends a didReceiveResponse to m_client. Returns false if + // the header is incomplete (in which case we just wait for more data). + bool ParseHeaders(); + + // Find the next boundary in data_. Returns std::string::npos if there's no + // full token. + size_t FindBoundary(); + + // A temporary buffer to hold data between reads for multipart data that + // gets split in the middle of a header. + std::string data_; + + // Multipart boundary token + std::string boundary_; + + // true until we get our first on received data call + bool first_received_data_; + + // true if we're truncated in the middle of a header + bool processing_headers_; + + // true when we're done sending information. At that point, we stop + // processing AddData requests. + bool stop_sending_; +}; diff --git a/webkit/glue/multipart_response_delegate_unittest.cc b/webkit/glue/multipart_response_delegate_unittest.cc new file mode 100644 index 0000000..d810dc0 --- /dev/null +++ b/webkit/glue/multipart_response_delegate_unittest.cc @@ -0,0 +1,401 @@ +// Copyright 2008, 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 <vector> + +#include "config.h" + +#pragma warning(push, 0) +#include "KURL.h" +#include "ResourceResponse.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "String.h" +#pragma warning(pop) + +#include "base/basictypes.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/multipart_response_delegate.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace WebCore; +using namespace std; + +namespace { + +class MultipartResponseTest : public testing::Test { +}; + +class MockResourceHandleClient : public ResourceHandleClient { + public: + MockResourceHandleClient() { Reset(); } + + virtual void didReceiveResponse(ResourceHandle* handle, + const ResourceResponse& response) { + ++received_response_; + resource_response_ = response; + data_.clear(); + } + virtual void didReceiveData(ResourceHandle* handle, + const char* data, int data_length, + int length_received) { + ++received_data_; + data_.append(data, data_length); + } + + void Reset() { + received_response_ = received_data_ = 0; + data_.clear(); + resource_response_ = ResourceResponse(); + } + + int received_response_, received_data_; + string data_; + ResourceResponse resource_response_; +}; + +} // namespace + +// We can't put this in an anonymous function because it's a friend class for +// access to private members. +TEST(MultipartResponseTest, Functions) { + // PushOverLine tests + + ResourceResponse response(KURL(), "multipart/x-mixed-replace", 0, "en-US", + String()); + response.setHTTPHeaderField(String("Foo"), String("Bar")); + response.setHTTPHeaderField(String("Content-type"), String("text/plain")); + MockResourceHandleClient client; + MultipartResponseDelegate delegate(&client, NULL, response, "bound"); + + struct { + const char* input; + const int position; + const int expected; + } line_tests[] = { + { "Line", 0, 0 }, + { "Line", 2, 0 }, + { "Line", 10, 0 }, + { "\r\nLine", 0, 2 }, + { "\nLine", 0, 1 }, + { "\n\nLine", 0, 2 }, + { "\rLine", 0, 1 }, + { "Line\r\nLine", 4, 2 }, + { "Line\nLine", 4, 1 }, + { "Line\n\nLine", 4, 2 }, + { "Line\rLine", 4, 1 }, + { "Line\r\rLine", 4, 1 }, + }; + for (int i = 0; i < arraysize(line_tests); ++i) { + EXPECT_EQ(line_tests[i].expected, + delegate.PushOverLine(line_tests[i].input, + line_tests[i].position)); + } + + // ParseHeaders tests + struct { + const char* data; + const bool rv; + const int received_response_calls; + const char* newdata; + } header_tests[] = { + { "This is junk", false, 0, "This is junk" }, + { "Foo: bar\nBaz:\n\nAfter:\n", true, 1, "After:\n" }, + { "Foo: bar\nBaz:\n", false, 0, "Foo: bar\nBaz:\n" }, + { "Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 1, "After:\r\n" }, + { "Foo: bar\r\nBaz:\r\n", false, 0, "Foo: bar\r\nBaz:\r\n" }, + { "Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 1, "After:\n\n" }, + { "Foo: bar\r\nBaz:\n", false, 0, "Foo: bar\r\nBaz:\n" }, + { "\r\n", true, 1, "" }, + }; + for (int i = 0; i < arraysize(header_tests); ++i) { + client.Reset(); + delegate.data_.assign(header_tests[i].data); + EXPECT_EQ(header_tests[i].rv, + delegate.ParseHeaders()); + EXPECT_EQ(header_tests[i].received_response_calls, + client.received_response_); + EXPECT_EQ(string(header_tests[i].newdata), + delegate.data_); + } + // Test that the resource response is filled in correctly when parsing + // headers. + client.Reset(); + string test_header("content-type: image/png\ncontent-length: 10\n\n"); + delegate.data_.assign(test_header); + EXPECT_TRUE(delegate.ParseHeaders()); + EXPECT_TRUE(delegate.data_.length() == 0); + EXPECT_EQ(webkit_glue::StringToStdWString( + client.resource_response_.httpHeaderField( + String("Content-Type"))), + wstring(L"image/png")); + EXPECT_EQ(webkit_glue::StringToStdWString( + client.resource_response_.httpHeaderField( + String("content-length"))), + wstring(L"10")); + // This header is passed from the original request. + EXPECT_EQ(webkit_glue::StringToStdWString( + client.resource_response_.httpHeaderField(String("foo"))), + wstring(L"Bar")); + + // FindBoundary tests + struct { + const char* boundary; + const char* data; + const size_t position; + } boundary_tests[] = { + { "bound", "bound", 0 }, + { "bound", "--bound", 0 }, + { "bound", "junkbound", 4 }, + { "bound", "junk--bound", 4 }, + { "foo", "bound", string::npos }, + { "bound", "--boundbound", 0 }, + }; + for (int i = 0; i < arraysize(boundary_tests); ++i) { + delegate.boundary_.assign(boundary_tests[i].boundary); + delegate.data_.assign(boundary_tests[i].data); + EXPECT_EQ(boundary_tests[i].position, + delegate.FindBoundary()); + } +} + +namespace { + +TEST(MultipartResponseTest, MissingBoundaries) { + ResourceResponse response(KURL(), "multipart/x-mixed-replace", 0, "en-US", + String()); + response.setHTTPHeaderField(String("Foo"), String("Bar")); + response.setHTTPHeaderField(String("Content-type"), String("text/plain")); + MockResourceHandleClient client; + MultipartResponseDelegate delegate(&client, NULL, response, "bound"); + + // No start boundary + string no_start_boundary( + "Content-type: text/plain\n\n" + "This is a sample response\n" + "--bound--" + "ignore junk after end token --bound\n\nTest2\n"); + delegate.OnReceivedData(no_start_boundary.c_str(), + static_cast<int>(no_start_boundary.length())); + EXPECT_EQ(1, client.received_response_); + EXPECT_EQ(1, client.received_data_); + EXPECT_EQ(string("This is a sample response\n"), + client.data_); + + delegate.OnCompletedRequest(); + EXPECT_EQ(1, client.received_response_); + EXPECT_EQ(1, client.received_data_); + + // No end boundary + client.Reset(); + MultipartResponseDelegate delegate2(&client, NULL, response, "bound"); + string no_end_boundary( + "bound\nContent-type: text/plain\n\n" + "This is a sample response\n"); + delegate2.OnReceivedData(no_end_boundary.c_str(), + static_cast<int>(no_end_boundary.length())); + EXPECT_EQ(1, client.received_response_); + EXPECT_EQ(0, client.received_data_); + EXPECT_EQ(string(), client.data_); + + delegate2.OnCompletedRequest(); + EXPECT_EQ(1, client.received_response_); + EXPECT_EQ(1, client.received_data_); + EXPECT_EQ(string("This is a sample response\n"), + client.data_); + + // Neither boundary + client.Reset(); + MultipartResponseDelegate delegate3(&client, NULL, response, "bound"); + string no_boundaries( + "Content-type: text/plain\n\n" + "This is a sample response\n"); + delegate3.OnReceivedData(no_boundaries.c_str(), + static_cast<int>(no_boundaries.length())); + EXPECT_EQ(1, client.received_response_); + EXPECT_EQ(0, client.received_data_); + EXPECT_EQ(string(), client.data_); + + delegate3.OnCompletedRequest(); + EXPECT_EQ(1, client.received_response_); + EXPECT_EQ(1, client.received_data_); + EXPECT_EQ(string("This is a sample response\n"), + client.data_); +} + + +// Used in for tests that break the data in various places. +struct TestChunk { + const int start_pos; // offset in data + const int end_pos; // end offset in data + const int expected_responses; + const int expected_received_data; + const char* expected_data; +}; + +void VariousChunkSizesTest(const TestChunk chunks[], int chunks_size, int responses, + int received_data, const char* completed_data) { + const string data( + "--bound\n" // 0-7 + "Content-type: image/png\n\n" // 8-32 + "datadatadatadatadata" // 33-52 + "--bound\n" // 53-60 + "Content-type: image/jpg\n\n" // 61-85 + "foofoofoofoofoo" // 86-100 + "--bound--"); // 101-109 + + ResourceResponse response(KURL(), "multipart/x-mixed-replace", 0, "en-US", + String()); + MockResourceHandleClient client; + MultipartResponseDelegate delegate(&client, NULL, response, "bound"); + + for (int i = 0; i < chunks_size; ++i) { + ASSERT(chunks[i].start_pos < chunks[i].end_pos); + string chunk = data.substr(chunks[i].start_pos, + chunks[i].end_pos - chunks[i].start_pos); + delegate.OnReceivedData(chunk.c_str(), static_cast<int>(chunk.length())); + EXPECT_EQ(chunks[i].expected_responses, + client.received_response_); + EXPECT_EQ(chunks[i].expected_received_data, + client.received_data_); + EXPECT_EQ(string(chunks[i].expected_data), + client.data_); + } + // Check final state + delegate.OnCompletedRequest(); + EXPECT_EQ(responses, + client.received_response_); + EXPECT_EQ(received_data, + client.received_data_); + EXPECT_EQ(string(completed_data), + client.data_); +} + +TEST(MultipartResponseTest, BreakInBoundary) { + // Break in the first boundary + const TestChunk bound1[] = { + { 0, 4, 0, 0, ""}, + { 4, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(bound1, arraysize(bound1), + 2, 2, "foofoofoofoofoo"); + + // Break in first and second + const TestChunk bound2[] = { + { 0, 4, 0, 0, ""}, + { 4, 55, 1, 0, "" }, + { 55, 65, 1, 1, "datadatadatadatadata" }, + { 65, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(bound2, arraysize(bound2), + 2, 2, "foofoofoofoofoo"); + + // Break in second only + const TestChunk bound3[] = { + { 0, 55, 1, 0, "" }, + { 55, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(bound3, arraysize(bound3), + 2, 2, "foofoofoofoofoo"); +} + +TEST(MultipartResponseTest, BreakInHeaders) { + // Break in first header + const TestChunk header1[] = { + { 0, 10, 0, 0, "" }, + { 10, 35, 1, 0, "" }, + { 35, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(header1, arraysize(header1), + 2, 2, "foofoofoofoofoo"); + + // Break in both headers + const TestChunk header2[] = { + { 0, 10, 0, 0, "" }, + { 10, 65, 1, 1, "datadatadatadatadata" }, + { 65, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(header2, arraysize(header2), + 2, 2, "foofoofoofoofoo"); + + // Break at end of a header + const TestChunk header3[] = { + { 0, 33, 1, 0, "" }, + { 33, 65, 1, 1, "datadatadatadatadata" }, + { 65, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(header3, arraysize(header3), + 2, 2, "foofoofoofoofoo"); +} + +TEST(MultipartResponseTest, BreakInData) { + // All data as one chunk + const TestChunk data1[] = { + { 0, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(data1, arraysize(data1), + 2, 2, "foofoofoofoofoo"); + + // breaks in data segment + const TestChunk data2[] = { + { 0, 35, 1, 0, "" }, + { 35, 65, 1, 1, "datadatadatadatadata" }, + { 65, 90, 2, 1, "" }, + { 90, 110, 2, 2, "foofoofoofoofoo" }, + }; + VariousChunkSizesTest(data2, arraysize(data2), + 2, 2, "foofoofoofoofoo"); + + // Incomplete send + const TestChunk data3[] = { + { 0, 35, 1, 0, "" }, + { 35, 90, 2, 1, "" }, + }; + VariousChunkSizesTest(data3, arraysize(data3), + 2, 2, "foof"); +} + +TEST(MultipartResponseTest, MultipleBoundaries) { + // Test multiple boundaries back to back + ResourceResponse response(KURL(), "multipart/x-mixed-replace", 0, "en-US", + String()); + MockResourceHandleClient client; + MultipartResponseDelegate delegate(&client, NULL, response, "bound"); + + string data("--bound\r\n\r\n--bound\r\n\r\nfoofoo--bound--"); + delegate.OnReceivedData(data.c_str(), static_cast<int>(data.length())); + EXPECT_EQ(2, + client.received_response_); + EXPECT_EQ(1, + client.received_data_); + EXPECT_EQ(string("foofoo"), + client.data_); +} + +} // namespace diff --git a/webkit/glue/password_autocomplete_listener.cc b/webkit/glue/password_autocomplete_listener.cc new file mode 100644 index 0000000..7270618 --- /dev/null +++ b/webkit/glue/password_autocomplete_listener.cc @@ -0,0 +1,110 @@ +// Copyright 2008, 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. +// +// This file provides the implementaiton of the password manager's autocomplete +// component. + +#include "webkit/glue/password_autocomplete_listener.h" +#include "base/logging.h" + +namespace webkit_glue { + +PasswordAutocompleteListener::PasswordAutocompleteListener( + AutocompleteEditDelegate* username_delegate, + HTMLInputDelegate* password_delegate, + const PasswordFormDomManager::FillData& data) + : AutocompleteInputListener(username_delegate), + password_delegate_(password_delegate), + data_(data) { +} + +void PasswordAutocompleteListener::OnBlur(const std::wstring& user_input) { + // If this listener exists, its because the password manager had more than + // one match for the password form, which implies it had at least one + // [preferred] username/password pair. + DCHECK(data_.basic_data.values.size() == 2); + + // Set the password field to match the current username. + if (data_.basic_data.values[0] == user_input) { + // Preferred username/login is selected. + password_delegate_->SetValue(data_.basic_data.values[1]); + } else if (data_.additional_logins.find(user_input) != + data_.additional_logins.end()) { + // One of the extra username/logins is selected. + password_delegate_->SetValue(data_.additional_logins[user_input]); + } + password_delegate_->OnFinishedAutocompleting(); +} + +void PasswordAutocompleteListener::OnInlineAutocompleteNeeded( + const std::wstring& user_input) { + // If wait_for_username is true, we only autofill the password when + // the username field is blurred (i.e not inline) with a matching + // username string entered. + if (data_.wait_for_username) + return; + // Look for any suitable matches to current field text. + // TODO(timsteele): The preferred login (in basic_data.values) and + // additional logins could be bundled into the same data structure + // (possibly even as WebCore strings) upon construction of the + // PasswordAutocompleteListener to simplify lookup and save string + // conversions (see SetValue) on each successful call to + // OnInlineAutocompleteNeeded. + if (TryToMatch(user_input, + data_.basic_data.values[0], + data_.basic_data.values[1])) { + return; + } + + // Scan additional logins for a match. + for (PasswordFormDomManager::LoginCollection::iterator it = + data_.additional_logins.begin(); + it != data_.additional_logins.end(); + ++it) { + if (TryToMatch(user_input, it->first, it->second)) + return; + } +} + +bool PasswordAutocompleteListener::TryToMatch(const std::wstring& input, + const std::wstring& username, + const std::wstring& password) { + if (input.compare(0, input.length(), username, 0, input.length()) != 0) + return false; + + // Input matches the username, fill in required values. + edit_delegate()->SetValue(username); + edit_delegate()->SetSelectionRange(input.length(), username.length()); + edit_delegate()->OnFinishedAutocompleting(); + password_delegate_->SetValue(password); + password_delegate_->OnFinishedAutocompleting(); + return true; +} + +} // webkit_glue diff --git a/webkit/glue/password_autocomplete_listener.h b/webkit/glue/password_autocomplete_listener.h new file mode 100644 index 0000000..9ee51c0 --- /dev/null +++ b/webkit/glue/password_autocomplete_listener.h @@ -0,0 +1,73 @@ +// Copyright 2008, 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. +// +// A concrete definition of the DOM autocomplete framework defined by +// autocomplete_input_listener.h, for the password manager. + +#ifndef WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H__ +#define WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H__ + +#include "base/basictypes.h" +#include "webkit/glue/autocomplete_input_listener.h" +#include "webkit/glue/password_form_dom_manager.h" + +namespace webkit_glue { + +class PasswordAutocompleteListener : public AutocompleteInputListener { + public: + PasswordAutocompleteListener(AutocompleteEditDelegate* username_delegate, + HTMLInputDelegate* password_delegate, + const PasswordFormDomManager::FillData& data); + virtual ~PasswordAutocompleteListener() { + } + + // AutocompleteInputListener implementation. + virtual void OnBlur(const std::wstring& user_input); + virtual void OnInlineAutocompleteNeeded(const std::wstring& user_input); + + private: + // Check if the input string resembles a potential matching login + // (username/password) and if so, match them up by autocompleting + // the edit delegates. + bool TryToMatch(const std::wstring& input, + const std::wstring& username, + const std::wstring& password); + + // Access to password field to autocomplete on blur/username updates. + scoped_ptr<HTMLInputDelegate> password_delegate_; + + // Contains the extra logins for matching on delta/blur. + PasswordFormDomManager::FillData data_; + + DISALLOW_EVIL_CONSTRUCTORS(PasswordAutocompleteListener); +}; + +} // webkit_glue + +#endif // WEBKIT_GLUE_PASSWORD_AUTOCOMPLETE_LISTENER_H__ diff --git a/webkit/glue/password_autocomplete_listener_unittest.cc b/webkit/glue/password_autocomplete_listener_unittest.cc new file mode 100644 index 0000000..4929f25 --- /dev/null +++ b/webkit/glue/password_autocomplete_listener_unittest.cc @@ -0,0 +1,274 @@ +// Copyright 2008, 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. +// +// The PasswordManagerAutocompleteTests in this file test only the +// PasswordAutocompleteListener class implementation (and not any of the +// higher level dom autocomplete framework). + +#include <string> +#include "config.h" +#pragma warning(push, 0) +#include "HTMLInputElement.h" +#include "HTMLFormElement.h" +#include "Document.h" +#include "Frame.h" +#include "Editor.h" +#include "EventNames.h" +#include "Event.h" +#include "EventListener.h" +#pragma warning(pop) + +#undef LOG + +#include "webkit/glue/autocomplete_input_listener.h" +#include "webkit/glue/password_autocomplete_listener.h" +#include "testing/gtest/include/gtest/gtest.h" + +using webkit_glue::AutocompleteInputListener; +using webkit_glue::PasswordAutocompleteListener; +using webkit_glue::AutocompleteEditDelegate; +using webkit_glue::HTMLInputDelegate; + +class TestHTMLInputDelegate : public HTMLInputDelegate { + public: + TestHTMLInputDelegate() : did_call_on_finish_(false), + did_set_value_(false), + did_set_selection_(false), + selection_start_(0), + selection_end_(0), + HTMLInputDelegate(NULL) { + } + + // Override those methods we implicitly invoke in the tests. + virtual std::wstring GetValue() const { + return value_; + } + + virtual void SetValue(const std::wstring& value) { + value_ = value; + did_set_value_ = true; + } + + virtual void OnFinishedAutocompleting() { + did_call_on_finish_ = true; + } + + virtual void SetSelectionRange(size_t start, size_t end) { + selection_start_ = start; + selection_end_ = end; + did_set_selection_ = true; + } + + // Testing-only methods. + void ResetTestState() { + did_call_on_finish_ = false; + did_set_value_ = false; + did_set_selection_ = false; + } + + bool did_call_on_finish() const { + return did_call_on_finish_; + } + + bool did_set_value() const { + return did_set_value_; + } + + bool did_set_selection() const { + return did_set_selection_; + } + + size_t selection_start() const { + return selection_start_; + } + + size_t selection_end() const { + return selection_end_; + } + + private: + bool did_call_on_finish_; + bool did_set_value_; + bool did_set_selection_; + std::wstring value_; + size_t selection_start_; + size_t selection_end_; +}; + +namespace { +class PasswordManagerAutocompleteTests : public testing::Test { + public: + virtual void SetUp() { + // Add a preferred login and an additional login to the FillData. + username1_ = L"alice"; + password1_ = L"password"; + username2_ = L"bob"; + password2_ = L"bobsyouruncle"; + data_.basic_data.values.push_back(username1_); + data_.basic_data.values.push_back(password1_); + data_.additional_logins[username2_] = password2_; + testing::Test::SetUp(); + } + + std::wstring username1_; + std::wstring password1_; + std::wstring username2_; + std::wstring password2_; + PasswordFormDomManager::FillData data_; +}; +} // namespace + +TEST_F(PasswordManagerAutocompleteTests, OnBlur) { + TestHTMLInputDelegate* username_delegate = new TestHTMLInputDelegate(); + TestHTMLInputDelegate* password_delegate = new TestHTMLInputDelegate(); + + PasswordAutocompleteListener* listener = new PasswordAutocompleteListener( + username_delegate, password_delegate, data_); + + // Clear the password field. + password_delegate->SetValue(std::wstring()); + // Simulate a blur event on the username field and expect a password autofill. + listener->OnBlur(username1_); + EXPECT_EQ(password1_, password_delegate->GetValue()); + + // Now the user goes back and changes the username to something we don't + // have saved. The password should remain unchanged. + listener->OnBlur(L"blahblahblah"); + EXPECT_EQ(password1_, password_delegate->GetValue()); + + // Now they type in the additional login username. + listener->OnBlur(username2_); + EXPECT_EQ(password2_, password_delegate->GetValue()); + + // Now they submit and the listener is destroyed. + delete listener; +} + +TEST_F(PasswordManagerAutocompleteTests, OnInlineAutocompleteNeeded) { + TestHTMLInputDelegate* username_delegate = new TestHTMLInputDelegate(); + TestHTMLInputDelegate* password_delegate = new TestHTMLInputDelegate(); + + PasswordAutocompleteListener* listener = new PasswordAutocompleteListener( + username_delegate, password_delegate, data_); + + password_delegate->SetValue(std::wstring()); + // Simulate the user typing in the first letter of 'alice', a stored username. + listener->OnInlineAutocompleteNeeded(L"a"); + // Both the username and password delegates should reflect selection + // of the stored login. + EXPECT_EQ(username1_, username_delegate->GetValue()); + EXPECT_EQ(password1_, password_delegate->GetValue()); + // And the selection should have been set to 'lice', the last 4 letters. + EXPECT_EQ(1, username_delegate->selection_start()); + EXPECT_EQ(username1_.length(), username_delegate->selection_end()); + // And both fields should have observed OnFinishedAutocompleting. + EXPECT_TRUE(username_delegate->did_call_on_finish()); + EXPECT_TRUE(password_delegate->did_call_on_finish()); + + // Now the user types the next letter of the same username, 'l'. + listener->OnInlineAutocompleteNeeded(L"al"); + // Now the fields should have the same value, but the selection should have a + // different start value. + EXPECT_EQ(username1_, username_delegate->GetValue()); + EXPECT_EQ(password1_, password_delegate->GetValue()); + EXPECT_EQ(2, username_delegate->selection_start()); + EXPECT_EQ(username1_.length(), username_delegate->selection_end()); + + // Now lets say the user goes astray from the stored username and types + // the letter 'f', spelling 'alf'. We don't know alf (that's just sad), + // so in practice the username should no longer be 'alice' and the selected + // range should be empty. In our case, when the autocomplete code doesn't + // know the text, it won't set the value or the selection and hence our + // delegate methods won't get called. The WebCore::HTMLInputElement's value + // and selection would be set directly by WebCore in practice. + + // Reset the delegate's test state so we can determine what, if anything, + // was invoked during OnInlineAutocompleteNeeded. + username_delegate->ResetTestState(); + password_delegate->ResetTestState(); + listener->OnInlineAutocompleteNeeded(L"alf"); + EXPECT_FALSE(username_delegate->did_set_selection()); + EXPECT_FALSE(username_delegate->did_set_value()); + EXPECT_FALSE(username_delegate->did_call_on_finish()); + EXPECT_FALSE(password_delegate->did_set_value()); + EXPECT_FALSE(password_delegate->did_call_on_finish()); + + // Ok, so now the user removes all the text and enters the letter 'b'. + listener->OnInlineAutocompleteNeeded(L"b"); + // The username and password fields should match the 'bob' entry. + EXPECT_EQ(username2_, username_delegate->GetValue()); + EXPECT_EQ(password2_, password_delegate->GetValue()); + EXPECT_EQ(1, username_delegate->selection_start()); + EXPECT_EQ(username2_.length(), username_delegate->selection_end()); + + // The End. + delete listener; +} + +TEST_F(PasswordManagerAutocompleteTests, TestWaitUsername) { + TestHTMLInputDelegate* username_delegate = new TestHTMLInputDelegate(); + TestHTMLInputDelegate* password_delegate = new TestHTMLInputDelegate(); + + // If we had an action authority mismatch (for example), we don't want to + // automatically autofill anything without some user interaction first. + // We require an explicit blur on the username field, and that a valid + // matching username is in the field, before we autofill passwords. + data_.wait_for_username = true; + PasswordAutocompleteListener* listener = new PasswordAutocompleteListener( + username_delegate, password_delegate, data_); + + std::wstring empty; + // In all cases, username_delegate should remain empty because we should + // never modify it when wait_for_username is true; only the user can by + // typing into (in real life) the HTMLInputElement. + password_delegate->SetValue(std::wstring()); + listener->OnInlineAutocompleteNeeded(L"a"); + EXPECT_EQ(empty, username_delegate->GetValue()); + EXPECT_EQ(empty, password_delegate->GetValue()); + listener->OnInlineAutocompleteNeeded(L"al"); + EXPECT_EQ(empty, username_delegate->GetValue()); + EXPECT_EQ(empty, password_delegate->GetValue()); + listener->OnInlineAutocompleteNeeded(L"alice"); + EXPECT_EQ(empty, username_delegate->GetValue()); + EXPECT_EQ(empty, password_delegate->GetValue()); + + listener->OnBlur(L"a"); + EXPECT_EQ(empty, username_delegate->GetValue()); + EXPECT_EQ(empty, password_delegate->GetValue()); + listener->OnBlur(L"ali"); + EXPECT_EQ(empty, username_delegate->GetValue()); + EXPECT_EQ(empty, password_delegate->GetValue()); + + // Blur with 'alice' should allow password autofill. + listener->OnBlur(L"alice"); + EXPECT_EQ(empty, username_delegate->GetValue()); + EXPECT_EQ(password1_, password_delegate->GetValue()); + + delete listener; +} diff --git a/webkit/glue/password_form.h b/webkit/glue/password_form.h new file mode 100644 index 0000000..cae102c --- /dev/null +++ b/webkit/glue/password_form.h @@ -0,0 +1,171 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PASSWORD_FORM_H__ +#define WEBKIT_GLUE_PASSWORD_FORM_H__ + +#include <string> +#include <map> + +#include "base/time.h" +#include "googleurl/src/gurl.h" + +// The PasswordForm struct encapsulates information about a login form, +// which can be an HTML form or a dialog with username/password text fields. +// +// The Web Data database stores saved username/passwords and associated form +// metdata using a PasswordForm struct, typically one that was created from +// a parsed HTMLFormElement or LoginDialog, but the saved entries could have +// also been created by imported data from another browser. +// +// The PasswordManager implements a fuzzy-matching algorithm to compare saved +// PasswordForm entries against PasswordForms that were created from a parsed +// HTML or dialog form. As one might expect, the more data contained in one +// of the saved PasswordForms, the better the job the PasswordManager can do +// in matching it against the actual form it was saved on, and autofill +// accurately. But it is not always possible, especially when importing from +// other browsers with different data models, to copy over all the information +// about a particular "saved password entry" to our PasswordForm +// representation. +// +// The field descriptions in the struct specification below are +// intended to describe which fields are not strictly required when adding a saved +// password entry to the database and how they can affect the matching process. + +struct PasswordForm { + // Enum to differentiate between HTML form based authentication, and dialogs + // using basic or digest schemes. Default is SCHEME_HTML. Only PasswordForms + // of the same Scheme will be matched/autofilled against each other. + enum Scheme { + SCHEME_HTML, + SCHEME_BASIC, + SCHEME_DIGEST, + SCHEME_OTHER + } scheme; + + // The "Realm" for the sign-on (scheme, host, port for SCHEME_HTML, and + // contains the HTTP realm for dialog-based forms). + // The signon_realm is effectively the primary key used for retrieving + // data from the database, so it must not be empty. + std::string signon_realm; + + // The URL (minus query parameters) containing the form. This is the primary + // data used by the PasswordManager to decide (in longest matching prefix + // fashion) whether or not a given PasswordForm result from the database is a + // good fit for a particular form on a page, so it must not be empty. + GURL origin; + + // The action target of the form. This is the primary data used by the + // PasswordManager for form autofill; that is, the action of the saved + // credentials must match the action of the form on the page to be autofilled. + // If this is empty / not available, it will result in a "restricted" + // IE-like autofill policy, where we wait for the user to type in his + // username before autofilling the password. In these cases, after successful + // login the action URL will automatically be assigned by the + // PasswordManager. + // + // When parsing an HTML form, this must always be set. + GURL action; + + // The name of the submit button used. Optional; only used in scoring + // of PasswordForm results from the database to make matches as tight as + // possible. + // + // When parsing an HTML form, this must always be set. + std::wstring submit_element; + + // The name of the username input element. Optional (improves scoring). + // + // When parsing an HTML form, this must always be set. + std::wstring username_element; + + // The username. Optional. + // + // When parsing an HTML form, this is typically empty unless the site + // has implemented some form of autofill. + std::wstring username_value; + + // The name of the password input element, Optional (improves scoring). + // + // When parsing an HTML form, this must always be set. + std::wstring password_element; + + // The password. Required. + // + // When parsing an HTML form, this is typically empty. + std::wstring password_value; + + // If the form was a change password form, the name of the + // 'old password' input element. Optional. + std::wstring old_password_element; + + // The old password. Optional. + std::wstring old_password_value; + + // Whether or not this login was saved under an HTTPS session with a valid + // SSL cert. We will never match or autofill a PasswordForm where + // ssl_valid == true with a PasswordForm where ssl_valid == false. This means + // passwords saved under HTTPS will never get autofilled onto an HTTP page. + // When importing, this should be set to true if the page URL is HTTPS, thus + // giving it "the benefit of the doubt" that the SSL cert was valid when it + // was saved. Default to false. + bool ssl_valid; + + // True if this PasswordForm represents the last username/password login the + // user selected to log in to the site. If there is only one saved entry for + // the site, this will always be true, but when there are multiple entries + // the PasswordManager ensures that only one of them has a preferred bit set + // to true. Default to false. + // + // When parsing an HTML form, this is not used. + bool preferred; + + // When the login was saved (by chrome). + // + // When parsing an HTML form, this is not used. + Time date_created; + + // Tracks if the user opted to never remember passwords for this form. Default + // to false. + // + // When parsing an HTML form, this is not used. + bool blacklisted_by_user; + + PasswordForm() + : scheme(SCHEME_HTML), + ssl_valid(false), + preferred(false), + blacklisted_by_user(false) { + } +}; + +// Map username to PasswordForm* for convenience. See password_form_manager.h. +typedef std::map<std::wstring, PasswordForm*> PasswordFormMap; + +#endif // WEBKIT_GLUE_PASSWORD_FORM_H__ diff --git a/webkit/glue/password_form_dom_manager.cc b/webkit/glue/password_form_dom_manager.cc new file mode 100644 index 0000000..0a3ed7c --- /dev/null +++ b/webkit/glue/password_form_dom_manager.cc @@ -0,0 +1,302 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "csshelper.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "KURL.h" +#pragma warning(pop) + +#undef LOG + +#include "base/logging.h" +#include "base/basictypes.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/password_form_dom_manager.h" + +// Maximum number of password fields we will observe before throwing our +// hands in the air and giving up with a given form. +static const int kMaxPasswords = 3; + +PasswordForm* PasswordFormDomManager::CreatePasswordForm( + WebCore::HTMLFormElement* form) { + WebCore::Frame* frame = form->document()->frame(); + if (frame == NULL) + return NULL; + + WebCore::FrameLoader* loader = frame->loader(); + if (loader == NULL) + return NULL; + + PasswordFormFields fields; + FindPasswordFormFields(form, &fields); + + // Get the document URL + WebCore::String origin_string = form->document()->documentURI(); + GURL full_origin(webkit_glue::StringToStdWString(origin_string)); + + // Calculate the canonical action URL + GURL full_action(webkit_glue::KURLToGURL(loader->completeURL(form->action()))); + if (!full_action.is_valid()) + return NULL; + + // Determine the types of the password fields + WebCore::HTMLInputElement* password = NULL; + WebCore::HTMLInputElement* old_password = NULL; + if (!LocateSpecificPasswords(&fields, &password, &old_password)) + return NULL; + + return AssemblePasswordFormResult(full_origin, full_action, + fields.submit, fields.username, + old_password, password); +} + +// static +PasswordFormDomManager::FillData* PasswordFormDomManager::CreateFillData( + const PasswordForm& form_on_page, + const PasswordFormMap& matches, + const PasswordForm* const preferred_match, + bool wait_for_username_before_autofill) { + DCHECK(preferred_match); + PasswordFormDomManager::FillData* result = + new PasswordFormDomManager::FillData(); + + // Fill basic form data. + result->basic_data.origin = form_on_page.origin; + result->basic_data.action = form_on_page.action; + result->basic_data.elements.push_back(form_on_page.username_element); + result->basic_data.values.push_back(preferred_match->username_value); + result->basic_data.elements.push_back(form_on_page.password_element); + result->basic_data.values.push_back(preferred_match->password_value); + result->basic_data.submit = form_on_page.submit_element; + result->wait_for_username = wait_for_username_before_autofill; + + // Copy additional username/value pairs. + PasswordFormMap::const_iterator iter; + for (iter = matches.begin(); iter != matches.end(); iter++) { + if (iter->second != preferred_match) + result->additional_logins[iter->first] = iter->second->password_value; + } + return result; +} + +// static +bool PasswordFormDomManager::LocateSpecificPasswords( + PasswordFormFields* fields, + WebCore::HTMLInputElement** password, + WebCore::HTMLInputElement** old_password) { + DCHECK(fields && password && old_password); + switch (fields->passwords.size()) { + case 1: + // Single password, easy. + *password = fields->passwords[0]; + break; + case 2: + if (fields->passwords[0]->value() == fields->passwords[1]->value()) + // Treat two identical passwords as a single password. + *password = fields->passwords[0]; + else { + // Assume first is old password, second is new (no choice but to guess). + *old_password = fields->passwords[0]; + *password = fields->passwords[1]; + } + break; + case 3: + if (fields->passwords[0]->value() == fields->passwords[1]->value() && + fields->passwords[0]->value() == fields->passwords[2]->value()) { + // All three passwords the same? Just treat as one and hope. + *password = fields->passwords[0]; + } else if (fields->passwords[0]->value() == + fields->passwords[1]->value()) { + // Two the same and one different -> old password is duplicated one. + *old_password = fields->passwords[0]; + *password = fields->passwords[2]; + } else if (fields->passwords[1]->value() == + fields->passwords[2]->value()) { + *old_password = fields->passwords[0]; + *password = fields->passwords[1]; + } else { + // Three different passwords, or first and last match with middle + // different. No idea which is which, so no luck. + return false; + } + break; + default: + return false; + } + return true; +} + // This method based on Firefox2 code in + // toolkit/components/passwordmgr/base/nsPasswordManager.cpp + // Its license block is + // + /* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Password Manager. + * + * The Initial Developer of the Original Code is + * Brian Ryner. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner <bryner@brianryner.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +// static +void PasswordFormDomManager::FindPasswordFormFields( + WebCore::HTMLFormElement* form, + PasswordFormFields* fields) { + DCHECK(form && fields); + int first_password_index = 0; + // First, find the password fields and activated submit button + const WTF::Vector<WebCore::HTMLGenericFormElement*>& form_elements = + form->formElements; + for (size_t i = 0; i < form_elements.size(); i++) { + WebCore::HTMLGenericFormElement* form_element = form_elements[i]; + if (form_element->isActivatedSubmit()) + fields->submit = form_element; + + if (!form_element->hasLocalName(WebCore::HTMLNames::inputTag)) + continue; + + WebCore::HTMLInputElement* input_element = + static_cast<WebCore::HTMLInputElement*>(form_element); + if (!input_element->isEnabled()) + continue; + + if ((fields->passwords.size() < kMaxPasswords) && + (input_element->inputType() == WebCore::HTMLInputElement::PASSWORD) && + (input_element->autoComplete())) { + if (fields->passwords.empty()) + first_password_index = i; + fields->passwords.push_back(input_element); + } + } + + if (!fields->passwords.empty()) { + // Then, search backwards for the username field + for (int i = first_password_index - 1; i >= 0; i--) { + WebCore::HTMLGenericFormElement* form_element = form_elements[i]; + if (!form_element->hasLocalName(WebCore::HTMLNames::inputTag)) + continue; + + WebCore::HTMLInputElement* input_element = + static_cast<WebCore::HTMLInputElement*>(form_element); + if (!input_element->isEnabled()) + continue; + + if ((input_element->inputType() == WebCore::HTMLInputElement::TEXT) && + (input_element->autoComplete())) { + fields->username = input_element; + break; + } + } + } +} + +// static +PasswordForm* PasswordFormDomManager::AssemblePasswordFormResult( + const GURL& full_origin, + const GURL& full_action, + WebCore::HTMLGenericFormElement* submit, + WebCore::HTMLInputElement* username, + WebCore::HTMLInputElement* old_password, + WebCore::HTMLInputElement* password) { + std::wstring empty; + PasswordForm* result = new PasswordForm(); + // Ignore the query and ref components + GURL::Replacements rep; + rep.ClearUsername(); + rep.ClearPassword(); + rep.ClearQuery(); + rep.ClearRef(); + // We want to keep the path but strip any authentication data, as well as + // query and ref portions of URL, for the form action and form origin. + result->action = full_action.ReplaceComponents(rep); + result->origin = full_origin.ReplaceComponents(rep); + + // Naming is confusing here because we have both the HTML form origin URL + // the page where the form was seen), and the "origin" components of the url + // (scheme, host, and port). + result->signon_realm = full_origin.GetOrigin().spec(); + // Note PasswordManager sets ssl_valid by asking the WebContents' SSLManager. + result->submit_element = + submit == NULL ? empty : webkit_glue::StringToStdWString(submit->name()); + result->username_element = + username == NULL ? empty + : webkit_glue::StringToStdWString(username->name()); + result->username_value = + username == NULL ? empty + : webkit_glue::StringToStdWString(username->value()); + result->password_element = + password == NULL ? empty + : webkit_glue::StringToStdWString(password->name()); + result->password_value = + password == NULL ? empty + : webkit_glue::StringToStdWString(password->value()); + result->old_password_element = + old_password == NULL ? empty + : webkit_glue::StringToStdWString(old_password->name()); + result->old_password_value = + old_password == NULL ? empty + : webkit_glue::StringToStdWString(old_password->value()); + return result; +} diff --git a/webkit/glue/password_form_dom_manager.h b/webkit/glue/password_form_dom_manager.h new file mode 100644 index 0000000..3e0b22c --- /dev/null +++ b/webkit/glue/password_form_dom_manager.h @@ -0,0 +1,158 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PASSWORD_FORM_DOM_MANAGER_H__ +#define WEBKIT_GLUE_PASSWORD_FORM_DOM_MANAGER_H__ + +#include "webkit/glue/form_data.h" +#include "webkit/glue/password_form.h" + +#include <map> +#include <vector> + +namespace WebCore { +class HTMLFormElement; +class HTMLInputElement; +class HTMLGenericFormElement; +} + +class GURL; + +class PasswordFormDomManager { + public: + typedef std::map<std::wstring, std::wstring> LoginCollection; + + // Structure used for autofilling password forms. + // basic_data identifies the HTML form on the page and preferred username/ + // password for login, while + // additional_logins is a list of other matching user/pass pairs for the form. + // wait_for_username tells us whether we need to wait for the user to enter + // a valid username before we autofill the password. By default, this is off + // unless the PasswordManager determined there is an additional risk + // associated with this form. This can happen, for example, if action URI's + // of the observed form and our saved representation don't match up. + struct FillData { + FormData basic_data; + LoginCollection additional_logins; + bool wait_for_username; + FillData() : wait_for_username(false) { + } + }; + + // Create a PasswordForm from DOM form. Webkit doesn't allow storing + // custom metadata to DOM nodes, so we have to do this every time an event + // happens with a given form and compare against previously Create'd forms + // to identify..which sucks. + static PasswordForm* CreatePasswordForm(WebCore::HTMLFormElement* form); + + // Create a FillData structure in preparation for autofilling a form, + // from basic_data identifying which form to fill, and a collection of + // matching stored logins to use as username/password values. + // preferred_match should equal (address) one of matches. + // wait_for_username_before_autofill is true if we should not autofill + // anything until the user typed in a valid username and blurred the field. + static FillData* CreateFillData(const PasswordForm& form_on_page, + const PasswordFormMap& matches, + const PasswordForm* const preferred_match, + bool wait_for_username_before_autofill); + private: + // Helper structure to locate username, passwords and submit fields. + struct PasswordFormFields { + WebCore::HTMLInputElement* username; + std::vector<WebCore::HTMLInputElement*> passwords; + WebCore::HTMLGenericFormElement* submit; + PasswordFormFields() : username(NULL), submit(NULL) { + } + }; + + // Helper to CreatePasswordForm to do the locating of username/password + // fields. + // This method based on Firefox2 code in + // toolkit/components/passwordmgr/base/nsPasswordManager.cpp + // Its license block is + + /* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Password Manager. + * + * The Initial Developer of the Original Code is + * Brian Ryner. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner <bryner@brianryner.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + static void FindPasswordFormFields(WebCore::HTMLFormElement* form, + PasswordFormFields* fields); + // Helper to determine which password is the main one, and which is + // an old password (e.g on a "make new password" form), if any. + static bool LocateSpecificPasswords( + PasswordFormFields* fields, + WebCore::HTMLInputElement** password, + WebCore::HTMLInputElement** old_password_index); + + // Helper to gather up the final form data and create a PasswordForm. + static PasswordForm* AssemblePasswordFormResult( + const GURL& full_origin, + const GURL& full_action, + WebCore::HTMLGenericFormElement* submit, + WebCore::HTMLInputElement* username, + WebCore::HTMLInputElement* old_password, + WebCore::HTMLInputElement* password); + + // This class can't be instantiated. + DISALLOW_IMPLICIT_CONSTRUCTORS(PasswordFormDomManager); +}; + +#endif // WEBKIT_GLUE_PASSWORD_FORM_DOM_MANAGER_H__ diff --git a/webkit/glue/plugins/mozilla_extensions.cc b/webkit/glue/plugins/mozilla_extensions.cc new file mode 100644 index 0000000..efda1df --- /dev/null +++ b/webkit/glue/plugins/mozilla_extensions.cc @@ -0,0 +1,395 @@ +// Copyright 2008, 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 <windows.h> +#include <Winhttp.h> + +#include <algorithm> + +#include "webkit/glue/plugins/mozilla_extensions.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "googleurl/src/gurl.h" +#include "net/base/net_errors.h" +#include "net/http/http_proxy_service.h" +#include "net/http/http_proxy_resolver_winhttp.h" +#include "third_party/npapi/bindings/npapi.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_instance.h" + +#define QI_SUPPORTS_IID(iid, iface) \ + QI_SUPPORTS_IID_(iid, iface::GetIID(), iface) + +#define QI_SUPPORTS_IID_(src_iid, iface_iid, iface) \ + if (iid.Equals(iface_iid)) { \ + AddRef(); \ + *result = static_cast<iface*>(this); \ + return NS_OK; \ + } + +namespace NPAPI +{ + +void MozillaExtensionApi::DetachFromInstance() { + plugin_instance_ = NULL; +} + +bool MozillaExtensionApi::FindProxyForUrl(const char* url, + std::string* proxy) { + bool result = false; + + if ((!url) || (!proxy)) { + NOTREACHED(); + return result; + } + + net::HttpProxyResolverWinHttp proxy_resolver; + net::HttpProxyService proxy_service(&proxy_resolver); + net::HttpProxyInfo proxy_info; + + if (proxy_service.ResolveProxy(GURL(std::string(url)), + &proxy_info, + NULL, + NULL) == net::OK) { + if (!proxy_info.is_direct()) { + std::wstring winhttp_proxy = proxy_info.proxy_server(); + + // Winhttp returns proxy in the the following format: + // - HTTP proxy: "111.111.111.111:11" + // -.SOCKS proxy: "socks=111.111.111.111:11" + // - Mixed proxy: "http=111.111.111.111:11; socks=222.222.222.222:22" + // + // We need to translate this into the following format: + // i) "DIRECT" -- no proxy + // ii) "PROXY xxx.xxx.xxx.xxx" -- use proxy + // iii) "SOCKS xxx.xxx.xxx.xxx" -- use SOCKS + // iv) Mixed. e.g. "PROXY 111.111.111.111;PROXY 112.112.112.112", + // "PROXY 111.111.111.111;SOCKS 112.112.112.112".... + StringToLowerASCII(winhttp_proxy); + if (std::wstring::npos == winhttp_proxy.find(L'=')) { + // Proxy is in the form: "111.111.111.111:11" + winhttp_proxy.insert(0, L"http "); + } else { + // Proxy is in the following form. + // -.SOCKS proxy: "socks=111.111.111.111:11" + // - Mixed proxy: "http=111.111.111.111:11; socks=222.222.222.222:22" + // in this case just replace the '=' with a space + std::replace_if(winhttp_proxy.begin(), + winhttp_proxy.end(), + std::bind2nd(std::equal_to<wchar_t>(), L'='), L' '); + } + + *proxy = WideToASCII(std::wstring(winhttp_proxy)); + result = true; + } + } + + return result; +} + +// nsISupports implementation +NS_IMETHODIMP MozillaExtensionApi::QueryInterface(REFNSIID iid, + void** result) { + static const nsIID knsISupportsIID = NS_ISUPPORTS_IID; + QI_SUPPORTS_IID_(iid, knsISupportsIID, nsIServiceManager) + QI_SUPPORTS_IID(iid, nsIServiceManager) + QI_SUPPORTS_IID(iid, nsIPluginManager) + QI_SUPPORTS_IID(iid, nsIPluginManager2) + QI_SUPPORTS_IID(iid, nsICookieStorage) + + NOTREACHED(); + return NS_ERROR_NO_INTERFACE; +} + +NS_IMETHODIMP_(nsrefcnt) MozillaExtensionApi::AddRef(void) { + return InterlockedIncrement(reinterpret_cast<LONG*>(&ref_count_)); +} + +NS_IMETHODIMP_(nsrefcnt) MozillaExtensionApi::Release(void) { + DCHECK(static_cast<int>(ref_count_) > 0); + if (InterlockedDecrement(reinterpret_cast<LONG*>(&ref_count_)) == 0) { + delete this; + return 0; + } + + return ref_count_; +} + +NS_IMETHODIMP MozillaExtensionApi::GetService(REFNSIID class_guid, + REFNSIID iid, + void** result) { + + static const nsIID kPluginManagerCID = NS_PLUGINMANAGER_CID; + static const nsIID kCookieStorageCID = NS_COOKIESTORAGE_CID; + + nsresult rv = NS_ERROR_FAILURE; + + if ((class_guid.Equals(kPluginManagerCID)) || + (class_guid.Equals(kCookieStorageCID))) { + rv = QueryInterface(iid, result); + } + + DCHECK(rv == NS_OK); + return rv; +} + +NS_IMETHODIMP MozillaExtensionApi::GetServiceByContractID( + const char* contract_id, + REFNSIID iid, + void** result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::IsServiceInstantiated(REFNSIID class_guid, + REFNSIID iid, + PRBool* result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::IsServiceInstantiatedByContractID( + const char* contract_id, + REFNSIID iid, + PRBool* result) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + + +NS_IMETHODIMP MozillaExtensionApi::GetValue(nsPluginManagerVariable variable, + void * value) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::ReloadPlugins(PRBool reloadPages) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::UserAgent( + const char** resultingAgentString) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::GetURL( + nsISupports* pluginInst, + const char* url, + const char* target, + nsIPluginStreamListener* streamListener, + const char* altHost, + const char* referrer, + PRBool forceJSEnabled) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::PostURL( + nsISupports* pluginInst, + const char* url, + unsigned int postDataLen, + const char* postData, + PRBool isFile, + const char* target, + nsIPluginStreamListener* streamListener, + const char* altHost, + const char* referrer, + PRBool forceJSEnabled , + unsigned int postHeadersLength, + const char* postHeaders) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::RegisterPlugin( + REFNSIID aCID, + const char *aPluginName, + const char *aDescription, + const char * * aMimeTypes, + const char * * aMimeDescriptions, + const char * * aFileExtensions, + PRInt32 aCount) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::UnregisterPlugin(REFNSIID aCID) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::GetURLWithHeaders( + nsISupports* pluginInst, + const char* url, + const char* target /* = NULL */, + nsIPluginStreamListener* streamListener /* = NULL */, + const char* altHost /* = NULL */, + const char* referrer /* = NULL */, + PRBool forceJSEnabled /* = PR_FALSE */, + PRUint32 getHeadersLength /* = 0 */, + const char* getHeaders /* = NULL */){ + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +// nsIPluginManager2 +NS_IMETHODIMP MozillaExtensionApi::BeginWaitCursor() { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::EndWaitCursor() { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::SupportsURLProtocol(const char* aProtocol, + PRBool* aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::NotifyStatusChange(nsIPlugin* aPlugin, + nsresult aStatus) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::FindProxyForURL( + const char* aURL, + char** aResult) { + std::string proxy = "DIRECT"; + FindProxyForUrl(aURL, &proxy); + + // Allocate this using the NPAPI allocator. The plugin will call + // NPN_Free to free this. + char* result = static_cast<char*>(NPN_MemAlloc(proxy.length() + 1)); + strncpy(result, proxy.c_str(), proxy.length() + 1); + + *aResult = result; + return NS_OK; +} + +NS_IMETHODIMP MozillaExtensionApi::RegisterWindow( + nsIEventHandler* handler, + nsPluginPlatformWindowRef window) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::UnregisterWindow( + nsIEventHandler* handler, + nsPluginPlatformWindowRef win) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::AllocateMenuID(nsIEventHandler* aHandler, + PRBool aIsSubmenu, + PRInt16 *aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::DeallocateMenuID(nsIEventHandler* aHandler, + PRInt16 aMenuID) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP MozillaExtensionApi::HasAllocatedMenuID(nsIEventHandler* aHandler, + PRInt16 aMenuID, + PRBool* aResult) { + NOTREACHED(); + return NS_ERROR_FAILURE; +} + +// nsICookieStorage +NS_IMETHODIMP MozillaExtensionApi::GetCookie( + const char* url, + void* cookie_buffer, + PRUint32& buffer_size) { + if ((!url) || (!cookie_buffer)) { + return NS_ERROR_INVALID_ARG; + } + + if (!plugin_instance_) + return NS_ERROR_FAILURE; + + WebPlugin* webplugin = plugin_instance_->webplugin(); + if (!webplugin) + return NS_ERROR_FAILURE; + + // Bypass third-party cookie blocking by using the url as the policy_url. + GURL cookies_url((std::string(url))); + std::string cookies = webplugin->GetCookies(cookies_url, cookies_url); + + if (cookies.empty()) + return NS_ERROR_FAILURE; + + if(cookies.length() >= buffer_size) + return NS_ERROR_FAILURE; + + strncpy(static_cast<char*>(cookie_buffer), + cookies.c_str(), + cookies.length() + 1); + + buffer_size = cookies.length(); + return NS_OK; +} + +NS_IMETHODIMP MozillaExtensionApi::SetCookie( + const char* url, + const void* cookie_buffer, + PRUint32 buffer_size){ + if ((!url) || (!cookie_buffer) || (!buffer_size)) { + return NS_ERROR_INVALID_ARG; + } + + if (!plugin_instance_) + return NS_ERROR_FAILURE; + + WebPlugin* webplugin = plugin_instance_->webplugin(); + if (!webplugin) + return NS_ERROR_FAILURE; + + std::string cookie(static_cast<const char*>(cookie_buffer), + buffer_size); + GURL cookies_url((std::string(url))); + webplugin->SetCookie(cookies_url, + cookies_url, + cookie); + return NS_OK; +} + + +} // namespace NPAPI diff --git a/webkit/glue/plugins/mozilla_extensions.h b/webkit/glue/plugins/mozilla_extensions.h new file mode 100644 index 0000000..737f9fe9 --- /dev/null +++ b/webkit/glue/plugins/mozilla_extensions.h @@ -0,0 +1,124 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_MOZILLA_EXTENSIONS_H__ +#define WEBKIT_GLUE_PLUGINS_MOZILLA_EXTENSIONS_H__ + +#include <windows.h> + +// Include npapi first to avoid definition clashes due to +// XP_WIN +#include "third_party/npapi/bindings/npapi.h" + +#include "third_party/mozilla/include/nsIServiceManager.h" +#include "third_party/mozilla/include/nsIPluginManager2.h" +#include "third_party/mozilla/include/nsICookieStorage.h" +#include "third_party/mozilla/include/nsError.h" + +#include "base/ref_counted.h" + +// NS_DECL_NSIPLUGINMANAGER doesn't include methods described as "C++" in the +// nsIPluginManager.idl. +#define NS_DECL_NSIPLUGINMANAGER_FIXED \ + NS_DECL_NSIPLUGINMANAGER \ + NS_IMETHOD \ + GetURL(nsISupports* pluginInst, \ + const char* url, \ + const char* target = NULL, \ + nsIPluginStreamListener* streamListener = NULL, \ + const char* altHost = NULL, \ + const char* referrer = NULL, \ + PRBool forceJSEnabled = PR_FALSE); \ + NS_IMETHOD \ + PostURL(nsISupports* pluginInst, \ + const char* url, \ + PRUint32 postDataLen, \ + const char* postData, \ + PRBool isFile = PR_FALSE, \ + const char* target = NULL, \ + nsIPluginStreamListener* streamListener = NULL, \ + const char* altHost = NULL, \ + const char* referrer = NULL, \ + PRBool forceJSEnabled = PR_FALSE, \ + PRUint32 postHeadersLength = 0, \ + const char* postHeaders = NULL); \ + NS_IMETHOD \ + GetURLWithHeaders(nsISupports* pluginInst, \ + const char* url, \ + const char* target = NULL, \ + nsIPluginStreamListener* streamListener = NULL, \ + const char* altHost = NULL, \ + const char* referrer = NULL, \ + PRBool forceJSEnabled = PR_FALSE, \ + PRUint32 getHeadersLength = 0, \ + const char* getHeaders = NULL); + +// Avoid dependence on the nsIsupportsImpl.h and so on. +#ifndef NS_DECL_ISUPPORTS +#define NS_DECL_ISUPPORTS \ + NS_IMETHOD QueryInterface(REFNSIID aIID, \ + void** aInstancePtr); \ + NS_IMETHOD_(nsrefcnt) AddRef(void); \ + NS_IMETHOD_(nsrefcnt) Release(void); +#endif // NS_DECL_ISUPPORTS + +namespace NPAPI +{ + +class PluginInstance; + +// Implementation of extended Mozilla interfaces needed to support +// Sun's new Java plugin. +class MozillaExtensionApi : public nsIServiceManager, + public nsIPluginManager2, + public nsICookieStorage { + public: + MozillaExtensionApi(PluginInstance* plugin_instance) : + plugin_instance_(plugin_instance), ref_count_(0) { + } + + void DetachFromInstance(); + + NS_DECL_ISUPPORTS + NS_DECL_NSISERVICEMANAGER + NS_DECL_NSIPLUGINMANAGER_FIXED + NS_DECL_NSIPLUGINMANAGER2 + NS_DECL_NSICOOKIESTORAGE + + protected: + bool FindProxyForUrl(const char* url, std::string* proxy); + + protected: + scoped_refptr<NPAPI::PluginInstance> plugin_instance_; + unsigned long ref_count_; +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGINS_MOZILLA_EXTENSIONS_H__ diff --git a/webkit/glue/plugins/nphostapi.h b/webkit/glue/plugins/nphostapi.h new file mode 100644 index 0000000..a248fd6 --- /dev/null +++ b/webkit/glue/plugins/nphostapi.h @@ -0,0 +1,302 @@ +// Copyright 2008, 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. + +// TODO: Did not implement JRIGlobalRef function yet. Not sure if this is used? + +#ifndef WEBKIT_GLUE_PLUGIN_NPHOSTAPI_H__ +#define WEBKIT_GLUE_PLUGIN_NPHOSTAPI_H__ + +#include "bindings/npapi.h" +#include "bindings/npruntime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// +// NPAPI NPP Function Pointers +// +typedef NPError (*NPP_NewProcPtr)(NPMIMEType pluginType, + NPP instance, + uint16 mode, + int16 argc, + char* argn[], + char* argv[], + NPSavedData* saved); +typedef NPError (*NPP_DestroyProcPtr)(NPP instance, + NPSavedData** save); +typedef NPError (*NPP_SetWindowProcPtr)(NPP instance, + NPWindow* window); +typedef NPError (*NPP_NewStreamProcPtr)(NPP instance, + NPMIMEType type, + NPStream* stream, + NPBool seekable, + uint16* stype); +typedef NPError (*NPP_DestroyStreamProcPtr)(NPP instance, + NPStream* stream, + NPReason reason); +typedef int32 (*NPP_WriteReadyProcPtr)(NPP instance, + NPStream* stream); +typedef int32 (*NPP_WriteProcPtr)(NPP instance, + NPStream* stream, + int32 offset, + int32 len, + void* buffer); +typedef void (*NPP_StreamAsFileProcPtr)(NPP instance, + NPStream* stream, + const char* fname); +typedef void (*NPP_PrintProcPtr)(NPP instance, + NPPrint* platformPrint); +typedef int16 (*NPP_HandleEventProcPtr)(NPP instance, + void* event); +typedef void (*NPP_URLNotifyProcPtr)(NPP instance, + const char* url, + NPReason reason, + void* notifyData); +typedef void* JRIGlobalRef; //not using this right now +typedef NPError (*NPP_GetValueProcPtr)(NPP instance, + NPPVariable variable, + void *ret_alue); +typedef NPError (*NPP_SetValueProcPtr)(NPP instance, + NPNVariable variable, + void *ret_alue); + +// +// NPAPI NPN Function Pointers +// +typedef NPError (*NPN_GetURLProcPtr)(NPP instance, + const char* URL, + const char* window); +typedef NPError (*NPN_PostURLProcPtr)(NPP instance, + const char* URL, + const char* window, + uint32 len, + const char* buf, + NPBool file); +typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream, + NPByteRange* rangeList); +typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, + NPMIMEType type, + const char* window, + NPStream** stream); +typedef int32 (*NPN_WriteProcPtr)(NPP instance, + NPStream* stream, + int32 len, + void* buffer); +typedef NPError (*NPN_DestroyStreamProcPtr)(NPP instance, + NPStream* stream, + NPReason reason); +typedef void (*NPN_StatusProcPtr)(NPP instance, + const char* message); +typedef const char* (*NPN_UserAgentProcPtr)(NPP instance); +typedef void* (*NPN_MemAllocProcPtr)(uint32 size); +typedef void (*NPN_MemFreeProcPtr)(void* ptr); +typedef uint32 (*NPN_MemFlushProcPtr)(uint32 size); +typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages); + +typedef void* (*NPN_GetJavaEnvProcPtr)(void); +typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance); + +typedef NPError (*NPN_GetURLNotifyProcPtr)(NPP instance, + const char* URL, + const char* window, + void* notifyData); +typedef NPError (*NPN_PostURLNotifyProcPtr)(NPP instance, + const char* URL, + const char* window, + uint32 len, + const char* buf, + NPBool file, + void* notifyData); +typedef NPError (*NPN_GetValueProcPtr)(NPP instance, + NPNVariable variable, + void *ret_value); +typedef NPError (*NPN_SetValueProcPtr)(NPP instance, + NPPVariable variable, + void *value); +typedef void (*NPN_InvalidateRectProcPtr)(NPP instance, + NPRect *rect); +typedef void (*NPN_InvalidateRegionProcPtr)(NPP instance, + NPRegion region); +typedef void (*NPN_ForceRedrawProcPtr)(NPP instance); + +typedef void (*NPN_ReleaseVariantValueProcPtr) (NPVariant *variant); + +typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr) (const NPUTF8 *name); +typedef void (*NPN_GetStringIdentifiersProcPtr) (const NPUTF8 **names, + int32_t nameCount, + NPIdentifier *identifiers); +typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr) (int32_t intid); +typedef int32_t (*NPN_IntFromIdentifierProcPtr) (NPIdentifier identifier); +typedef bool (*NPN_IdentifierIsStringProcPtr) (NPIdentifier identifier); +typedef NPUTF8 * (*NPN_UTF8FromIdentifierProcPtr) (NPIdentifier identifier); + +typedef NPObject* (*NPN_CreateObjectProcPtr) (NPP, + NPClass *aClass); +typedef NPObject* (*NPN_RetainObjectProcPtr) (NPObject *obj); +typedef void (*NPN_ReleaseObjectProcPtr) (NPObject *obj); +typedef bool (*NPN_InvokeProcPtr) (NPP npp, + NPObject *obj, + NPIdentifier methodName, + const NPVariant *args, + unsigned argCount, + NPVariant *result); +typedef bool (*NPN_InvokeDefaultProcPtr) (NPP npp, + NPObject *obj, + const NPVariant *args, + unsigned argCount, + NPVariant *result); +typedef bool (*NPN_EvaluateProcPtr) (NPP npp, + NPObject *obj, + NPString *script, + NPVariant *result); +typedef bool (*NPN_GetPropertyProcPtr) (NPP npp, + NPObject *obj, + NPIdentifier propertyName, + NPVariant *result); +typedef bool (*NPN_SetPropertyProcPtr) (NPP npp, + NPObject *obj, + NPIdentifier propertyName, + const NPVariant *value); +typedef bool (*NPN_HasPropertyProcPtr) (NPP, + NPObject *npobj, + NPIdentifier propertyName); +typedef bool (*NPN_HasMethodProcPtr) (NPP npp, + NPObject *npobj, + NPIdentifier methodName); +typedef bool (*NPN_RemovePropertyProcPtr) (NPP npp, + NPObject *obj, + NPIdentifier propertyName); +typedef void (*NPN_SetExceptionProcPtr) (NPObject *obj, + const NPUTF8 *message); +typedef void (*NPN_PushPopupsEnabledStateProcPtr)(NPP npp, + NPBool enabled); +typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP npp); +typedef bool (*NPN_EnumerateProcPtr)(NPP npp, + NPObject *obj, + NPIdentifier **identifier, + uint32_t *count); +typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP instance, + void (*func)(void *), + void *userData); +typedef bool (*NPN_ConstructProcPtr)(NPP npp, + NPObject* obj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result); + +// +// NPAPI Function table of NPP functions (functions provided by plugin to host) +// +typedef struct _NPPluginFuncs { + unsigned short size; + unsigned short version; + NPP_NewProcPtr newp; + NPP_DestroyProcPtr destroy; + NPP_SetWindowProcPtr setwindow; + NPP_NewStreamProcPtr newstream; + NPP_DestroyStreamProcPtr destroystream; + NPP_StreamAsFileProcPtr asfile; + NPP_WriteReadyProcPtr writeready; + NPP_WriteProcPtr write; + NPP_PrintProcPtr print; + NPP_HandleEventProcPtr event; + NPP_URLNotifyProcPtr urlnotify; + JRIGlobalRef javaClass; + NPP_GetValueProcPtr getvalue; + NPP_SetValueProcPtr setvalue; +} NPPluginFuncs; + +// +// NPAPI Function table NPN functions (functions provided by host to plugin) +// +typedef struct _NPNetscapeFuncs { + uint16 size; + uint16 version; + NPN_GetURLProcPtr geturl; + NPN_PostURLProcPtr posturl; + NPN_RequestReadProcPtr requestread; + NPN_NewStreamProcPtr newstream; + NPN_WriteProcPtr write; + NPN_DestroyStreamProcPtr destroystream; + NPN_StatusProcPtr status; + NPN_UserAgentProcPtr uagent; + NPN_MemAllocProcPtr memalloc; + NPN_MemFreeProcPtr memfree; + NPN_MemFlushProcPtr memflush; + NPN_ReloadPluginsProcPtr reloadplugins; + NPN_GetJavaEnvProcPtr getJavaEnv; + NPN_GetJavaPeerProcPtr getJavaPeer; + NPN_GetURLNotifyProcPtr geturlnotify; + NPN_PostURLNotifyProcPtr posturlnotify; + NPN_GetValueProcPtr getvalue; + NPN_SetValueProcPtr setvalue; + NPN_InvalidateRectProcPtr invalidaterect; + NPN_InvalidateRegionProcPtr invalidateregion; + NPN_ForceRedrawProcPtr forceredraw; + + NPN_GetStringIdentifierProcPtr getstringidentifier; + NPN_GetStringIdentifiersProcPtr getstringidentifiers; + NPN_GetIntIdentifierProcPtr getintidentifier; + NPN_IdentifierIsStringProcPtr identifierisstring; + NPN_UTF8FromIdentifierProcPtr utf8fromidentifier; + NPN_IntFromIdentifierProcPtr intfromidentifier; + NPN_CreateObjectProcPtr createobject; + NPN_RetainObjectProcPtr retainobject; + NPN_ReleaseObjectProcPtr releaseobject; + NPN_InvokeProcPtr invoke; + NPN_InvokeDefaultProcPtr invokeDefault; + NPN_EvaluateProcPtr evaluate; + NPN_GetPropertyProcPtr getproperty; + NPN_SetPropertyProcPtr setproperty; + NPN_RemovePropertyProcPtr removeproperty; + NPN_HasPropertyProcPtr hasproperty; + NPN_HasMethodProcPtr hasmethod; + NPN_ReleaseVariantValueProcPtr releasevariantvalue; + NPN_SetExceptionProcPtr setexception; + NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate; + NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate; + NPN_EnumerateProcPtr enumerate; + NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall; + NPN_ConstructProcPtr construct; +} NPNetscapeFuncs; + +// +// NPAPI DLL entry points +// +typedef NPError (__stdcall * NP_InitializeFunc)(NPNetscapeFuncs* pFuncs); +typedef NPError (__stdcall * NP_GetEntryPointsFunc)(NPPluginFuncs* pFuncs); +typedef NPError (__stdcall * NP_ShutdownFunc)(void); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBKIT_GLUE_PLUGIN_NPHOSTAPI_H__ diff --git a/webkit/glue/plugins/plugin_data_stream.cc b/webkit/glue/plugins/plugin_data_stream.cc new file mode 100644 index 0000000..bb7ee0b --- /dev/null +++ b/webkit/glue/plugins/plugin_data_stream.cc @@ -0,0 +1,68 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_data_stream.h" + +namespace NPAPI { + +PluginDataStream::PluginDataStream(PluginInstance *instance, + const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified) + : PluginStream(instance, url.c_str(), false, 0), + mime_type_(mime_type), + headers_(headers), + expected_length_(expected_length), + last_modified_(last_modified), + stream_open_failed_(false) { +} + +PluginDataStream::~PluginDataStream() { +} + +void PluginDataStream::SendToPlugin(const char* buffer, int length) { + if (stream_open_failed_) + return; + + if (!open()) { + if (!Open(mime_type_, headers_, expected_length_, last_modified_)) { + stream_open_failed_ = true; + return; + } + } + + // TODO(iyengar) - check if it was not fully sent, and figure out a + // backup plan. + int written = Write(buffer, length); + DCHECK(written == length); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_data_stream.h b/webkit/glue/plugins/plugin_data_stream.h new file mode 100644 index 0000000..64cc3b1 --- /dev/null +++ b/webkit/glue/plugins/plugin_data_stream.h @@ -0,0 +1,66 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGIN_PLUGIN_DATA_STREAM_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_DATA_STREAM_H__ + +#include "webkit/glue/plugins/plugin_stream.h" + +namespace NPAPI { + +class PluginInstance; + +// A NPAPI stream based on data received from the renderer. +class PluginDataStream : public PluginStream { + public: + // Create a new stream for sending to the plugin. + PluginDataStream(PluginInstance *instance, const std::string& url, + const std::string& mime_type, const std::string& headers, + uint32 expected_length, uint32 last_modified); + virtual ~PluginDataStream(); + + // Initiates the sending of data to the plugin. + void SendToPlugin(const char* buffer, int length); + + private: + std::string mime_type_; + std::string headers_; + uint32 expected_length_; + uint32 last_modified_; + // This flag when set serves as an indicator that subsequent + // data coming from the renderer should not be handed off to the plugin. + bool stream_open_failed_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginDataStream); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_DATA_STREAM_H__ + diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc new file mode 100644 index 0000000..63cd6ca --- /dev/null +++ b/webkit/glue/plugins/plugin_host.cc @@ -0,0 +1,877 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_host.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "webkit/default_plugin/default_plugin_shared.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/plugins/plugin_stream_url.h" +#include "third_party/npapi/bindings/npruntime.h" + +extern "C" { + +// FindInstance() +// Finds a PluginInstance from an NPP. +// The caller must take a reference if needed. +NPAPI::PluginInstance* FindInstance(NPP id) { + if (id == NULL) { + NOTREACHED(); + return NULL; + } + + return (NPAPI::PluginInstance *)id->ndata; +} + +namespace NPAPI +{ +scoped_refptr<PluginHost> PluginHost::singleton_; + +static const int kFlashMessageThrottleDelayMs = 10; + +PluginHost::PluginHost() +#pragma warning(suppress: 4355) // can use this + : throttle_factory_(this) { + InitializeHostFuncs(); +} + +PluginHost::~PluginHost() { +} + +PluginHost *PluginHost::Singleton() { + if (singleton_.get() == NULL) { + singleton_ = new PluginHost(); + } + + DCHECK(singleton_.get() != NULL); + return singleton_; +} + +void PluginHost::InitializeHostFuncs() { + memset(&host_funcs_, 0, sizeof(host_funcs_)); + host_funcs_.size = sizeof(host_funcs_); + host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR); + + // The "basic" functions + host_funcs_.geturl = &NPN_GetURL; + host_funcs_.posturl = &NPN_PostURL; + host_funcs_.requestread = &NPN_RequestRead; + host_funcs_.newstream = &NPN_NewStream; + host_funcs_.write = &NPN_Write; + host_funcs_.destroystream = &NPN_DestroyStream; + host_funcs_.status = &NPN_Status; + host_funcs_.uagent = &NPN_UserAgent; + host_funcs_.memalloc = &NPN_MemAlloc; + host_funcs_.memfree = &NPN_MemFree; + host_funcs_.memflush = &NPN_MemFlush; + host_funcs_.reloadplugins = &NPN_ReloadPlugins; + + // We don't implement java yet + host_funcs_.getJavaEnv = &NPN_GetJavaEnv; + host_funcs_.getJavaPeer = &NPN_GetJavaPeer; + + // Advanced functions we implement + host_funcs_.geturlnotify = &NPN_GetURLNotify; + host_funcs_.posturlnotify = &NPN_PostURLNotify; + host_funcs_.getvalue = &NPN_GetValue; + host_funcs_.setvalue = &NPN_SetValue; + host_funcs_.invalidaterect = &NPN_InvalidateRect; + host_funcs_.invalidateregion = &NPN_InvalidateRegion; + host_funcs_.forceredraw = &NPN_ForceRedraw; + + // These come from the Javascript Engine + host_funcs_.getstringidentifier = NPN_GetStringIdentifier; + host_funcs_.getstringidentifiers = NPN_GetStringIdentifiers; + host_funcs_.getintidentifier = NPN_GetIntIdentifier; + host_funcs_.identifierisstring = NPN_IdentifierIsString; + host_funcs_.utf8fromidentifier = NPN_UTF8FromIdentifier; + host_funcs_.intfromidentifier = NPN_IntFromIdentifier; + host_funcs_.createobject = NPN_CreateObject; + host_funcs_.retainobject = NPN_RetainObject; + host_funcs_.releaseobject = NPN_ReleaseObject; + host_funcs_.invoke = NPN_Invoke; + host_funcs_.invokeDefault = NPN_InvokeDefault; + host_funcs_.evaluate = NPN_Evaluate; + host_funcs_.getproperty = NPN_GetProperty; + host_funcs_.setproperty = NPN_SetProperty; + host_funcs_.removeproperty = NPN_RemoveProperty; + host_funcs_.hasproperty = NPN_HasProperty; + host_funcs_.hasmethod = NPN_HasMethod; + host_funcs_.releasevariantvalue = NPN_ReleaseVariantValue; + host_funcs_.setexception = NPN_SetException; + host_funcs_.pushpopupsenabledstate = &NPN_PushPopupsEnabledState; + host_funcs_.poppopupsenabledstate = &NPN_PopPopupsEnabledState; + host_funcs_.enumerate = &NPN_Enumerate; + host_funcs_.pluginthreadasynccall = &NPN_PluginThreadAsyncCall; + host_funcs_.construct = &NPN_Construct; + +} + +void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) { + // When running in the plugin process, we need to patch the NPN functions + // that the plugin calls to interact with NPObjects that we give. Otherwise + // the plugin will call the v8 NPN functions, which won't work since we have + // an NPObjectProxy and not a real v8 implementation. + if (overrides->invoke) + host_funcs_.invoke = overrides->invoke; + + if (overrides->invokeDefault) + host_funcs_.invokeDefault = overrides->invokeDefault; + + if (overrides->evaluate) + host_funcs_.evaluate = overrides->evaluate; + + if (overrides->getproperty) + host_funcs_.getproperty = overrides->getproperty; + + if (overrides->setproperty) + host_funcs_.setproperty = overrides->setproperty; + + if (overrides->removeproperty) + host_funcs_.removeproperty = overrides->removeproperty; + + if (overrides->hasproperty) + host_funcs_.hasproperty = overrides->hasproperty; + + if (overrides->hasmethod) + host_funcs_.hasmethod = overrides->hasmethod; + + if (overrides->setexception) + host_funcs_.setexception = overrides->setexception; + + if (overrides->enumerate) + host_funcs_.enumerate = overrides->enumerate; +} + +void PluginHost::InvalidateRect(NPP id, NPRect* invalidRect) { + if (!invalidRect) { + NOTREACHED(); + return; + } + + // Invalidates specified drawing area prior to repainting or refreshing a + // windowless plugin + + // Before a windowless plugin can refresh part of its drawing area, it must + // first invalidate it. This function causes the NPP_HandleEvent method to + // pass an update event or a paint message to the plug-in. After calling + // this method, the plug-in recieves a paint message asynchronously. + + // The browser redraws invalid areas of the document and any windowless + // plug-ins at regularly timed intervals. To force a paint message, the + // plug-in can call NPN_ForceRedraw after calling this method. + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + DCHECK(plugin.get() != NULL); + + if (plugin.get() && plugin->webplugin()) { + if (plugin->throttle_invalidate()) { + // We need to track plugin invalidates on a per instance basis. + ThrottledInvalidates plugin_instance_invalidates; + InstanceThrottledInvalidatesMap::iterator invalidate_index = + instance_throttled_invalidates_.find(id); + if (invalidate_index != instance_throttled_invalidates_.end()) { + plugin_instance_invalidates = (*invalidate_index).second; + } + + bool throttle_active = + (plugin_instance_invalidates.throttled_invalidates.size() > 0); + + gfx::Rect rect(invalidRect->left, + invalidRect->top, + invalidRect->right - invalidRect->left, + invalidRect->bottom - invalidRect->top); + + plugin_instance_invalidates.throttled_invalidates.push_back(rect); + + if (!throttle_active) { + // We hold a reference to the plugin instance to avoid race conditions + // due to the instance being released before the OnInvalidateRect + // function is invoked. + plugin->AddRef(); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + throttle_factory_.NewRunnableMethod(&PluginHost::OnInvalidateRect, + id, plugin.get()), + kFlashMessageThrottleDelayMs); + } + instance_throttled_invalidates_[id] = plugin_instance_invalidates; + } else { + gfx::Rect rect(invalidRect->left, + invalidRect->top, + invalidRect->right - invalidRect->left, + invalidRect->bottom - invalidRect->top); + plugin->webplugin()->InvalidateRect(rect); + } + } +} + +bool PluginHost::SetPostData(const char *buf, + uint32 length, + std::vector<std::string>* names, + std::vector<std::string>* values, + std::vector<char>* body) { + // Use a state table to do the parsing. Whitespace must be + // trimmed after the fact if desired. In our case, we actually + // don't care about the whitespace, because we're just going to + // pass this back into another POST. This function strips out the + // "Content-length" header and does not append it to the request. + + // + // This parser takes action only on state changes. + // + // Transition table: + // : \n NULL Other + // 0 GetHeader 1 2 4 0 + // 1 GetValue 1 0 3 1 + // 2 GetData 2 2 3 2 + // 3 DONE + // 4 ERR + // + enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER }; + enum { GETNAME, GETVALUE, GETDATA, DONE, ERR }; + int statemachine[3][4] = { { GETVALUE, GETDATA, ERR, GETNAME }, + { GETVALUE, GETNAME, DONE, GETVALUE }, + { GETDATA, GETDATA, DONE, GETDATA } }; + std::string name, value; + char *ptr = (char*)buf; + char *start = ptr; + int state = GETNAME; // initial state + bool done = false; + bool err = false; + do { + int input; + + // Translate the current character into an input + // for the state table. + switch (*ptr) { + case ':' : + input = INPUT_COLON; + break; + case '\n': + input = INPUT_NEWLINE; + break; + case 0 : + input = INPUT_NULL; + break; + default : + input = INPUT_OTHER; + break; + } + + int newstate = statemachine[state][input]; + + // Take action based on the new state. + if (state != newstate) { + switch (newstate) { + case GETNAME: + // Got a value. + value = std::string(start, ptr - start); + TrimWhitespace(value, TRIM_ALL, &value); + // If the name field is empty, we'll skip this header + // but we won't error out. + if (!name.empty() && name != "content-length") { + names->push_back(name); + values->push_back(value); + } + start = ptr + 1; + break; + case GETVALUE: + // Got a header. + name = StringToLowerASCII(std::string(start, ptr - start)); + TrimWhitespace(name, TRIM_ALL, &name); + start = ptr + 1; + break; + case GETDATA: + { + // Finished headers, now get body. + start = ptr + 1; + size_t previous_size = body->size(); + size_t new_body_size = length - static_cast<int>(start - buf); + body->resize(previous_size + new_body_size); + if (!body->empty()) + memcpy(&body->front() + previous_size, start, new_body_size); + done = true; + break; + } + case ERR: + // error + err = true; + done = true; + break; + } + } + state = newstate; + ptr++; + } while (!done); + + return !err; +} + +void PluginHost::OnInvalidateRect(NPP id, PluginInstance* instance) { + if (!instance) { + NOTREACHED(); + return; + } + + InstanceThrottledInvalidatesMap::iterator invalidate_index = + instance_throttled_invalidates_.find(id); + if (invalidate_index == instance_throttled_invalidates_.end()) { + NOTREACHED(); + instance->Release(); + return; + } + + ThrottledInvalidates plugin_instance_invalidates = + (*invalidate_index).second; + + if (instance->webplugin()) { + for (unsigned int throttle_index = 0; + throttle_index < + plugin_instance_invalidates.throttled_invalidates.size(); + throttle_index++) { + instance->webplugin()->InvalidateRect( + plugin_instance_invalidates.throttled_invalidates[throttle_index]); + } + } + + instance->Release(); + instance_throttled_invalidates_.erase(invalidate_index); +} + +} // namespace NPAPI + +// Allocates memory from the host's memory space. +void* NPN_MemAlloc(uint32 size) { + scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton(); + if (host != NULL) { + // Note: We must use the same allocator/deallocator + // that is used by the javascript library, as some of the + // JS APIs will pass memory to the plugin which the plugin + // will attempt to free. + return malloc(size); + } + return NULL; +} + +// Deallocates memory from the host's memory space +void NPN_MemFree(void* ptr) { + scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton(); + if (host != NULL) { + if (ptr != NULL && ptr != (void*)-1) { + free(ptr); + } + } +} + +// Requests that the host free a specified amount of memory. +uint32 NPN_MemFlush(uint32 size) { + // This is not relevant on Windows; MAC specific + return size; +} + +// This is for dynamic discovery of new plugins. +// Should force a re-scan of the plugins directory to load new ones. +void NPN_ReloadPlugins(NPBool reloadPages) { + // TODO: implement me +} + +// Requests a range of bytes for a seekable stream. +NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) { + // TODO: implement me + return NPERR_GENERIC_ERROR; +} + +static bool IsJavaScriptUrl(const std::string& url) { + return StartsWithASCII(url, "javascript:", false); +} + +// Generic form of GetURL for common code between +// GetURL() and GetURLNotify(). +static NPError GetURLNotify(NPP id, + const char* url, + const char* target, + bool notify, + void* notify_data) { + if (!url) + return NPERR_INVALID_URL; + + bool is_javascript_url = IsJavaScriptUrl(url); + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get()) { + plugin->webplugin()->HandleURLRequest("GET", + is_javascript_url, + target, + 0, + 0, + false, + notify, + url, + notify_data, + plugin->popups_allowed()); + } else { + NOTREACHED(); + return NPERR_GENERIC_ERROR; + } + return NPERR_NO_ERROR; +} + +// Requests creation of a new stream with the contents of the +// specified URL; gets notification of the result. +NPError NPN_GetURLNotify(NPP id, + const char* url, + const char* target, + void* notify_data) { + // This is identical to NPN_GetURL, but after finishing, the + // browser will call NPP_URLNotify to inform the plugin that + // it has completed. + + // According to the NPAPI documentation, if target == _self + // or a parent to _self, the browser should return NPERR_INVALID_PARAM, + // because it can't notify the plugin once deleted. This is + // absolutely false; firefox doesn't do this, and Flash relies on + // being able to use this. + + // Also according to the NPAPI documentation, we should return + // NPERR_INVALID_URL if the url requested is not valid. However, + // this would require that we synchronously start fetching the + // URL. That just isn't practical. As such, there really is + // no way to return this error. From looking at the Firefox + // implementation, it doesn't look like Firefox does this either. + + return GetURLNotify(id, url, target, true, notify_data); +} + +NPError NPN_GetURL(NPP id, const char* url, const char* target) { + // Notes: + // Request from the Plugin to fetch content either for the plugin + // or to be placed into a browser window. + // + // If target == null, the browser fetches content and streams to plugin. + // otherwise, the browser loads content into an existing browser frame. + // If the target is the window/frame containing the plugin, the plugin + // may be destroyed. + // If the target is _blank, a mailto: or news: url open content in a new + // browser window + // If the target is _self, no other instance of the plugin is created. The + // plugin continues to operate in its own window + + return GetURLNotify(id, url, target, false, 0); +} + +// Generic form of PostURL for common code between +// PostURL() and PostURLNotify(). +static NPError PostURLNotify(NPP id, + const char* url, + const char* target, + uint32 len, + const char* buf, + NPBool file, + bool notify, + void* notify_data) { + if (!url) + return NPERR_INVALID_URL; + + if (file) { + // Unfortunately, our WebKit requests can support files which + // contain *only* data. But the files from NPAPI contain + // headers + data! So, we need to read the file, extract + // the headers, write the data back to a new file, and then + // finally we can post the file. TODO: Implement me! + // TODO: implement me + NOTREACHED(); + return NPERR_GENERIC_ERROR; + } + + bool is_javascript_url = IsJavaScriptUrl(url); + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get()) { + // The post data sent by a plugin contains both headers + // and post data. Example: + // Content-type: text/html + // Content-length: 200 + // + // <200 bytes of content here> + // + // Unfortunately, our stream needs these broken apart, + // so we need to parse the data and set headers and data + // separately. + plugin->webplugin()->HandleURLRequest("POST", + is_javascript_url, + target, + len, + buf, + file ? true : false, + notify, + url, + notify_data, + plugin->popups_allowed()); + return NPERR_NO_ERROR; + } else { + NOTREACHED(); + } + return NPERR_GENERIC_ERROR; +} + +NPError NPN_PostURLNotify(NPP id, + const char* url, + const char* target, + uint32 len, + const char* buf, + NPBool file, + void* notify_data) { + return PostURLNotify(id, url, target, len, buf, file, true, notify_data); +} + +NPError NPN_PostURL(NPP id, + const char* url, + const char* target, + uint32 len, + const char* buf, + NPBool file) { + // POSTs data to an URL, either from a temp file or a buffer. + // If file is true, buf contains a temp file (which host will delete after + // completing), and len contains the length of the filename. + // If file is false, buf contains the data to send, and len contains the + // length of the buffer + // + // If target is null, + // server response is returned to the plugin + // If target is _current, _self, or _top, + // server response is written to the plugin window and plugin is unloaded. + // If target is _new or _blank, + // server response is written to a new browser window + // If target is an existing frame, + // server response goes to that frame. + // + // For protocols other than FTP + // file uploads must be line-end converted from \r\n to \n + // + // Note: you cannot specify headers (even a blank line) in a memory buffer, + // use NPN_PostURLNotify + + return PostURLNotify(id, url, target, len, buf, file, false, 0); +} + +NPError NPN_NewStream(NPP id, + NPMIMEType type, + const char* target, + NPStream** stream) { + // Requests creation of a new data stream produced by the plugin, + // consumed by the browser. + // + // Browser should put this stream into a window target. + // + + // TODO: implement me + return NPERR_GENERIC_ERROR; +} + +int32 NPN_Write(NPP id, NPStream* stream, int32 len, void* buffer) { + // Writes data to an existing Plugin-created stream. + + // TODO: implement me + return NPERR_GENERIC_ERROR; +} + +NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) { + // Destroys a stream (could be created by plugin or browser). + // + // Reasons: + // NPRES_DONE - normal completion + // NPRES_USER_BREAK - user terminated + // NPRES_NETWORK_ERROR - network error (all errors fit here?) + // + // + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get() == NULL) { + NOTREACHED(); + return NPERR_GENERIC_ERROR; + } + + return plugin->NPP_DestroyStream(stream, reason); +} + +const char* NPN_UserAgent(NPP id) { + // Flash passes in a null id during the NP_initialize call. We need to + // default to the Mozilla user agent if we don't have an NPP instance or + // else Flash won't request windowless mode. + if (id) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin.get() && !plugin->use_mozilla_user_agent()) + return webkit_glue::GetDefaultUserAgent().c_str(); + } + + static const char *UA = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) Gecko/20061103 Firefox/2.0a1"; + return UA; +} + +void NPN_Status(NPP id, const char* message) { + // Displays a message on the status line of the browser window. + + // TODO: implement me +} + +void NPN_InvalidateRect(NPP id, NPRect *invalidRect) { + scoped_refptr<NPAPI::PluginHost> host = NPAPI::PluginHost::Singleton(); + if (host != NULL) { + host->InvalidateRect(id, invalidRect); + } +} + +void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) { + // Invalidates a specified drawing region prior to repainting + // or refreshing a window-less plugin. + // + // Similar to NPN_InvalidateRect. + + // TODO: implement me +} + +void NPN_ForceRedraw(NPP id) { + // Forces repaint for a windowless plug-in. + // + // Once a value has been invalidated with NPN_InvalidateRect/ + // NPN_InvalidateRegion, ForceRedraw can be used to force a paint message. + // + // The plugin will receive a WM_PAINT message, the lParam of the WM_PAINT + // message holds a pointer to an NPRect that is the bounding box of the + // update area. + // Since the plugin and browser share the same HDC, before drawing, the + // plugin is responsible fro saving the current HDC settings, setting up + // its own environment, drawing, and restoring the HDC to the previous + // settings. The HDC settings must be restored whenever control returns + // back to the browser, either before returning from NPP_HandleEvent or + // before calling a drawing-related netscape method. + // + + // TODO: implement me +} + +NPError NPN_GetValue(NPP id, NPNVariable variable, void *value) { + // Allows the plugin to query the browser for information + // + // Variables: + // NPNVxDisplay (unix only) + // NPNVxtAppContext (unix only) + // NPNVnetscapeWindow (win only) - Gets the native window on which the + // plug-in drawing occurs, returns HWND + // NPNVjavascriptEnabledBool: tells whether Javascript is enabled + // NPNVasdEnabledBool: tells whether SmartUpdate is enabled + // NPNVOfflineBool: tells whether offline-mode is enabled + + NPError rv = NPERR_GENERIC_ERROR; + + switch (variable) { + case NPNVWindowNPObject: + { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject(); + // Return value is expected to be retained, as + // described here: + // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> + if (np_object) { + NPN_RetainObject(np_object); + void **v = (void **)value; + *v = np_object; + rv = NPERR_NO_ERROR; + } else { + NOTREACHED(); + } + break; + } + case NPNVPluginElementNPObject: + { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + NPObject *np_object = plugin->webplugin()->GetPluginElement(); + // Return value is expected to be retained, as + // described here: + // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> + if (np_object) { + NPN_RetainObject(np_object); + void **v = (void **)value; + *v = np_object; + rv = NPERR_NO_ERROR; + } else { + NOTREACHED(); + } + break; + } + case NPNVnetscapeWindow: + { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + HWND handle = plugin->window_handle(); + *((void**)value) = (void*)handle; + rv = NPERR_NO_ERROR; + break; + } + case NPNVjavascriptEnabledBool: + { + // yes, JS is enabled. + *((void**)value) = (void*)1; + rv = NPERR_NO_ERROR; + break; + } + case NPNVserviceManager: + { + NPAPI::PluginInstance* instance = + NPAPI::PluginInstance::GetInitializingInstance(); + if (instance) { + instance->GetServiceManager(reinterpret_cast<void**>(value)); + } else { + NOTREACHED(); + } + + rv = NPERR_NO_ERROR; + break; + } + case default_plugin::kMissingPluginStatusStart + + default_plugin::MISSING_PLUGIN_AVAILABLE: + // fall through + case default_plugin::kMissingPluginStatusStart + + default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD: + { + // This is a hack for the default plugin to send notification to renderer. + // Because we will check if the plugin is default plugin, we don't need + // to worry about future standard change that may conflict with the + // variable definition. + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin->plugin_lib()->plugin_info().file == kDefaultPluginDllName) + plugin->webplugin()->OnMissingPluginStatus( + variable - default_plugin::kMissingPluginStatusStart); + break; + } + default: + { + // TODO: implement me + break; + } + } + return rv; +} + +NPError NPN_SetValue(NPP id, NPPVariable variable, void *value) { + // Allows the plugin to set various modes + + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + switch(variable) + { + case NPPVpluginWindowBool: + { + // Sets windowless mode for display of the plugin + // Note: the documentation at http://developer.mozilla.org/en/docs/NPN_SetValue + // is wrong. When value is NULL, the mode is set to true. This is the same + // way Mozilla works. + bool mode = (value == 0); + plugin->set_windowless(mode); + return NPERR_NO_ERROR; + } + case NPPVpluginTransparentBool: + { + // Sets transparent mode for display of the plugin + // + // Transparent plugins require the browser to paint the background + // before having the plugin paint. By default, windowless plugins + // are transparent. Making a windowless plugin opaque means that + // the plugin does not require the browser to paint the background. + // + bool mode = (value != 0); + plugin->set_transparent(mode); + return NPERR_NO_ERROR; + } + case NPPVjavascriptPushCallerBool: + // Specifies whether you are pushing or popping the JSContext off + // the stack + // TODO: implement me + return NPERR_GENERIC_ERROR; + case NPPVpluginKeepLibraryInMemory: + // Tells browser that plugin dll should live longer than usual. + // TODO: implement me + return NPERR_GENERIC_ERROR; + default: + // TODO: implement me + break; + } + + NOTREACHED(); + return NPERR_GENERIC_ERROR; +} + +void *NPN_GetJavaEnv() { + // TODO: implement me + return NULL; +} + +void *NPN_GetJavaPeer(NPP) { + // TODO: implement me + return NULL; +} + +void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin) { + plugin->PushPopupsEnabledState(enabled); + } +} + +void NPN_PopPopupsEnabledState(NPP id) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin) { + plugin->PopPopupsEnabledState(); + } +} + +void NPN_PluginThreadAsyncCall(NPP id, + void (*func)(void *), + void *userData) { + scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id); + if (plugin) { + plugin->PluginThreadAsyncCall(func, userData); + } +} + +bool NPN_Construct(NPP npp, + NPObject* obj, + const NPVariant *args, + uint32_t argCount, + NPVariant *result) { + NOTREACHED(); + return false; +} + +} // extern "C" diff --git a/webkit/glue/plugins/plugin_host.h b/webkit/glue/plugins/plugin_host.h new file mode 100644 index 0000000..61f03c5 --- /dev/null +++ b/webkit/glue/plugins/plugin_host.h @@ -0,0 +1,107 @@ +// Copyright 2008, 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. + +// TODO: Need mechanism to cleanup the static instance + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__ + +#include <string> +#include <vector> +#include <map> + +#include "base/ref_counted.h" +#include "base/gfx/rect.h" +#include "base/task.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +namespace NPAPI +{ +class PluginInstance; + +// The Plugin Host implements the NPN_xxx functions for NPAPI plugins. +// These are the functions exposed from the Plugin Host for use +// by the Plugin. +// +// The PluginHost is managed as a singleton. This isn't strictly +// necessary, but since the callback functions are all global C +// functions, there is really no point in having per-instance PluginHosts. +class PluginHost : public base::RefCounted<PluginHost> { + public: + // Access the single PluginHost instance. Callers + // must call deref() when finished with the object. + static PluginHost *Singleton(); + virtual ~PluginHost(); + + // The table of functions provided to the plugin. + NPNetscapeFuncs *host_functions() { return &host_funcs_; } + + // Helper function for parsing post headers, and applying attributes + // to the stream. NPAPI post data include headers + data combined. + // This function parses it out and adds it to the stream in a WebKit + // style. + static bool SetPostData(const char *buf, + uint32 length, + std::vector<std::string>* names, + std::vector<std::string>* values, + std::vector<char>* body); + + void PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides); + + // Handles invalidateRect requests for windowless plugins. + void InvalidateRect(NPP id, NPRect* invalidRect); + + private: + PluginHost(); + void InitializeHostFuncs(); + // For certain plugins like flash we need to throttle invalidateRect + // requests as they are made at a high frequency. + void OnInvalidateRect(NPP id, PluginInstance* instance); + + static scoped_refptr<PluginHost> singleton_; + NPNetscapeFuncs host_funcs_; + DISALLOW_EVIL_CONSTRUCTORS(PluginHost); + + // This structure keeps track of individual plugin instance invalidates. + struct ThrottledInvalidates { + std::vector<gfx::Rect> throttled_invalidates; + }; + + // We need to track throttled invalidate rects on a per plugin instance + // basis. + typedef std::map<NPP, ThrottledInvalidates> InstanceThrottledInvalidatesMap; + InstanceThrottledInvalidatesMap instance_throttled_invalidates_; + + ScopedRunnableMethodFactory<PluginHost> throttle_factory_; +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__ diff --git a/webkit/glue/plugins/plugin_instance.cc b/webkit/glue/plugins/plugin_instance.cc new file mode 100644 index 0000000..62e1357 --- /dev/null +++ b/webkit/glue/plugins/plugin_instance.cc @@ -0,0 +1,463 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_instance.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/thread_local_storage.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_data_stream.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/plugins/plugin_stream_url.h" +#include "webkit/glue/plugins/plugin_string_stream.h" +#include "webkit/glue/plugins/mozilla_extensions.h" +#include "net/base/escape.h" + +namespace NPAPI +{ +int PluginInstance::plugin_instance_tls_index_ = ThreadLocalStorage::Alloc(); + +PluginInstance::PluginInstance(PluginLib *plugin, const std::string &mime_type) + : plugin_(plugin), + npp_(0), + host_(PluginHost::Singleton()), + npp_functions_(plugin->functions()), + hwnd_(0), + windowless_(false), + transparent_(true), + mime_type_(mime_type), + webplugin_(0), + use_mozilla_user_agent_(false), + message_loop_(MessageLoop::current()), + load_manually_(false), + throttle_invalidate_(false), + get_notify_data_(NULL), + in_close_streams_(false) { + npp_ = new NPP_t(); + npp_->ndata = 0; + npp_->pdata = 0; + + memset(&zero_padding_, 0, sizeof(zero_padding_)); + DCHECK(message_loop_); +} + +PluginInstance::~PluginInstance() { + CloseStreams(); + + if (npp_ != 0) { + delete npp_; + npp_ = 0; + } + + if (plugin_) + plugin_->CloseInstance(); +} + +PluginStreamUrl *PluginInstance::CreateStream(int resource_id, + const std::string &url, + const std::string &mime_type, + bool notify_needed, + void *notify_data) { + PluginStreamUrl *stream = new PluginStreamUrl( + resource_id, GURL(url), this, notify_needed, notify_data); + + AddStream(stream); + return stream; +} + +void PluginInstance::SendStream(const std::string &url, + bool notify_needed, + void *notify_data) { + if (notify_needed) { + host_->host_functions()->geturlnotify(npp(), url.c_str(), NULL, + notify_data); + } else { + host_->host_functions()->geturl(npp(), url.c_str(), NULL); + } +} + +void PluginInstance::AddStream(PluginStream* stream) { + open_streams_.push_back(stream); +} + +void PluginInstance::RemoveStream(PluginStream* stream) { + if (in_close_streams_) + return; + + std::vector<scoped_refptr<PluginStream> >::iterator stream_index; + for (stream_index = open_streams_.begin(); + stream_index != open_streams_.end(); ++stream_index) { + if (*stream_index == stream) { + open_streams_.erase(stream_index); + break; + } + } +} + +bool PluginInstance::IsValidStream(const NPStream* stream) { + std::vector<scoped_refptr<PluginStream> >::iterator stream_index; + for (stream_index = open_streams_.begin(); + stream_index != open_streams_.end(); ++stream_index) { + if ((*stream_index)->stream() == stream) + return true; + } + + return false; +} + +void PluginInstance::CloseStreams() { + in_close_streams_ = true; + for (unsigned int index = 0; index < open_streams_.size(); ++index) { + // Close all streams on the way down. + open_streams_[index]->Close(NPRES_USER_BREAK); + } + open_streams_.clear(); + in_close_streams_ = false; +} + +bool PluginInstance::HandleEvent(UINT message, WPARAM wParam, LPARAM lParam) { + if (!windowless_) + return false; + + NPEvent windowEvent; + windowEvent.event = message; + windowEvent.lParam = static_cast<uint32>(lParam); + windowEvent.wParam = static_cast<uint32>(wParam); + return NPP_HandleEvent(&windowEvent) != 0; +} + +bool PluginInstance::Start(const GURL& url, + char** const param_names, + char** const param_values, + int param_count, + bool load_manually) { + load_manually_ = load_manually; + instance_url_ = url; + unsigned short mode = load_manually_ ? NP_FULL : NP_EMBED; + npp_->ndata = this; + + NPError err = NPP_New(mode, param_count, + const_cast<char **>(param_names), const_cast<char **>(param_values)); + return err == NPERR_NO_ERROR; +} + +NPObject *PluginInstance::GetPluginScriptableObject() { + NPObject *value; + NPError error = NPP_GetValue(NPPVpluginScriptableNPObject, &value); + if (error != NPERR_NO_ERROR) + return NULL; + return value; +} + +void PluginInstance::SetURLLoadData(const GURL& url, + void* notify_data) { + get_url_ = url; + get_notify_data_ = notify_data; +} + +// WebPluginLoadDelegate methods +void PluginInstance::DidFinishLoadWithReason(NPReason reason) { + if (!get_url_.is_empty()) { + NPP_URLNotify(get_url_.spec().c_str(), reason, get_notify_data_); + } + + get_url_ = GURL(); + get_notify_data_ = NULL; +} + +// NPAPI methods +NPError PluginInstance::NPP_New(unsigned short mode, + short argc, + char *argn[], + char *argv[]) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->newp != 0); + DCHECK(argc >= 0); + + if (npp_functions_->newp != 0) { + return npp_functions_->newp( + (NPMIMEType)mime_type_.c_str(), npp_, mode, argc, argn, argv, NULL); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +void PluginInstance::NPP_Destroy() { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->newp != 0); + + if (npp_functions_->destroy != 0) { + NPSavedData *savedData = 0; + npp_functions_->destroy(npp_, &savedData); + + // TODO: Support savedData. Technically, these need to be + // saved on a per-URL basis, and then only passed + // to new instances of the plugin at the same URL. + // Sounds like a huge security risk. When we do support + // these, we should pass them back to the PluginLib + // to be stored there. + DCHECK(savedData == 0); + } + + // Clean up back references to this instance if any + if (mozilla_extenstions_) { + mozilla_extenstions_->DetachFromInstance(); + mozilla_extenstions_ = NULL; + } +} + +NPError PluginInstance::NPP_SetWindow(NPWindow *window) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->setwindow != 0); + + if (npp_functions_->setwindow != 0) { + return npp_functions_->setwindow(npp_, window); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +NPError PluginInstance::NPP_NewStream(NPMIMEType type, + NPStream *stream, + NPBool seekable, + unsigned short *stype) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->newstream != 0); + if (npp_functions_->newstream != 0) { + return npp_functions_->newstream(npp_, type, stream, seekable, stype); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +NPError PluginInstance::NPP_DestroyStream(NPStream *stream, NPReason reason) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->destroystream != 0); + + if (stream == NULL || (stream->ndata == NULL) || + !IsValidStream(stream)) + return NPERR_INVALID_INSTANCE_ERROR; + + if (npp_functions_->destroystream != 0) { + NPError result = npp_functions_->destroystream(npp_, stream, reason); + stream->ndata = NULL; + return result; + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +int PluginInstance::NPP_WriteReady(NPStream *stream) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->writeready != 0); + if (npp_functions_->writeready != 0) { + return npp_functions_->writeready(npp_, stream); + } + return NULL; +} + +int PluginInstance::NPP_Write(NPStream *stream, + int offset, + int len, + void *buffer) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->write != 0); + if (npp_functions_->write != 0) { + return npp_functions_->write(npp_, stream, offset, len, buffer); + } + return NULL; +} + +void PluginInstance::NPP_StreamAsFile(NPStream *stream, const char *fname) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->asfile != 0); + if (npp_functions_->asfile != 0) { + npp_functions_->asfile(npp_, stream, fname); + } +} + +void PluginInstance::NPP_URLNotify(const char *url, + NPReason reason, + void *notifyData) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->urlnotify != 0); + if (npp_functions_->urlnotify != 0) { + npp_functions_->urlnotify(npp_, url, reason, notifyData); + } +} + +NPError PluginInstance::NPP_GetValue(NPPVariable variable, void *value) { + DCHECK(npp_functions_ != 0); + // getvalue is NULL for Shockwave + if (npp_functions_->getvalue != 0) { + return npp_functions_->getvalue(npp_, variable, value); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +NPError PluginInstance::NPP_SetValue(NPNVariable variable, void *value) { + DCHECK(npp_functions_ != 0); + if (npp_functions_->setvalue != 0) { + return npp_functions_->setvalue(npp_, variable, value); + } + return NPERR_INVALID_FUNCTABLE_ERROR; +} + +short PluginInstance::NPP_HandleEvent(NPEvent *event) { + DCHECK(npp_functions_ != 0); + DCHECK(npp_functions_->event != 0); + if (npp_functions_->event != 0) { + return npp_functions_->event(npp_, (void*)event); + } + return false; +} + +bool PluginInstance::NPP_Print(NPPrint* platform_print) { + DCHECK(npp_functions_ != 0); + if (npp_functions_->print != 0) { + npp_functions_->print(npp_, platform_print); + return true; + } + return false; +} + +void PluginInstance::SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + if (success) { + PluginStringStream *stream = + new PluginStringStream(this, url, notify_needed, + reinterpret_cast<void*>(notify_data)); + AddStream(stream); + stream->SendToPlugin(WideToUTF8(result), "text/html"); + } else { + // NOTE: Sending an empty stream here will crash MacroMedia + // Flash 9. Just send the URL Notify. + if (notify_needed) { + this->NPP_URLNotify(url.c_str(), NPRES_DONE, + reinterpret_cast<void*>(notify_data)); + } + } +} + +void PluginInstance::DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified) { + DCHECK(load_manually_); + std::string response_url = url; + if (response_url.empty()) { + response_url = instance_url_.spec(); + } + + plugin_data_stream_ = new PluginDataStream(this, response_url, mime_type, + headers, expected_length, + last_modified); + AddStream(plugin_data_stream_.get()); +} + +void PluginInstance::DidReceiveManualData(const char* buffer, int length) { + DCHECK(load_manually_); + DCHECK(plugin_data_stream_.get() != NULL); + plugin_data_stream_->SendToPlugin(buffer, length); +} + +void PluginInstance::DidFinishManualLoading() { + DCHECK(load_manually_); + DCHECK(plugin_data_stream_); + plugin_data_stream_->Close(NPRES_DONE); + RemoveStream(plugin_data_stream_.get()); + plugin_data_stream_ = NULL; +} + +void PluginInstance::DidManualLoadFail() { + DCHECK(load_manually_); + DCHECK(plugin_data_stream_); + plugin_data_stream_->Close(NPRES_NETWORK_ERR); + RemoveStream(plugin_data_stream_.get()); + plugin_data_stream_ = NULL; +} + +void PluginInstance::PluginThreadAsyncCall(void (*func)(void *), + void *userData) { + message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &PluginInstance::OnPluginThreadAsyncCall, func, userData)); +} + +void PluginInstance::OnPluginThreadAsyncCall(void (*func)(void *), + void *userData) { + // We are invoking an arbitrary callback provided by a third + // party plugin. It's better to wrap this into an exception + // block to protect us from crashes. + __try { + func(userData); + } __except(EXCEPTION_EXECUTE_HANDLER) { + // Maybe we can disable a crashing plugin. + // But for now, just continue. + } +} + +PluginInstance* PluginInstance::SetInitializingInstance( + PluginInstance* instance) { + PluginInstance* old_instance = + static_cast<PluginInstance*>( + ThreadLocalStorage::Get(plugin_instance_tls_index_)); + ThreadLocalStorage::Set(plugin_instance_tls_index_, instance); + return old_instance; +} + +PluginInstance* PluginInstance::GetInitializingInstance() { + PluginInstance* instance = + static_cast<PluginInstance*>( + ThreadLocalStorage::Get(plugin_instance_tls_index_)); + return instance;} + +NPError PluginInstance::GetServiceManager(void** service_manager) { + if (!mozilla_extenstions_) { + mozilla_extenstions_ = new MozillaExtensionApi(this); + } + + DCHECK(mozilla_extenstions_); + mozilla_extenstions_->QueryInterface(nsIServiceManager::GetIID(), + service_manager); + return NPERR_NO_ERROR; +} + +void PluginInstance::PushPopupsEnabledState(bool enabled) { + popups_enabled_stack_.push(enabled); +} + +void PluginInstance::PopPopupsEnabledState() { + popups_enabled_stack_.pop(); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_instance.h b/webkit/glue/plugins/plugin_instance.h new file mode 100644 index 0000000..5743c24 --- /dev/null +++ b/webkit/glue/plugins/plugin_instance.h @@ -0,0 +1,288 @@ +// Copyright 2008, 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. + +// TODO: Need to deal with NPAPI's NPSavedData. +// I haven't seen plugins use it yet. + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__ + +#include <string> +#include <vector> +#include <stack> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "googleurl/src/gurl.h" +#include "third_party/npapi/bindings/npapi.h" + +class WebPlugin; +class MessageLoop; + +namespace NPAPI +{ +class PluginLib; +class PluginHost; +class PluginStream; +class PluginStreamUrl; +class PluginDataStream; +class MozillaExtensionApi; + +// A PluginInstance is an active, running instance of a Plugin. +// A single plugin may have many PluginInstances. +class PluginInstance : public base::RefCounted<PluginInstance> { + public: + // Create a new instance of a plugin. The PluginInstance + // will hold a reference to the plugin. + PluginInstance(PluginLib *plugin, const std::string &mime_type); + virtual ~PluginInstance(); + + // Activates the instance by calling NPP_New. + // This should be called after our instance is all + // setup from the host side and we are ready to receive + // requests from the plugin. We must not call any + // functions on the plugin instance until start has + // been called. + // + // url: The instance URL. + // param_names: the list of names of attributes passed via the + // element. + // param_values: the list of values corresponding to param_names + // param_count: number of attributes + // load_manually: if true indicates that the plugin data would be passed + // from webkit. if false indicates that the plugin should + // download the data. + // This also controls whether the plugin is instantiated as + // a full page plugin (NP_FULL) or embedded (NP_EMBED) + // + bool Start(const GURL& url, + char** const param_names, + char** const param_values, + int param_count, + bool load_manually); + + // NPAPI's instance identifier for this instance + NPP npp() { return npp_; } + + // Get/Set for the instance's HWND. + HWND window_handle() { return hwnd_; } + void set_window_handle(HWND value) { hwnd_ = value; } + + // Get/Set whether this instance is in Windowless mode. + // Default is false. + bool windowless() { return windowless_; } + void set_windowless(bool value) { windowless_ = value; } + + // Get/Set whether this instance is transparent. + // This only applies to windowless plugins. Transparent + // plugins require that webkit paint the background. + // Default is true. + bool transparent() { return transparent_; } + void set_transparent(bool value) { transparent_ = value; } + + // Get/Set the WebPlugin associated with this instance + WebPlugin* webplugin() { return webplugin_; } + void set_web_plugin(WebPlugin* webplugin) { webplugin_ = webplugin; } + + // Get the mimeType for this plugin stream + const std::string &mime_type() { return mime_type_; } + + NPAPI::PluginLib* plugin_lib() { return plugin_; } + + // Handles a windows native message which this PluginInstance should deal + // with. Returns true if the event is handled, false otherwise. + bool HandleEvent(UINT message, WPARAM wParam, LPARAM lParam); + + // Creates a stream for sending an URL. If notify_needed + // is true, it will send a notification to the plugin + // when the stream is complete; otherwise it will not. + // Set object_url to true if the load is for the object tag's + // url, or false if it's for a url that the plugin + // fetched through NPN_GetUrl[Notify]. + PluginStreamUrl *CreateStream(int resource_id, + const std::string &url, + const std::string &mime_type, + bool notify_needed, + void *notify_data); + + // Convenience function for sending a stream from a URL to this instance. + // URL can be a relative or a fully qualified url. + void SendStream(const std::string& url, bool notify_needed, + void* notify_data); + // For each instance, we track all streams. When the + // instance closes, all remaining streams are also + // closed. All streams associated with this instance + // should call AddStream so that they can be cleaned + // up when the instance shuts down. + void AddStream(PluginStream* stream); + + // This is called when a stream is closed. We remove the stream from the + // list, which releases the reference maintained to the stream. + void RemoveStream(PluginStream* stream); + + // Closes all open streams on this instance. + void CloseStreams(); + + // Have the plugin create it's script object. + NPObject *GetPluginScriptableObject(); + + // WebViewDelegate methods that we implement. This is for handling + // callbacks during getURLNotify. + virtual void DidFinishLoadWithReason(NPReason reason); + + // Helper method to set some persistent data for getURLNotify since + // resource fetches happen async. + void SetURLLoadData(const GURL& url, void* notify_data); + + // If true, send the Mozilla user agent instead of Chrome's to the plugin. + bool use_mozilla_user_agent() { return use_mozilla_user_agent_; } + void set_use_mozilla_user_agent() { use_mozilla_user_agent_ = true; } + + bool throttle_invalidate() const { return throttle_invalidate_; } + void set_throttle_invalidate(bool throttle_invalidate) { + throttle_invalidate_ = throttle_invalidate; + } + + // Helper that implements NPN_PluginThreadAsyncCall semantics + void PluginThreadAsyncCall(void (*func)(void *), + void *userData); + + // + // NPAPI methods for calling the Plugin Instance + // + NPError NPP_New(unsigned short, short, char *[], char *[]); + NPError NPP_SetWindow(NPWindow *); + NPError NPP_NewStream(NPMIMEType, NPStream *, NPBool, unsigned short *); + NPError NPP_DestroyStream(NPStream *, NPReason); + int NPP_WriteReady(NPStream *); + int NPP_Write(NPStream *, int, int, void *); + void NPP_StreamAsFile(NPStream *, const char *); + void NPP_URLNotify(const char *, NPReason, void *); + NPError NPP_GetValue(NPPVariable, void *); + NPError NPP_SetValue(NPNVariable, void *); + short NPP_HandleEvent(NPEvent *); + void NPP_Destroy(); + bool NPP_Print(NPPrint* platform_print); + + void SendJavaScriptStream(const std::string& url, const std::wstring& result, + bool success, bool notify_needed, int notify_data); + + void DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified); + void DidReceiveManualData(const char* buffer, int length); + void DidFinishManualLoading(); + void DidManualLoadFail(); + + NPError GetServiceManager(void** service_manager); + + static PluginInstance* SetInitializingInstance(PluginInstance* instance); + static PluginInstance* GetInitializingInstance(); + + void PushPopupsEnabledState(bool enabled); + void PopPopupsEnabledState(); + + bool popups_allowed() const { + return popups_enabled_stack_.empty() ? false : popups_enabled_stack_.top(); + } + + private: + void OnPluginThreadAsyncCall(void (*func)(void *), + void *userData); + bool IsValidStream(const NPStream* stream); + + // This is a hack to get the real player plugin to work with chrome + // The real player plugin dll(nppl3260) when loaded by firefox is loaded via + // the NS COM API which is analogous to win32 COM. So the NPAPI functions in + // the plugin are invoked via an interface by firefox. The plugin instance + // handle which is passed to every NPAPI method is owned by the real player + // plugin, i.e. it expects the ndata member to point to a structure which + // it knows about. Eventually it dereferences this structure and compares + // a member variable at offset 0x24(Version 6.0.11.2888) /2D (Version + // 6.0.11.3088) with 0 and on failing this check, takes a different code + // path which causes a crash. Safari and Opera work with version 6.0.11.2888 + // by chance as their ndata structure contains a 0 at the location which real + // player checks:(. They crash with version 6.0.11.3088 as well. The + // following member just adds a 96 byte padding to our PluginInstance class + // which is passed in the ndata member. This magic number works correctly on + // Vista with UAC on or off :(. + // NOTE: Please dont change the ordering of the member variables + // New members should be added after this padding array. + // TODO(iyengar) : Disassemble the Realplayer ndata structure and look into + // the possiblity of conforming to it (http://b/issue?id=936667). We + // could also log a bug with Real, which would save the effort. + uint8 zero_padding_[96]; + scoped_refptr<NPAPI::PluginLib> plugin_; + NPP npp_; + scoped_refptr<PluginHost> host_; + NPPluginFuncs* npp_functions_; + std::vector<scoped_refptr<PluginStream> > open_streams_; + HWND hwnd_; + bool windowless_; + bool transparent_; + WebPlugin* webplugin_; + std::string mime_type_; + GURL get_url_; + void* get_notify_data_; + bool use_mozilla_user_agent_; + scoped_refptr<MozillaExtensionApi> mozilla_extenstions_; + MessageLoop* message_loop_; + // Using TLS to store PluginInstance object during its creation. + // We need to pass this instance to the service manager + // (MozillaExtensionApi) created as a result of NPN_GetValue + // in the context of NP_Initialize. + static int plugin_instance_tls_index_; + scoped_refptr<PluginDataStream> plugin_data_stream_; + GURL instance_url_; + + // This flag if true indicates that the plugin data would be passed from + // webkit. if false indicates that the plugin should download the data. + bool load_manually_; + + // This flag indicates if the NPN_InvalidateRect calls made by the + // plugin need to be throttled. + bool throttle_invalidate_; + + // Stack indicating if popups are to be enabled for the outgoing + // NPN_GetURL/NPN_GetURLNotify calls. + std::stack<bool> popups_enabled_stack_; + + // True if in CloseStreams(). + bool in_close_streams_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginInstance); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
\ No newline at end of file diff --git a/webkit/glue/plugins/plugin_lib.cc b/webkit/glue/plugins/plugin_lib.cc new file mode 100644 index 0000000..bf36152 --- /dev/null +++ b/webkit/glue/plugins/plugin_lib.cc @@ -0,0 +1,433 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_lib.h" + +#include "base/file_util.h" +#include "base/file_version_info.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/stats_counters.h" +#include "base/string_util.h" +#include "base/task.h" +#include "webkit/activex_shim/npp_impl.h" +#include "webkit/default_plugin/plugin_main.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "net/base/mime_util.h" + + +namespace NPAPI +{ + +const wchar_t kPluginLibrariesLoadedCounter[] = L"PluginLibrariesLoaded"; +const wchar_t kPluginInstancesActiveCounter[] = L"PluginInstancesActive"; + +PluginLib::PluginMap* PluginLib::loaded_libs_; + +PluginLib* PluginLib::CreatePluginLib(const std::wstring& filename) { + // We can only have one PluginLib object per plugin as it controls the per + // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep + // a (non-ref counted) map of PluginLib objects. + if (!loaded_libs_) + loaded_libs_ = new PluginMap(); + + PluginMap::const_iterator iter = loaded_libs_->find(filename); + if (iter != loaded_libs_->end()) + return iter->second; + + static const InternalPluginInfo activex_shim_info = { + {kActiveXShimFileName, + L"ActiveX Plug-in", + L"ActiveX Plug-in provides a shim to support ActiveX controls", + L"1, 0, 0, 1", + L"application/x-oleobject|application/oleobject|" + L"application/x-ms-wmp|application/asx|video/x-ms-asf-plugin|" + L"application/x-mplayer2|video/x-ms-asf|video/x-ms-wm|audio/x-ms-wma|" + L"audio/x-ms-wax|video/x-ms-wmv|video/x-ms-wvx", + L"*|*|*|*|*|*|asf,asx,*|wm,*|wma,*|wax,*|wmv,*|wvx,*", + L"" + }, + activex_shim::ActiveX_Shim_NP_GetEntryPoints, + activex_shim::ActiveX_Shim_NP_Initialize, + activex_shim::ActiveX_Shim_NP_Shutdown + }; + + static const InternalPluginInfo default_null_plugin_info = { + {kDefaultPluginDllName, + L"Default Plug-in", + L"Provides functionality for installing third-party plug-ins", + L"1, 0, 0, 1", + L"*", + L"", + L"" + }, + default_plugin::NP_GetEntryPoints, + default_plugin::NP_Initialize, + default_plugin::NP_Shutdown + }; + + WebPluginInfo* info = NULL; + const InternalPluginInfo* internal_plugin_info = NULL; + if (filename == activex_shim_info.version_info.file_name) { + info = CreateWebPluginInfo(activex_shim_info.version_info); + internal_plugin_info = &activex_shim_info; + } else if (filename == default_null_plugin_info.version_info.file_name) { + info = CreateWebPluginInfo(default_null_plugin_info.version_info); + internal_plugin_info = &default_null_plugin_info; + } else { + info = ReadWebPluginInfo(filename); + if (!info) { + DLOG(INFO) << "This file isn't a valid NPAPI plugin: " << filename; + return NULL; + } + } + + return new PluginLib(info, internal_plugin_info); +} + +void PluginLib::UnloadAllPlugins() { + if (loaded_libs_) { + PluginMap::iterator lib_index; + for (lib_index = loaded_libs_->begin(); lib_index != loaded_libs_->end(); + ++lib_index) { + lib_index->second->Unload(); + } + delete loaded_libs_; + loaded_libs_ = NULL; + } +} + +void PluginLib::ShutdownAllPlugins() { + if (loaded_libs_) { + PluginMap::iterator lib_index; + for (lib_index = loaded_libs_->begin(); lib_index != loaded_libs_->end(); + ++lib_index) { + lib_index->second->Shutdown(); + } + } +} + +PluginLib::PluginLib(WebPluginInfo* info, + const InternalPluginInfo* internal_plugin_info) + : web_plugin_info_(info), + module_(0), + initialized_(false), + saved_data_(0), + instance_count_(0) { + StatsCounter(kPluginLibrariesLoadedCounter).Increment(); + memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); + + (*loaded_libs_)[info->file] = this; + if (internal_plugin_info) { + internal_ = true; + NP_Initialize_ = internal_plugin_info->np_initialize; + NP_GetEntryPoints_ = internal_plugin_info->np_getentrypoints; + NP_Shutdown_ = internal_plugin_info->np_shutdown; + } else { + internal_ = false; + } +} + +PluginLib::~PluginLib() { + StatsCounter(kPluginLibrariesLoadedCounter).Decrement(); + if (saved_data_ != 0) { + // TODO - delete the savedData object here + } +} + +NPPluginFuncs *PluginLib::functions() { + return &plugin_funcs_; +} + +bool PluginLib::SupportsType(const std::string &mime_type, + bool allow_wildcard) { + // Webkit will ask for a plugin to handle empty mime types. + if (mime_type.empty()) + return false; + + for (size_t i = 0; i < web_plugin_info_->mime_types.size(); ++i) { + const WebPluginMimeType& mime_info = web_plugin_info_->mime_types[i]; + if (mime_util::MatchesMimeType(mime_info.mime_type, mime_type)) { + if (!allow_wildcard && (mime_info.mime_type == "*")) { + continue; + } + return true; + } + } + return false; +} + +NPError PluginLib::NP_Initialize() { + if (initialized_) + return NPERR_NO_ERROR; + + if (!Load()) + return NPERR_MODULE_LOAD_FAILED_ERROR; + + PluginHost *host = PluginHost::Singleton(); + if (host == 0) + return NPERR_GENERIC_ERROR; + + NPError rv = NP_Initialize_(host->host_functions()); + initialized_ = (rv == NPERR_NO_ERROR); + return rv; +} + +void PluginLib::NP_Shutdown(void) { + DCHECK(initialized_); + NP_Shutdown_(); +} + +PluginInstance *PluginLib::CreateInstance(const std::string &mime_type) { + PluginInstance *new_instance = new PluginInstance(this, mime_type); + instance_count_++; + StatsCounter(kPluginInstancesActiveCounter).Increment(); + DCHECK(new_instance != 0); + return new_instance; +} + +void PluginLib::CloseInstance() { + StatsCounter(kPluginInstancesActiveCounter).Decrement(); + instance_count_--; + // If a plugin is running in its own process it will get unloaded on process + // shutdown. + if ((instance_count_ == 0) && + webkit_glue::IsPluginRunningInRendererProcess()) { + Unload(); + loaded_libs_->erase(web_plugin_info_->file); + if (loaded_libs_->empty()) { + delete loaded_libs_; + loaded_libs_ = NULL; + } + } +} + +bool PluginLib::Load() { + bool rv = false; + HMODULE module = 0; + + if (!internal_) { + if (module_ != 0) + return rv; + + module = LoadPluginHelper(web_plugin_info_->file); + if (module == 0) + return rv; + + rv = true; // assume success now + + NP_Initialize_ = (NP_InitializeFunc)GetProcAddress( + module, "NP_Initialize"); + if (NP_Initialize_ == 0) + rv = false; + + NP_GetEntryPoints_ = (NP_GetEntryPointsFunc)GetProcAddress( + module, "NP_GetEntryPoints"); + if (NP_GetEntryPoints_ == 0) + rv = false; + + NP_Shutdown_ = (NP_ShutdownFunc)GetProcAddress( + module, "NP_Shutdown"); + if (NP_Shutdown_ == 0) + rv = false; + } else { + rv = true; + } + + if (rv) { + plugin_funcs_.size = sizeof(plugin_funcs_); + plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + if (NP_GetEntryPoints_(&plugin_funcs_) != NPERR_NO_ERROR) + rv = false; + } + + if (!internal_) { + if (rv) + module_ = module; + else + FreeLibrary(module); + } + + return rv; +} + +HMODULE PluginLib::LoadPluginHelper(const std::wstring plugin_file) { + // Switch the current directory to the plugin directory as the plugin + // may have dependencies on dlls in this directory. + bool restore_directory = false; + std::wstring current_directory; + if (PathService::Get(base::DIR_CURRENT, ¤t_directory)) { + std::wstring plugin_path = file_util::GetDirectoryFromPath( + plugin_file); + if (!plugin_path.empty()) { + PathService::SetCurrentDirectory(plugin_path); + restore_directory = true; + } + } + + HMODULE module = LoadLibrary(plugin_file.c_str()); + if (restore_directory) + PathService::SetCurrentDirectory(current_directory); + + return module; +} + +// This class implements delayed NP_Shutdown and FreeLibrary on the plugin dll. +class FreePluginLibraryTask : public Task { + public: + FreePluginLibraryTask(HMODULE module, NP_ShutdownFunc shutdown_func) + : module_(module), + NP_Shutdown_(shutdown_func) { + } + + ~FreePluginLibraryTask() {} + + void Run() { + if (NP_Shutdown_) + NP_Shutdown_(); + + if (module_) { + FreeLibrary(module_); + module_ = NULL; + } + } + + private: + HMODULE module_; + NP_ShutdownFunc NP_Shutdown_; + DISALLOW_EVIL_CONSTRUCTORS(FreePluginLibraryTask); +}; + +void PluginLib::Unload() { + if (!internal_ && module_) { + // In case of single process mode, a plugin can delete itself + // by executing a script. So delay the unloading of the DLL + // so that the plugin will have a chance to unwind. + bool defer_unload = webkit_glue::IsPluginRunningInRendererProcess(); + +#if USE(JAVASCRIPTCORE_BINDINGS) + // The plugin NPAPI instances may still be around. Delay the + // NP_Shutdown and FreeLibrary calls at least till the next + // peek message. + defer_unload = true; +#endif + + if (defer_unload) { + FreePluginLibraryTask* free_library_task = + new FreePluginLibraryTask(module_, NP_Shutdown_); + MessageLoop::current()->PostTask(FROM_HERE, free_library_task); + } else { + Shutdown(); + FreeLibrary(module_); + } + + module_ = 0; + } +} + +void PluginLib::Shutdown() { + if (initialized_ && !internal_) { + NP_Shutdown(); + initialized_ = false; + } +} + +WebPluginInfo* PluginLib::CreateWebPluginInfo(const PluginVersionInfo& pvi) { + std::vector<std::string> mime_types, file_extensions; + std::vector<std::wstring> descriptions; + SplitString(WideToNativeMB(pvi.mime_types), '|', &mime_types); + SplitString(WideToNativeMB(pvi.file_extents), '|', &file_extensions); + SplitString(pvi.file_open_names, '|', &descriptions); + + if (mime_types.empty()) + return NULL; + + WebPluginInfo *info = new WebPluginInfo(); + info->name = pvi.product_name; + info->desc = pvi.file_description; + info->version = pvi.file_version; + info->file = StringToLowerASCII(pvi.file_name); + + for (size_t i = 0; i < mime_types.size(); ++i) { + WebPluginMimeType mime_type; + mime_type.mime_type = StringToLowerASCII(mime_types[i]); + if (file_extensions.size() > i) + SplitString(file_extensions[i], ',', &mime_type.file_extensions); + + if (descriptions.size() > i) { + mime_type.description = descriptions[i]; + + // Remove the extension list from the description. + size_t ext = mime_type.description.find(L"(*"); + if (ext != std::wstring::npos) { + if (ext > 1 && mime_type.description[ext -1] == ' ') + ext--; + + mime_type.description.erase(ext); + } + } + + info->mime_types.push_back(mime_type); + } + + return info; +} + +WebPluginInfo* PluginLib::ReadWebPluginInfo(const std::wstring &filename) { + // On windows, the way we get the mime types for the library is + // to check the version information in the DLL itself. This + // will be a string of the format: <type1>|<type2>|<type3>|... + // For example: + // video/quicktime|audio/aiff|image/jpeg + scoped_ptr<FileVersionInfo> version_info( + FileVersionInfo::CreateFileVersionInfo(filename)); + if (!version_info.get()) + return NULL; + + PluginVersionInfo pvi; + version_info->GetValue(L"MIMEType", &pvi.mime_types); + version_info->GetValue(L"FileExtents", &pvi.file_extents); + version_info->GetValue(L"FileOpenName", &pvi.file_open_names); + pvi.product_name = version_info->product_name(); + pvi.file_description = version_info->file_description(); + pvi.file_version = version_info->file_version(); + pvi.file_name = filename; + + return CreateWebPluginInfo(pvi); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_lib.h b/webkit/glue/plugins/plugin_lib.h new file mode 100644 index 0000000..798b2ea --- /dev/null +++ b/webkit/glue/plugins/plugin_lib.h @@ -0,0 +1,163 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGIN_PLUGIN_LIB_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_LIB_H__ + +#include <hash_map> +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +struct WebPluginInfo; + +namespace NPAPI +{ + +class PluginInstance; + +// This struct fully describes a plugin. For dll plugins, it's read in from +// the version info of the dll; For internal plugins, it's predefined. +struct PluginVersionInfo { + std::wstring file_name; + std::wstring product_name; + std::wstring file_description; + std::wstring file_version; + std::wstring mime_types; + std::wstring file_extents; + std::wstring file_open_names; +}; + +// This struct contains information of an internal plugin and addresses of +// entry functions. +struct InternalPluginInfo { + PluginVersionInfo version_info; + NP_GetEntryPointsFunc np_getentrypoints; + NP_InitializeFunc np_initialize; + NP_ShutdownFunc np_shutdown; +}; + +// A PluginLib is a single NPAPI Plugin Library, and is the lifecycle +// manager for new PluginInstances. +class PluginLib : public base::RefCounted<PluginLib> { + public: + virtual ~PluginLib(); + static PluginLib* CreatePluginLib(const std::wstring& filename); + + // Unloads all the loaded plugin dlls and cleans up the plugin map. + static void UnloadAllPlugins(); + + // Shuts down all loaded plugin instances. + static void ShutdownAllPlugins(); + + // Get the Plugin's function pointer table. + NPPluginFuncs *functions(); + + // Returns true if this Plugin supports a given mime-type. + // mime_type should be all lower case. + bool SupportsType(const std::string &mime_type, bool allow_wildcard); + + // Creates a new instance of this plugin. + PluginInstance *CreateInstance(const std::string &mime_type); + + // Called by the instance when the instance is tearing down. + void CloseInstance(); + + // Gets information about this plugin and the mime types that it + // supports. + const WebPluginInfo& plugin_info() { return *web_plugin_info_; } + + // + // NPAPI functions + // + + // NPAPI method to initialize a Plugin. + // Initialize can be safely called multiple times + NPError NP_Initialize(); + + // NPAPI method to shutdown a Plugin. + void NP_Shutdown(void); + + // Helper function to load a plugin. + // Returns the module handle on success. + static HMODULE LoadPluginHelper(const std::wstring plugin_file); + + int instance_count() const { return instance_count_; } + + private: + // Creates a new PluginLib. The WebPluginInfo object is owned by this + // object. If internal_plugin_info is not NULL, this Lib is an internal + // plugin thus doesn't need to load dll. + PluginLib(WebPluginInfo* info, + const InternalPluginInfo* internal_plugin_info); + + // Attempts to load the plugin from the DLL. + // Returns true if it is a legitimate plugin, false otherwise + bool Load(); + + // Unloading the plugin DLL. + void Unload(); + + // Shutdown the plugin DLL. + void Shutdown(); + + // Returns a WebPluginInfo structure given a plugin's path. Returns NULL if + // the dll couldn't be found, or if it's not a plugin. + static WebPluginInfo* ReadWebPluginInfo(const std::wstring &filename); + // Creates WebPluginInfo structure based on read in or built in + // PluginVersionInfo. + static WebPluginInfo* CreateWebPluginInfo(const PluginVersionInfo& info); + + bool internal_; // Whether this an internal plugin. + scoped_ptr<WebPluginInfo> web_plugin_info_; // supported mime types, description + HMODULE module_; // the opened DLL handle + NPPluginFuncs plugin_funcs_; // the struct of plugin side functions + bool initialized_; // is the plugin initialized + NPSavedData *saved_data_; // persisted plugin info for NPAPI + int instance_count_; // count of plugins in use + + // A map of all the insantiated plugins. + typedef stdext::hash_map<std::wstring, scoped_refptr<PluginLib> > PluginMap; + static PluginMap* loaded_libs_; + + // C-style function pointers + NP_InitializeFunc NP_Initialize_; + NP_GetEntryPointsFunc NP_GetEntryPoints_; + NP_ShutdownFunc NP_Shutdown_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginLib); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_LIB_H__ diff --git a/webkit/glue/plugins/plugin_list.cc b/webkit/glue/plugins/plugin_list.cc new file mode 100644 index 0000000..8c8deef --- /dev/null +++ b/webkit/glue/plugins/plugin_list.cc @@ -0,0 +1,500 @@ +// Copyright 2008, 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 <algorithm> +#include <tchar.h> + +#include "webkit/glue/plugins/plugin_list.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/registry.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/time.h" +#include "webkit/activex_shim/activex_shared.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI +{ + +scoped_refptr<PluginList> PluginList::singleton_; + +static const TCHAR kRegistryApps[] = + _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"); +static const TCHAR kRegistryFirefox[] = _T("firefox.exe"); +static const TCHAR kRegistryAcrobat[] = _T("AcroRd32.exe"); +static const TCHAR kRegistryWindowsMedia[] = _T("wmplayer.exe"); +static const TCHAR kRegistryQuickTime[] = _T("QuickTimePlayer.exe"); +static const TCHAR kRegistryPath[] = _T("Path"); +static const TCHAR kRegistryMozillaPlugins[] = _T("SOFTWARE\\MozillaPlugins"); +static const TCHAR kRegistryFirefoxInstalled[] = + _T("SOFTWARE\\Mozilla\\Mozilla Firefox"); +static const TCHAR kMozillaActiveXPlugin[] = _T("npmozax.dll"); +static const TCHAR kNewWMPPlugin[] = _T("np-mswmp.dll"); +static const TCHAR kOldWMPPlugin[] = _T("npdsplay.dll"); +static const TCHAR kRegistryJava[] = + _T("Software\\JavaSoft\\Java Runtime Environment"); +static const TCHAR kRegistryBrowserJavaVersion[] = _T("BrowserJavaVersion"); +static const TCHAR kRegistryCurrentJavaVersion[] = _T("CurrentVersion"); +static const TCHAR kRegistryJavaHome[] = _T("JavaHome"); + +// Extra registry paths to search. +static std::vector<std::wstring>* extra_plugin_paths_ = NULL; + +PluginList* PluginList::Singleton() { + if (singleton_.get() == NULL) { + singleton_ = new PluginList(); + singleton_->LoadPlugins(false); + } + + return singleton_; +} + +void PluginList::AddExtraPluginPath(const std::wstring& plugin_path) { + DCHECK(!singleton_.get() || !singleton_->plugins_loaded_); + + if (!extra_plugin_paths_) + extra_plugin_paths_ = new std::vector<std::wstring>; + extra_plugin_paths_->push_back(plugin_path); +} + +PluginList::PluginList() : + plugins_loaded_(false) { + CommandLine command_line; + dont_load_new_wmp_ = command_line.HasSwitch(kUseOldWMPPluginSwitch); + use_internal_activex_shim_ = + !command_line.HasSwitch(kNoNativeActiveXShimSwitch); +} + +PluginList::~PluginList() { + plugins_.clear(); +} + +void PluginList::LoadPlugins(bool refresh) { + if (plugins_loaded_ && !refresh) + return; + + plugins_.clear(); + plugins_loaded_ = true; + + TimeTicks start_time = TimeTicks::Now(); + + LoadInternalPlugins(); + + // Load any plugins listed in the registry + if (extra_plugin_paths_) { + for (size_t i = 0; i < extra_plugin_paths_->size(); ++i) { + LoadPlugin((*extra_plugin_paths_)[i]); + } + } + + // Load from the application-specific area + LoadPlugins(GetPluginAppDirectory()); + + // Load from the executable area + LoadPlugins(GetPluginExeDirectory()); + + // Load Java + LoadJavaPlugin(); + + // Load firefox plugins too. This is mainly to try to locate + // a pre-installed Flash player. + LoadFirefoxPlugins(); + + // Firefox hard-codes the paths of some popular plugins to ensure that + // the plugins are found. We are going to copy this as well. + LoadAcrobatPlugins(); + LoadQuicktimePlugins(); + LoadWindowsMediaPlugins(); + + if (webkit_glue::IsDefaultPluginEnabled()) { + scoped_refptr<PluginLib> default_plugin = PluginLib::CreatePluginLib( + kDefaultPluginDllName); + plugins_.push_back(default_plugin); + } + + TimeTicks end_time = TimeTicks::Now(); + TimeDelta elapsed = end_time - start_time; + DLOG(INFO) << "Loaded plugin list in " << elapsed.InMilliseconds() << " ms."; +} + +void PluginList::LoadPlugins(const std::wstring &path) { + WIN32_FIND_DATA find_file_data; + HANDLE find_handle; + + std::wstring dir = path; + // FindFirstFile requires that you specify a wildcard for directories. + dir.append(L"\\NP*.DLL"); + + find_handle = FindFirstFile(dir.c_str(), &find_file_data); + if (find_handle == INVALID_HANDLE_VALUE) + return; + + do { + if (!(find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + std::wstring filename = path; + filename.append(L"\\"); + filename.append(find_file_data.cFileName); + + LoadPlugin(filename); + } + } while (FindNextFile(find_handle, &find_file_data) != 0); + + DCHECK(GetLastError() == ERROR_NO_MORE_FILES); + FindClose(find_handle); +} + +void PluginList::LoadPlugin(const std::wstring &path) { + std::wstring path_lc = StringToLowerASCII(path); + if (!ShouldLoadPlugin(file_util::GetFilenameFromPath(path_lc))) + return; + + scoped_refptr<PluginLib> new_plugin = PluginLib::CreatePluginLib(path_lc); + if (!new_plugin.get()) + return; + + const WebPluginInfo& plugin_info = new_plugin->plugin_info(); + for (size_t i = 0; i < plugin_info.mime_types.size(); ++i) { + // TODO: don't load global handlers for now. + // WebKit hands to the Plugin before it tries + // to handle mimeTypes on its own. + const std::string &mime_type = plugin_info.mime_types[i].mime_type; + if (mime_type == "*" ) { +#ifndef NDEBUG + // Make an exception for NPSPY. + if (plugin_info.file.find(L"npspy.dll") != std::wstring::npos) { + // Put it at the beginning so it's used before the real plugin. + plugins_.insert(plugins_.begin(), new_plugin.get()); + } +#endif + continue; + } + + if (!SupportsType(mime_type)) + plugins_.push_back(new_plugin); + } +} + +bool PluginList::ShouldLoadPlugin(const std::wstring& filename) { + // Depends on XPCOM. + if (filename == kMozillaActiveXPlugin) + return false; + + // We will use activex shim to handle embeded wmp media. + if (use_internal_activex_shim_) { + if (filename == kNewWMPPlugin || filename == kOldWMPPlugin) + return false; + } else { + // If both the new and old WMP plugins exist, only load the new one. + if (filename == kNewWMPPlugin) { + if (dont_load_new_wmp_) + return false; + + int old_plugin = FindPluginFile(kOldWMPPlugin); + if (old_plugin != -1) + plugins_.erase(plugins_.begin() + old_plugin); + } else if (filename == kOldWMPPlugin) { + if (FindPluginFile(kNewWMPPlugin) != -1) + return false; + } + } + + return true; +} + +void PluginList::LoadInternalPlugins() { + if (use_internal_activex_shim_) { + scoped_refptr<PluginLib> new_plugin = PluginLib::CreatePluginLib( + kActiveXShimFileName); + plugins_.push_back(new_plugin); + } +} + +int PluginList::FindPluginFile(const std::wstring& filename) { + for (size_t i = 0; i < plugins_.size(); ++i) { + if (file_util::GetFilenameFromPath(plugins_[i]->plugin_info().file) == + filename) { + return static_cast<int>(i); + } + } + + return -1; +} + +PluginLib* PluginList::FindPlugin(const std::string& mime_type, + const std::string& clsid, + bool allow_wildcard) { + DCHECK(mime_type == StringToLowerASCII(mime_type)); + + for (size_t idx = 0; idx < plugins_.size(); ++idx) { + if (plugins_[idx]->SupportsType(mime_type, allow_wildcard)) { + if (!clsid.empty() && + plugins_[idx]->plugin_info().file == kActiveXShimFileName) { + // Special handling for ActiveX shim. If ActiveX is not installed, we + // should use the default plugin to show the installation UI. + if (!activex_shim::IsActiveXInstalled(clsid)) + continue; + } + return plugins_[idx]; + } + } + + return NULL; +} + +PluginLib* PluginList::FindPlugin(const GURL &url, std::string* actual_mime_type) { + std::wstring path = NativeMBToWide(url.path()); + std::wstring extension_wide = file_util::GetFileExtensionFromPath(path); + if (extension_wide.empty()) + return NULL;; + + std::string extension = StringToLowerASCII(WideToNativeMB(extension_wide)); + + for (size_t idx = 0; idx < plugins_.size(); ++idx) { + if (SupportsExtension(plugins_[idx]->plugin_info(), extension, actual_mime_type)) { + return plugins_[idx]; + } + } + + return NULL; +} + +bool PluginList::SupportsType(const std::string &mime_type) { + DCHECK(mime_type == StringToLowerASCII(mime_type)); + bool allow_wildcard = true; + return (FindPlugin(mime_type, "", allow_wildcard ) != 0); +} + +bool PluginList::SupportsExtension(const WebPluginInfo& info, + const std::string &extension, + std::string* actual_mime_type) { + for (size_t i = 0; i < info.mime_types.size(); ++i) { + const WebPluginMimeType& mime_type = info.mime_types[i]; + for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) { + if (mime_type.file_extensions[j] == extension) { + if (actual_mime_type) + *actual_mime_type = mime_type.mime_type; + return true; + } + } + } + + return false; +} + + +bool PluginList::GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) { + if (refresh) + LoadPlugins(true); + + plugins->resize(plugins_.size()); + for (size_t i = 0; i < plugins->size(); ++i) + (*plugins)[i] = plugins_[i]->plugin_info(); + + return true; +} + +bool PluginList::GetPluginInfo(const GURL& url, + const std::string& mime_type, + const std::string& clsid, + bool allow_wildcard, + WebPluginInfo* info, + std::string* actual_mime_type) { + scoped_refptr<PluginLib> plugin = FindPlugin(mime_type, clsid, + allow_wildcard); + + if (plugin.get() == NULL || + (plugin->plugin_info().file == kDefaultPluginDllName && clsid.empty())) { + scoped_refptr<PluginLib> default_plugin = plugin; + plugin = FindPlugin(url, actual_mime_type); + // url matches may not return the default plugin if no match is found. + if (plugin.get() == NULL && default_plugin.get() != NULL) + plugin = default_plugin; + } + + if (plugin.get() == NULL) + return false; + + *info = plugin->plugin_info(); + return true; +} + +bool PluginList::GetPluginInfoByDllPath(const std::wstring& dll_path, + WebPluginInfo* info) { + for (size_t i = 0; i < plugins_.size(); ++i) { + if (wcsicmp(plugins_[i]->plugin_info().file.c_str(), + dll_path.c_str()) == 0) { + *info = plugins_[i]->plugin_info(); + return true; + } + } + + return false; +} + +void PluginList::Shutdown() { + // TODO +} + +std::wstring PluginList::GetPluginAppDirectory() { + std::wstring app_path; + if (webkit_glue::GetApplicationDirectory(&app_path)) + app_path.append(L"\\plugins"); + + return app_path; +} + +std::wstring PluginList::GetPluginExeDirectory() { + std::wstring exe_path; + if (webkit_glue::GetExeDirectory(&exe_path)) + exe_path.append(L"\\plugins"); + + return exe_path; +} + +// Gets the installed path for a registered app. +static bool GetInstalledPath(const TCHAR* app, std::wstring* out) { + std::wstring reg_path(kRegistryApps); + reg_path.append(L"\\"); + reg_path.append(app); + + RegKey key(HKEY_LOCAL_MACHINE, reg_path.c_str()); + return key.ReadValue(kRegistryPath, out); +} + +// Enumerate through the registry key to find all installed FireFox paths. +// FireFox 3 beta and version 2 can coexist. See bug: 1025003 +static void GetFirefoxInstalledPaths(std::vector<std::wstring>* out) { + RegistryKeyIterator it(HKEY_LOCAL_MACHINE, kRegistryFirefoxInstalled); + for (; it.Valid(); ++it) { + std::wstring full_path = std::wstring(kRegistryFirefoxInstalled) + L"\\" + + it.Name() + L"\\Main"; + RegKey key(HKEY_LOCAL_MACHINE, full_path.c_str(), KEY_READ); + std::wstring install_dir; + if (!key.ReadValue(L"Install Directory", &install_dir)) + continue; + out->push_back(install_dir); + } +} + +void PluginList::LoadFirefoxPlugins() { + std::vector<std::wstring> paths; + GetFirefoxInstalledPaths(&paths); + for (unsigned int i = 0; i < paths.size(); ++i) { + std::wstring path = paths[i] + L"\\plugins"; + LoadPlugins(path); + } + + LoadPluginsInRegistryFolder(HKEY_CURRENT_USER, kRegistryMozillaPlugins); + LoadPluginsInRegistryFolder(HKEY_LOCAL_MACHINE, kRegistryMozillaPlugins); + + std::wstring firefox_app_data_plugin_path; + if (PathService::Get(base::DIR_APP_DATA, &firefox_app_data_plugin_path)) { + firefox_app_data_plugin_path += L"\\Mozilla\\plugins"; + LoadPlugins(firefox_app_data_plugin_path); + } +} + +void PluginList::LoadAcrobatPlugins() { + std::wstring path; + if (GetInstalledPath(kRegistryAcrobat, &path)) { + path.append(L"\\Browser"); + LoadPlugins(path); + } +} + +void PluginList::LoadQuicktimePlugins() { + std::wstring path; + if (GetInstalledPath(kRegistryQuickTime, &path)) { + path.append(L"\\plugins"); + LoadPlugins(path); + } +} + +void PluginList::LoadWindowsMediaPlugins() { + std::wstring path; + if (GetInstalledPath(kRegistryWindowsMedia, &path)) { + LoadPlugins(path); + } +} + +void PluginList::LoadJavaPlugin() { + // Load the new NPAPI Java plugin + // 1. Open the main JRE key under HKLM + RegKey java_key(HKEY_LOCAL_MACHINE, kRegistryJava, KEY_QUERY_VALUE); + + // 2. Read the current Java version + std::wstring java_version; + if (!java_key.ReadValue(kRegistryBrowserJavaVersion, &java_version)) + java_key.ReadValue(kRegistryCurrentJavaVersion, &java_version); + + if (!java_version.empty()) { + java_key.OpenKey(java_version.c_str(), KEY_QUERY_VALUE); + + // 3. Install path of the JRE binaries is specified in "JavaHome" + // value under the Java version key. + std::wstring java_plugin_directory; + if (java_key.ReadValue(kRegistryJavaHome, &java_plugin_directory)) { + + // 4. The new plugin resides under the 'bin/new_plugin' + // subdirectory. + DCHECK(!java_plugin_directory.empty()); + java_plugin_directory.append(L"\\bin\\new_plugin"); + + // 5. We don't know the exact name of the DLL but it's in the form + // NP*.dll so just invoke LoadPlugins on this path. + LoadPlugins(java_plugin_directory); + } + } +} + +void PluginList::LoadPluginsInRegistryFolder( + HKEY root_key, + const std::wstring& registry_folder) { + for (RegistryKeyIterator iter(root_key, registry_folder.c_str()); + iter.Valid(); ++iter) { + // Use the registry to gather plugin across the file system. + std::wstring reg_path = registry_folder; + reg_path.append(L"\\"); + reg_path.append(iter.Name()); + RegKey key(root_key, reg_path.c_str()); + + std::wstring path; + if (key.ReadValue(kRegistryPath, &path)) + LoadPlugin(path); + } +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_list.h b/webkit/glue/plugins/plugin_list.h new file mode 100644 index 0000000..1176ad8 --- /dev/null +++ b/webkit/glue/plugins/plugin_list.h @@ -0,0 +1,197 @@ +// Copyright 2008, 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. + +// TODO: Need mechanism to cleanup the static instance + +#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H__ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "webkit/glue/webplugin.h" + +class GURL; + +namespace NPAPI +{ + +// Used by plugins_test when testing the older WMP plugin to force the new +// plugin to not get loaded. +#define kUseOldWMPPluginSwitch L"use-old-wmp" +// Used for testing ActiveX shim. By default it's off. If this flag is specified +// we will use the native ActiveX shim. +#define kNoNativeActiveXShimSwitch L"no-activex" +// Internal file name for activex shim, used as a unique identifier. +#define kActiveXShimFileName L"activex-shim" + +#define kDefaultPluginDllName L"default_plugin" + +class PluginLib; +class PluginInstance; + +// The PluginList is responsible for loading our NPAPI based plugins. +// It loads plugins from a known directory by looking for DLLs +// which start with "NP", and checking to see if they are valid +// NPAPI libraries. +class PluginList : public base::RefCounted<PluginList> { + public: + // Gets the one instance of the PluginList. + // + // Accessing the singleton causes the PluginList to look on + // disk for existing plugins. It does not actually load + // libraries, that will only happen when you initialize + // the plugin for the first time. + static PluginList* Singleton(); + + // Add an extra plugin to load when we actually do the loading. This is + // static because we want to be able to add to it without searching the disk + // for plugins. Must be called before the plugins have been loaded. + static void AddExtraPluginPath(const std::wstring& plugin_path); + + virtual ~PluginList(); + + // Find a plugin to by mime type, and clsid. + // If clsid is empty, we will just find the plugin that supports mime type. + // Otherwise, if mime_type is application/x-oleobject etc that supported by + // by our activex shim, we need to check if the specified ActiveX exists. + // If not we will not return the activex shim, instead we will let the + // default plugin handle activex installation. + // The allow_wildcard parameter controls whether this function returns + // plugins which support wildcard mime types (* as the mime type) + PluginLib* FindPlugin(const std::string &mime_type, const std::string& clsid, + bool allow_wildcard); + + // Find a plugin to by extension. Returns the corresponding mime type + PluginLib* FindPlugin(const GURL &url, std::string* actual_mime_type); + + // Check if we have any plugin for a given type. + // mime_type must be all lowercase. + bool SupportsType(const std::string &mime_type); + + // Returns true if the given WebPluginInfo supports a given file extension. + // extension should be all lower case. + // If mime_type is not NULL, it will be set to the mime type if found. + // The mime type which corresponds to the extension is optionally returned + // back. + static bool SupportsExtension(const WebPluginInfo& info, + const std::string &extension, + std::string* actual_mime_type); + + // Shutdown all plugins. Should be called at process teardown. + void Shutdown(); + + // Get all the plugins + bool GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins); + + // Returns true if a plugin is found for the given url and mime type. + // The mime type which corresponds to the URL is optionally returned + // back. + // The allow_wildcard parameter controls whether this function returns + // plugins which support wildcard mime types (* as the mime type) + bool GetPluginInfo(const GURL& url, + const std::string& mime_type, + const std::string& clsid, + bool allow_wildcard, + WebPluginInfo* info, + std::string* actual_mime_type); + + // Get plugin info by plugin dll path. Returns true if the plugin is found and + // WebPluginInfo has been filled in |info| + bool GetPluginInfoByDllPath(const std::wstring& dll_path, + WebPluginInfo* info); + private: + // Constructors are private for singletons + PluginList(); + + // Load all plugins from the default plugins directory + void LoadPlugins(bool refresh); + + // Load all plugins from a specific directory + void LoadPlugins(const std::wstring &path); + + // Load a specific plugin with full path. filename can be mixed case. + void LoadPlugin(const std::wstring &filename); + + // Returns true if we should load the given plugin, or false otherwise. + // filename must be lower case. + bool ShouldLoadPlugin(const std::wstring& filename); + + // Load internal plugins. Right now there is only one: activex_shim. + void LoadInternalPlugins(); + + // Find a plugin by filename. Returns -1 if it's not found, otherwise its + // index in plugins_. filename needs to be lower case. + int FindPluginFile(const std::wstring& filename); + + // The application path where we expect to find plugins. + static std::wstring GetPluginAppDirectory(); + + // The executable path where we expect to find plugins. + static std::wstring GetPluginExeDirectory(); + + // Load plugins from the Firefox install path. This is kind of + // a kludge, but it helps us locate the flash player for users that + // already have it for firefox. Not having to download yet-another-plugin + // is a good thing. + void LoadFirefoxPlugins(); + + // Hardcoded logic to detect and load acrobat plugins + void LoadAcrobatPlugins(); + + // Hardcoded logic to detect and load quicktime plugins + void LoadQuicktimePlugins(); + + // Hardcoded logic to detect and load Windows Media Player plugins + void LoadWindowsMediaPlugins(); + + // Hardcoded logic to detect and load Java plugins + void LoadJavaPlugin(); + + // Search the registry at the given path and load plugins listed there. + void LoadPluginsInRegistryFolder(HKEY root_key, + const std::wstring& registry_folder); + + // true if we shouldn't load the new WMP plugin. + bool dont_load_new_wmp_; + + bool use_internal_activex_shim_; + + static scoped_refptr<PluginList> singleton_; + bool plugins_loaded_; + std::vector<scoped_refptr<PluginLib> > plugins_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginList); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H__ diff --git a/webkit/glue/plugins/plugin_stream.cc b/webkit/glue/plugins/plugin_stream.cc new file mode 100644 index 0000000..9f41ac9 --- /dev/null +++ b/webkit/glue/plugins/plugin_stream.cc @@ -0,0 +1,323 @@ +// Copyright 2008, 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. + +// TODO : Support NP_ASFILEONLY mode +// TODO : Support NP_SEEK mode +// TODO : Support SEEKABLE=true in NewStream + +#include "webkit/glue/plugins/plugin_stream.h" + +#include "base/string_util.h" +#include "base/message_loop.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/webkit_glue.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI { + +PluginStream::PluginStream( + PluginInstance *instance, + const char *url, + bool need_notify, + void *notify_data) + : instance_(instance), + bytes_sent_(0), + notify_needed_(need_notify), + notify_data_(notify_data), + close_on_write_data_(false), + opened_(false), + requested_plugin_mode_(NP_NORMAL), + temp_file_handle_(INVALID_HANDLE_VALUE) { + memset(&stream_, 0, sizeof(stream_)); + stream_.url = _strdup(url); + temp_file_name_[0] = '\0'; +} + +PluginStream::~PluginStream() { + // always cleanup our temporary files. + CleanupTempFile(); + + free(const_cast<char*>(stream_.url)); +} + +void PluginStream::UpdateUrl(const char* url) { + DCHECK(!opened_); + free(const_cast<char*>(stream_.url)); + stream_.url = _strdup(url); +} + +bool PluginStream::Open(const std::string &mime_type, + const std::string &headers, + uint32 length, + uint32 last_modified) { + headers_ = headers; + NPP id = instance_->npp(); + stream_.end = length; + stream_.lastmodified = last_modified; + stream_.pdata = 0; + stream_.ndata = id->ndata; + stream_.notifyData = notify_data_; + if (!headers_.empty()) + stream_.headers = headers_.c_str(); + + const char *char_mime_type = "application/x-unknown-content-type"; + std::string temp_mime_type; + if (!mime_type.empty()) { + char_mime_type = mime_type.c_str(); + } else { + GURL gurl(stream_.url); + std::wstring path(UTF8ToWide(gurl.path())); + if (webkit_glue::GetMimeTypeFromFile(path, &temp_mime_type)) + char_mime_type = temp_mime_type.c_str(); + } + + // Silverlight expects a valid mime type + DCHECK(strlen(char_mime_type) != 0); + NPError err = instance_->NPP_NewStream((NPMIMEType)char_mime_type, + &stream_, false, + &requested_plugin_mode_); + if (err != NPERR_NO_ERROR) + return false; + + opened_ = true; + + // If the plugin has requested certain modes, then we need a copy + // of this file on disk. Open it and save it as we go. + if (requested_plugin_mode_ == NP_ASFILEONLY || + requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_SEEK) { + if (OpenTempFile() == false) + return false; + } + + return true; +} + +int PluginStream::Write(const char *buffer, const int length) { + // There may be two streams to write to - the plugin and the file. + // It is unclear what to do if we cannot write to both. The rules of + // this function are that the plugin must consume at least as many + // bytes as returned by the WriteReady call. So, we will attempt to + // write that many to both streams. If we can't write that many bytes + // to each stream, we'll return failure. + + DCHECK(opened_); + if (WriteToFile(buffer, length) && WriteToPlugin(buffer, length)) + return length; + + return -1; +} + +bool PluginStream::WriteToFile(const char *buf, const int length) { + // For ASFILEONLY, ASFILE, and SEEK modes, we need to write + // to the disk + if (temp_file_handle_ != INVALID_HANDLE_VALUE && + (requested_plugin_mode_ == NP_SEEK || + requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_ASFILEONLY) ) { + int totalBytesWritten = 0; + DWORD bytes; + do { + if (WriteFile(temp_file_handle_, buf, length, &bytes, 0) == FALSE) + break; + totalBytesWritten += bytes; + } while (bytes > 0 && totalBytesWritten < length); + + if (totalBytesWritten != length) + return false; + } + + return true; +} + +bool PluginStream::WriteToPlugin(const char *buf, const int length) { + // For NORMAL and ASFILE modes, we send the data to the plugin now + if (requested_plugin_mode_ != NP_NORMAL && + requested_plugin_mode_ != NP_ASFILE) + return true; + + int written = TryWriteToPlugin(buf, length); + if (written == -1) + return false; + + if (written < length) { + // Buffer the remaining data. + size_t remaining = length - written; + size_t previous_size = delivery_data_.size(); + delivery_data_.resize(previous_size + remaining); + memcpy(&delivery_data_[previous_size], buf + written, remaining); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &PluginStream::OnDelayDelivery)); + } + + return true; +} + +void PluginStream::OnDelayDelivery() { + // It is possible that the plugin stream may have closed before the task + // was hit. + if (!opened_) { + return; + } + + int size = static_cast<int>(delivery_data_.size()); + int written = TryWriteToPlugin(&delivery_data_.front(), size); + if (written > 0) { + // Remove the data that we already wrote. + delivery_data_.erase(delivery_data_.begin(), + delivery_data_.begin() + written); + } +} + +int PluginStream::TryWriteToPlugin(const char *buf, const int length) { + bool result = true; + int byte_offset = 0; + + while (byte_offset < length) { + int bytes_remaining = length - byte_offset; + int bytes_to_write = instance_->NPP_WriteReady(&stream_); + if (bytes_to_write > bytes_remaining) + bytes_to_write = bytes_remaining; + + if (bytes_to_write == 0) + return byte_offset; + + int bytesSent = instance_->NPP_Write(&stream_, + bytes_sent_, + bytes_to_write, + const_cast<char*>(buf + byte_offset)); + if (bytesSent < bytes_to_write) { + // We couldn't write all the bytes. This is an error. + return -1; + } + bytes_sent_ += bytesSent; + byte_offset += bytesSent; + } + + if (close_on_write_data_) + Close(NPRES_DONE); + + return length; +} + +void PluginStream::WriteAsFile() { + if (requested_plugin_mode_ == NP_ASFILE || + requested_plugin_mode_ == NP_ASFILEONLY) + instance_->NPP_StreamAsFile(&stream_, temp_file_name_); +} + +bool PluginStream::Close(NPReason reason) { + if (opened_ == true) { + opened_ = false; + + if (delivery_data_.size()) { + if (reason == NPRES_DONE) { + // There is more data to be streamed, don't destroy the stream now. + close_on_write_data_ = true; + return true; + } else { + // Stop any pending data from being streamed + delivery_data_.resize(0); + } + } + + // If we have a temp file, be sure to close it. + // Also, allow the plugin to access it now. + if (temp_file_handle_ != INVALID_HANDLE_VALUE) { + CloseTempFile(); + WriteAsFile(); + } + + if (stream_.ndata != NULL) { + // Stream hasn't been closed yet. + NPError err = instance_->NPP_DestroyStream(&stream_, reason); + DCHECK(err == NPERR_NO_ERROR); + } + } + + Notify(reason); + return true; +} + +bool PluginStream::OpenTempFile() { + DCHECK(temp_file_handle_ == INVALID_HANDLE_VALUE); + + // The reason for using all the Ascii versions of these filesystem + // calls is that the filename which we pass back to the plugin + // via NPAPI is an ascii filename. Otherwise, we'd use wide-chars. + // + // TODO: + // This is a bug in NPAPI itself, and it needs to be fixed. + // The case which will fail is if a user has a multibyte name, + // but has the system locale set to english. GetTempPathA will + // return junk in this case, causing us to be unable to open the + // file. + + char temp_directory[MAX_PATH]; + if (GetTempPathA(MAX_PATH, temp_directory) == 0) + return false; + if (GetTempFileNameA(temp_directory, "npstream", 0, temp_file_name_) == 0) + return false; + temp_file_handle_ = CreateFileA(temp_file_name_, + FILE_ALL_ACCESS, + FILE_SHARE_READ, + 0, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (temp_file_handle_ == INVALID_HANDLE_VALUE) { + temp_file_name_[0] = '\0'; + return false; + } + return true; +} + +void PluginStream::CloseTempFile() { + if (temp_file_handle_ != INVALID_HANDLE_VALUE) { + CloseHandle(temp_file_handle_); + temp_file_handle_ = INVALID_HANDLE_VALUE; + } +} + +void PluginStream::CleanupTempFile() { + CloseTempFile(); + if (temp_file_name_[0] != '\0') { + DeleteFileA(temp_file_name_); + temp_file_name_[0] = '\0'; + } +} + +void PluginStream::Notify(NPReason reason) { + if (notify_needed_) { + instance_->NPP_URLNotify(stream_.url, reason, notify_data_); + notify_needed_ = false; + } +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_stream.h b/webkit/glue/plugins/plugin_stream.h new file mode 100644 index 0000000..e4b99ba --- /dev/null +++ b/webkit/glue/plugins/plugin_stream.h @@ -0,0 +1,136 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__ + +#include <string> +#include <vector> + +#include "base/ref_counted.h" +#include "third_party/npapi/bindings/npapi.h" + + +namespace NPAPI { + +class PluginInstance; + +// Base class for a NPAPI stream. Tracks basic elements +// of a stream for NPAPI notifications and stream position. +class PluginStream : public base::RefCounted<PluginStream> { + public: + // Create a new PluginStream object. If needNotify is true, then the + // plugin will be notified when the stream has been fully sent. + PluginStream(PluginInstance *instance, + const char *url, + bool need_notify, + void *notify_data); + virtual ~PluginStream(); + + // In case of a redirect, this can be called to update the url. But it must + // be called before Open(). + void UpdateUrl(const char* url); + + // Opens the stream to the Plugin. + // If the mime-type is not specified, we'll try to find one based on the + // mime-types table and the extension (if any) in the URL. + // If the size of the stream is known, use length to set the size. If + // not known, set length to 0. + bool Open(const std::string &mime_type, + const std::string &headers, + uint32 length, + uint32 last_modified); + + // Writes to the stream. + int Write(const char *buf, const int len); + + // Write the result as a file. + void WriteAsFile(); + + // Notify the plugin that a stream is complete. + void Notify(NPReason reason); + + // Close the stream. + virtual bool Close(NPReason reason); + + const NPStream* stream() const { + return &stream_; + } + + protected: + PluginInstance* instance() { return instance_.get(); } + // Check if the stream is open. + bool open() { return opened_; } + + private: + // Open a temporary file for this stream. + // If successful, will set temp_file_name_, temp_file_handle_, and + // return true. + bool OpenTempFile(); + + // Closes the temporary file if it is open. + void CloseTempFile(); + + // Closes the temporary file if it is open and deletes the file. + void CleanupTempFile(); + + // Sends the data to the file if it's open. + bool WriteToFile(const char *buf, const int length); + + // Sends the data to the plugin. If it's not ready, handles buffering it + // and retrying later. + bool WriteToPlugin(const char *buf, const int length); + + // Send the data to the plugin, returning how many bytes it accepted, or -1 + // if an error occurred. + int TryWriteToPlugin(const char *buf, const int length); + + // The callback which calls TryWriteToPlugin. + void OnDelayDelivery(); + + private: + NPStream stream_; + std::string headers_; + scoped_refptr<PluginInstance> instance_; + int bytes_sent_; + bool notify_needed_; + void * notify_data_; + bool close_on_write_data_; + uint16 requested_plugin_mode_; + bool opened_; + char temp_file_name_[MAX_PATH]; + HANDLE temp_file_handle_; + std::vector<char> delivery_data_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginStream); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_H__ diff --git a/webkit/glue/plugins/plugin_stream_url.cc b/webkit/glue/plugins/plugin_stream_url.cc new file mode 100644 index 0000000..9400f2c --- /dev/null +++ b/webkit/glue/plugins/plugin_stream_url.cc @@ -0,0 +1,104 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_stream_url.h" + +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/plugins/plugin_instance.h" + +namespace NPAPI { + +PluginStreamUrl::PluginStreamUrl( + int resource_id, + const GURL &url, + PluginInstance *instance, + bool notify_needed, + void *notify_data) + : PluginStream(instance, url.spec().c_str(), notify_needed, notify_data), + url_(url), + id_(resource_id) { +} + +PluginStreamUrl::~PluginStreamUrl() { +} + +bool PluginStreamUrl::Close(NPReason reason) { + if (id_ != 0) { + if (instance()->webplugin()) { + instance()->webplugin()->CancelResource(id_); + } + + id_ = 0; + } + + bool result = PluginStream::Close(reason); + instance()->RemoveStream(this); + return result; +} + +void PluginStreamUrl::WillSendRequest(const GURL& url) { + url_ = url; + UpdateUrl(url.spec().c_str()); +} + +void PluginStreamUrl::DidReceiveResponse(const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified, + bool* cancel) { + + bool opened = Open(mime_type, + headers, + expected_length, + last_modified); + if (!opened) { + instance()->RemoveStream(this); + *cancel = true; + } +} + +void PluginStreamUrl::DidReceiveData(const char* buffer, int length) { + if (!open()) + return; + + if (length > 0) + Write(const_cast<char*>(buffer), length); +} + +void PluginStreamUrl::DidFinishLoading() { + Close(NPRES_DONE); +} + +void PluginStreamUrl::DidFail() { + Close(NPRES_NETWORK_ERR); +} + +} // namespace NPAPI diff --git a/webkit/glue/plugins/plugin_stream_url.h b/webkit/glue/plugins/plugin_stream_url.h new file mode 100644 index 0000000..b6b5b3b --- /dev/null +++ b/webkit/glue/plugins/plugin_stream_url.h @@ -0,0 +1,84 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__ + + +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_stream.h" +#include "googleurl/src/gurl.h" + +namespace NPAPI { + +class PluginInstance; + +// A NPAPI Stream based on a URL. +class PluginStreamUrl : public PluginStream, + public WebPluginResourceClient { + public: + // Create a new stream for sending to the plugin by fetching + // a URL. If notifyNeeded is set, then the plugin will be notified + // when the stream has been fully sent to the plugin. Initialize + // must be called before the object is used. + PluginStreamUrl(int resource_id, + const GURL &url, + PluginInstance *instance, + bool notify_needed, + void *notify_data); + virtual ~PluginStreamUrl(); + + // Stop sending the stream to the client. + // Overrides the base Close so we can cancel our fetching the URL if + // it is still loading. + bool Close(NPReason reason); + + // + // WebPluginResourceClient methods + // + void WillSendRequest(const GURL& url); + void DidReceiveResponse(const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified, + bool* cancel); + void DidReceiveData(const char* buffer, int length); + void DidFinishLoading(); + void DidFail(); + + private: + GURL url_; + int id_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginStreamUrl); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
\ No newline at end of file diff --git a/webkit/glue/plugins/plugin_string_stream.cc b/webkit/glue/plugins/plugin_string_stream.cc new file mode 100644 index 0000000..c32f5ab --- /dev/null +++ b/webkit/glue/plugins/plugin_string_stream.cc @@ -0,0 +1,56 @@ +// Copyright 2008, 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 "webkit/glue/plugins/plugin_string_stream.h" + +namespace NPAPI { + +PluginStringStream::PluginStringStream( + PluginInstance *instance, + const std::string &url, + bool notify_needed, + void *notify_data) + : PluginStream(instance, url.c_str(), notify_needed, notify_data) { +} + +PluginStringStream::~PluginStringStream() { +} + +void PluginStringStream::SendToPlugin(const std::string &data, + const std::string &mime_type) { + int length = static_cast<int>(data.length()); + if (Open(mime_type, std::string(), length, 0)) { + // TODO - check if it was not fully sent, and figure out a backup plan. + int written = Write(data.c_str(), length); + NPReason reason = written == length ? NPRES_DONE : NPRES_NETWORK_ERR; + Close(reason); + } +} + +} diff --git a/webkit/glue/plugins/plugin_string_stream.h b/webkit/glue/plugins/plugin_string_stream.h new file mode 100644 index 0000000..8c2e2c6 --- /dev/null +++ b/webkit/glue/plugins/plugin_string_stream.h @@ -0,0 +1,62 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H__ +#define WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H__ + +#include "webkit/glue/plugins/plugin_stream.h" + +namespace NPAPI { + +class PluginInstance; + +// An NPAPI stream from a string. +class PluginStringStream : public PluginStream { + public: + // Create a new stream for sending to the plugin. + // If notify_needed, will notify the plugin after the data has + // all been sent. + PluginStringStream(PluginInstance *instance, + const std::string &url, + bool notify_needed, + void *notify_data); + virtual ~PluginStringStream(); + + // Initiates the sending of data to the plugin. + void SendToPlugin(const std::string &data, + const std::string &mime_type); + + private: + + DISALLOW_EVIL_CONSTRUCTORS(PluginStringStream); +}; + +} // namespace NPAPI + +#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H__ diff --git a/webkit/glue/plugins/test/SConscript b/webkit/glue/plugins/test/SConscript new file mode 100644 index 0000000..f8113f6 --- /dev/null +++ b/webkit/glue/plugins/test/SConscript @@ -0,0 +1,109 @@ +# Copyright 2008, 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. + +Import('env', 'env_res') + +env = env.Clone() +env_res = env_res.Clone() + +input_files = [ + 'npapi_constants.cc', + 'npapi_test.cc', + env_res.RES('npapi_test.rc'), + 'plugin_arguments_test.cc', + 'plugin_client.cc', + 'plugin_delete_plugin_in_stream_test.cc', + 'plugin_execute_script_delete_test.cc', + 'plugin_get_javascript_url_test.cc', + 'plugin_geturl_test.cc', + 'plugin_new_fails_test.cc', + 'plugin_npobject_lifetime_test.cc', + 'plugin_npobject_proxy_test.cc', + 'plugin_test.cc', + 'plugin_window_size_test.cc', + 'npapi_test.def', + + env.File('$BASE_DIR/base.lib'), +] + +env.Append( + CCFLAGS = [ + '/TP', + '/wd4800', + '/wd4503', + '/wd4819', + ], + + LIBS = [ + 'comctl32.lib', + 'shlwapi.lib', + 'rpcrt4.lib', + 'winmm.lib', + 'wininet.lib', + 'version.lib', + 'msimg32.lib', + 'ws2_32.lib', + 'usp10.lib', + 'psapi.lib', + 'kernel32.lib', + 'user32.lib', + 'gdi32.lib', + 'winspool.lib', + 'comdlg32.lib', + 'advapi32.lib', + 'shell32.lib', + 'ole32.lib', + 'oleaut32.lib', + 'uuid.lib', + 'odbc32.lib', + 'odbccp32.lib', + + 'delayimp.lib', + ], + + LINKFLAGS = [ + '/DELAYLOAD:"dwmapi.dll"', + '/DELAYLOAD:"uxtheme.dll"', + '/FIXED:No', + '/SUBSYSTEM:CONSOLE', + '/MACHINE:X86', + '/safeseh', + '/dynamicbase', + '/ignore:4199', + '/nxcompat', + ], +) + +dll = env.SharedLibrary(['npapi_test_plugin', + 'npapi_test_plugin.ilk', + 'npapi_test_plugin.pdb'], + input_files) + +i = env.Install('$TARGET_ROOT', dll) +env.Alias('webkit', i) diff --git a/webkit/glue/plugins/test/npapi_constants.cc b/webkit/glue/plugins/test/npapi_constants.cc new file mode 100644 index 0000000..eadd9e0 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_constants.cc @@ -0,0 +1,35 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/npapi_constants.h" + +namespace NPAPIClient { +const char kTestCompleteCookie[] = "status"; +const char kTestCompleteSuccess[] = "OK"; +} diff --git a/webkit/glue/plugins/test/npapi_constants.h b/webkit/glue/plugins/test/npapi_constants.h new file mode 100644 index 0000000..0894e7a --- /dev/null +++ b/webkit/glue/plugins/test/npapi_constants.h @@ -0,0 +1,44 @@ +// Copyright 2008, 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. + +// Constants for the NPAPI test + +#ifndef WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__ +#define WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__ + +namespace NPAPIClient { +// The name of the cookie which will be used to communicate between +// the plugin and the test harness. +extern const char kTestCompleteCookie[]; + +// The cookie value which will be sent to the client upon successful +// test. +extern const char kTestCompleteSuccess[]; +} +#endif // WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__ diff --git a/webkit/glue/plugins/test/npapi_test.cc b/webkit/glue/plugins/test/npapi_test.cc new file mode 100644 index 0000000..4272dd4 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test.cc @@ -0,0 +1,92 @@ +// Copyright 2008, 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. + +// +// npapitest +// +// This is a NPAPI Plugin Program which is used to test the Browser's NPAPI +// host implementation. It is used in conjunction with the npapi_unittest. +// +// As a NPAPI Plugin, you can invoke it by creating a web page of the following +// type: +// +// <embed src="content-to-load" type="application/vnd.npapi-test" +// name="test-name"> +// +// arguments: +// src: This is the initial content which will be sent to the plugin. +// type: Must be "application/vnd.npapi-test" +// name: The testcase to run when invoked +// id: The id of the test being run (for testing concurrent plugins) +// +// The Plugin drives the actual test, calling host functions and validating the +// Host callbacks which it receives. It is the duty of the plugin to record +// all errors. +// +// To indicate test completion, the plugin expects the containing HTML page to +// implement two javascript functions: +// onSuccess(string testname); +// onFailure(string testname, string results); +// The HTML host pages used in this test will then set a document cookie +// which the automated test framework can poll for and discover that the +// test has completed. +// +// +// TESTS +// When the PluginClient receives a NPP_New callback from the browser, +// it looks at the "name" argument which is passed in. It verifies that +// the name matches a known test, and instantiates that test. The test is +// a subclass of PluginTest. +// +// + +#include <windows.h> +#include "webkit/glue/plugins/test/plugin_client.h" + +BOOL WINAPI DllMain(HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) { + return TRUE; +} + +extern "C" { +NPError WINAPI NP_GetEntryPoints(NPPluginFuncs* pFuncs) { + return NPAPIClient::PluginClient::GetEntryPoints(pFuncs); +} + +NPError WINAPI NP_Initialize(NPNetscapeFuncs* pFuncs) { + return NPAPIClient::PluginClient::Initialize(pFuncs); +} + +NPError WINAPI NP_Shutdown() { + return NPAPIClient::PluginClient::Shutdown(); +} +} // extern "C" + +namespace WebCore { + const char* currentTextBreakLocaleID() { return "en_us"; } +}
\ No newline at end of file diff --git a/webkit/glue/plugins/test/npapi_test.def b/webkit/glue/plugins/test/npapi_test.def new file mode 100644 index 0000000..4481c16 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test.def @@ -0,0 +1,6 @@ +LIBRARY npapi_test_plugin + +EXPORTS + NP_GetEntryPoints @1 + NP_Initialize @2 + NP_Shutdown @3 diff --git a/webkit/glue/plugins/test/npapi_test.rc b/webkit/glue/plugins/test/npapi_test.rc new file mode 100644 index 0000000..a337ce6 --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test.rc @@ -0,0 +1,102 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "FileDescription", "npapites Dynamic Link Library" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "npapites" + VALUE "LegalCopyright", "Copyright (C) 2007" + VALUE "OriginalFilename", "npapites.dll" + VALUE "ProductName", " npapites Dynamic Link Library" + VALUE "ProductVersion", "1, 0, 0, 1" + VALUE "MIMEType", "application/vnd.npapi-test\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/webkit/glue/plugins/test/npapi_test_plugin.vcproj b/webkit/glue/plugins/test/npapi_test_plugin.vcproj new file mode 100644 index 0000000..ffcd76c --- /dev/null +++ b/webkit/glue/plugins/test/npapi_test_plugin.vcproj @@ -0,0 +1,256 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="npapi_test_plugin" + ProjectGUID="{0D04AEC1-6B68-492C-BCCF-808DFD69ABC6}" + RootNamespace="npapi_test" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="2" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;..\..\..\build\webkit_common.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="winmm.lib" + ModuleDefinitionFile="npapi_test.def" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="2" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;..\..\..\build\webkit_common.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="winmm.lib" + ModuleDefinitionFile="npapi_test.def" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCWebDeploymentTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <File + RelativePath=".\npapi_constants.cc" + > + </File> + <File + RelativePath=".\npapi_constants.h" + > + </File> + <File + RelativePath=".\npapi_test.cc" + > + </File> + <File + RelativePath=".\npapi_test.rc" + > + </File> + <File + RelativePath=".\plugin_arguments_test.cc" + > + </File> + <File + RelativePath=".\plugin_arguments_test.h" + > + </File> + <File + RelativePath=".\plugin_client.cc" + > + </File> + <File + RelativePath=".\plugin_client.h" + > + </File> + <File + RelativePath=".\plugin_delete_plugin_in_stream_test.cc" + > + </File> + <File + RelativePath=".\plugin_delete_plugin_in_stream_test.h" + > + </File> + <File + RelativePath=".\plugin_execute_script_delete_test.cc" + > + </File> + <File + RelativePath=".\plugin_execute_script_delete_test.h" + > + </File> + <File + RelativePath=".\plugin_get_javascript_url_test.cc" + > + </File> + <File + RelativePath=".\plugin_get_javascript_url_test.h" + > + </File> + <File + RelativePath=".\plugin_geturl_test.cc" + > + </File> + <File + RelativePath=".\plugin_geturl_test.h" + > + </File> + <File + RelativePath=".\plugin_new_fails_test.cc" + > + </File> + <File + RelativePath=".\plugin_new_fails_test.h" + > + </File> + <File + RelativePath=".\plugin_npobject_lifetime_test.cc" + > + </File> + <File + RelativePath=".\plugin_npobject_lifetime_test.h" + > + </File> + <File + RelativePath=".\plugin_npobject_proxy_test.cc" + > + </File> + <File + RelativePath=".\plugin_npobject_proxy_test.h" + > + </File> + <File + RelativePath=".\plugin_test.cc" + > + </File> + <File + RelativePath=".\plugin_test.h" + > + </File> + <File + RelativePath=".\plugin_window_size_test.cc" + > + </File> + <File + RelativePath=".\plugin_window_size_test.h" + > + </File> + <File + RelativePath=".\resource.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/webkit/glue/plugins/test/plugin_arguments_test.cc b/webkit/glue/plugins/test/plugin_arguments_test.cc new file mode 100644 index 0000000..dfd34e4 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_arguments_test.cc @@ -0,0 +1,92 @@ +// Copyright 2008, 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. + +#define STRSAFE_NO_DEPRECATE +#include <strsafe.h> +#include "webkit/glue/plugins/test/plugin_arguments_test.h" + +namespace NPAPIClient { + +PluginArgumentsTest::PluginArgumentsTest(NPP id, + NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError PluginArgumentsTest::New(uint16 mode, int16 argc, + const char* argn[], const char* argv[], + NPSavedData* saved) { + // mode: should be the string either "NP_EMBED" or "NP_FULL", + // depending on the mode passed in. + // count: the count of "val" arguments. If the value is + // 2, then we'll find arguments "val1" and "val2". If + // the value is 0, then there will be no "val" arguments. + // size: each val string will be this size * the value's + // index. E.g if size is "10", val1 will be 10bytes, + // and val2 will be 20bytes. + const char *mode_string = GetArgValue("mode", argc, argn, argv); + ExpectAsciiStringNotEqual(mode_string, (const char *)NULL); + if (mode_string != NULL) { + std::string mode_dep_string = mode_string; + if (mode == NP_EMBED) + ExpectStringLowerCaseEqual(mode_dep_string, "np_embed"); + else if (mode == NP_FULL) + ExpectStringLowerCaseEqual(mode_dep_string, "np_full"); + } + + const char *count_string = GetArgValue("count", argc, argn, argv); + if (count_string != NULL) { + int max_args = atoi(count_string); + + const char *size_string = GetArgValue("size", argc, argn, argv); + ExpectAsciiStringNotEqual(size_string, (const char *)NULL); + if (size_string != NULL) { + int size = atoi(size_string); + + for (int index = 1; index <= max_args; index++) { + char arg_name[MAX_PATH]; // Use MAX_PATH for Max Name Length + StringCchPrintfA(arg_name, sizeof(arg_name), "%s%d", "val", index); + const char *val_string = GetArgValue(arg_name, argc, argn, argv); + ExpectAsciiStringNotEqual(val_string, (const char*)NULL); + if (val_string != NULL) + ExpectIntegerEqual((int)strlen(val_string), (index*size)); + } + } + } + + return PluginTest::New(mode, argc, argn, argv, saved); +} + +NPError PluginArgumentsTest::SetWindow(NPWindow* pNPWindow) { + // This test just tests the arguments. We're done now. + this->SignalTestCompleted(); + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_arguments_test.h b/webkit/glue/plugins/test/plugin_arguments_test.h new file mode 100644 index 0000000..57d9e6f --- /dev/null +++ b/webkit/glue/plugins/test/plugin_arguments_test.h @@ -0,0 +1,69 @@ +// Copyright 2008, 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 WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The PluginArgumentsTest test that we properly receive arguments +// intended for the plugin. +// +// This is basically overkill for testing that the arguments passed +// to the plugin match what we expect. +// +// We expect to find the following arguments: +// mode: should be the string either "NP_EMBED" or "NP_FULL", +// depending on the mode passed in. +// count: the count of "val" arguments. If the value is +// 2, then we'll find arguments "val1" and "val2". If +// the value is 0, then there will be no "val" arguments. +// size: each val string will be this size * the value's +// index. E.g if size is "10", val1 will be 10bytes, +// and val2 will be 20bytes. +// +class PluginArgumentsTest : public PluginTest { + public: + // Constructor. + PluginArgumentsTest(NPP id, NPNetscapeFuncs *host_functions); + + // Initialize this PluginTest based on the arguments from NPP_New. + virtual NPError New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_client.cc b/webkit/glue/plugins/test/plugin_client.cc new file mode 100644 index 0000000..d8d409e --- /dev/null +++ b/webkit/glue/plugins/test/plugin_client.cc @@ -0,0 +1,300 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_client.h" +#include "webkit/glue/plugins/test/plugin_arguments_test.h" +#include "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h" +#include "webkit/glue/plugins/test/plugin_execute_script_delete_test.h" +#include "webkit/glue/plugins/test/plugin_get_javascript_url_test.h" +#include "webkit/glue/plugins/test/plugin_geturl_test.h" +#include "webkit/glue/plugins/test/plugin_new_fails_test.h" +#include "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h" +#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h" +#include "webkit/glue/plugins/test/plugin_window_size_test.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" + +namespace NPAPIClient { + +NPNetscapeFuncs* PluginClient::host_functions_; + +NPError PluginClient::GetEntryPoints(NPPluginFuncs* pFuncs) { + if (pFuncs == NULL) + return NPERR_INVALID_FUNCTABLE_ERROR; + + if (pFuncs->size < sizeof(NPPluginFuncs)) + return NPERR_INVALID_FUNCTABLE_ERROR; + + pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + pFuncs->newp = NPP_New; + pFuncs->destroy = NPP_Destroy; + pFuncs->setwindow = NPP_SetWindow; + pFuncs->newstream = NPP_NewStream; + pFuncs->destroystream = NPP_DestroyStream; + pFuncs->asfile = NPP_StreamAsFile; + pFuncs->writeready = NPP_WriteReady; + pFuncs->write = NPP_Write; + pFuncs->print = NPP_Print; + pFuncs->event = NPP_HandleEvent; + pFuncs->urlnotify = NPP_URLNotify; + pFuncs->getvalue = NPP_GetValue; + pFuncs->setvalue = NPP_SetValue; + pFuncs->javaClass = NPP_GetJavaClass; + + return NPERR_NO_ERROR; +} + +NPError PluginClient::Initialize(NPNetscapeFuncs* pFuncs) { + if (pFuncs == NULL) { + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + if (HIBYTE(pFuncs->version) > NP_VERSION_MAJOR) { + return NPERR_INCOMPATIBLE_VERSION_ERROR; + } + + host_functions_ = pFuncs; + + return NPERR_NO_ERROR; +} + +NPError PluginClient::Shutdown() { + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient + +extern "C" { +NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, + int16 argc, char* argn[], char* argv[], NPSavedData* saved) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // We look at the test name requested via the plugin arguments. We match + // that against a given test and try to instantiate it. + + // lookup the name parameter + int name_index = 0; + for (name_index = 0; name_index < argc; name_index++) + if (_stricmp(argn[name_index], "name") == 0) + break; + + if (name_index >= argc) + return NPERR_GENERIC_ERROR; // no name found + + NPError ret = NPERR_GENERIC_ERROR; + bool windowless_plugin = false; + + NPAPIClient::PluginTest *new_test = NULL; + if (_stricmp(argv[name_index], "arguments") == 0) { + new_test = new NPAPIClient::PluginArgumentsTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "geturl") == 0) { + new_test = new NPAPIClient::PluginGetURLTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "npobject_proxy") == 0) { + new_test = new NPAPIClient::NPObjectProxyTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "execute_script_delete_in_paint") == 0) { + new_test = new NPAPIClient::ExecuteScriptDeleteTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + windowless_plugin = true; + } else if (_stricmp(argv[name_index], "getjavascripturl") == 0) { + new_test = new NPAPIClient::ExecuteGetJavascriptUrlTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "checkwindowrect") == 0) { + new_test = new NPAPIClient::PluginWindowSizeTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "self_delete_plugin_stream") == 0) { + new_test = new NPAPIClient::DeletePluginInStreamTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "npobject_lifetime_test") == 0) { + new_test = new NPAPIClient::NPObjectLifetimeTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], + "npobject_lifetime_test_second_instance") == 0) { + new_test = new NPAPIClient::NPObjectLifetimeTestInstance2(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], "new_fails") == 0) { + new_test = new NPAPIClient::NewFailsTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else if (_stricmp(argv[name_index], + "npobject_delete_plugin_in_evaluate") == 0) { + new_test = new NPAPIClient::NPObjectDeletePluginInNPN_Evaluate(instance, + NPAPIClient::PluginClient::HostFunctions()); + } else { + // If we don't have a test case for this, create a + // generic one which basically never fails. + new_test = new NPAPIClient::PluginTest(instance, + NPAPIClient::PluginClient::HostFunctions()); + } + + if (new_test) { + ret = new_test->New(mode, argc, (const char**)argn, + (const char**)argv, saved); + if ((ret == NPERR_NO_ERROR) && windowless_plugin) { + NPAPIClient::PluginClient::HostFunctions()->setvalue( + instance, NPPVpluginWindowBool, NULL); + } + } + + return ret; +} + +NPError NPP_Destroy(NPP instance, NPSavedData** save) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + delete plugin; + + // XXXMB - do work here. + return NPERR_GENERIC_ERROR; +} + +NPError NPP_SetWindow(NPP instance, NPWindow* pNPWindow) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + if (pNPWindow->window == NULL) { + return NPERR_NO_ERROR; + } + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->SetWindow(pNPWindow); +} + +NPError NPP_NewStream(NPP instance, NPMIMEType type, + NPStream* stream, NPBool seekable, uint16* stype) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->NewStream(type, stream, seekable, stype); +} + +int32 NPP_WriteReady(NPP instance, NPStream *stream) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->WriteReady(stream); +} + +int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, + int32 len, void *buffer) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->Write(stream, offset, len, buffer); +} + +NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->DestroyStream(stream, reason); +} + +void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) { + if (instance == NULL) + return; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->StreamAsFile(stream, fname); +} + +void NPP_Print(NPP instance, NPPrint* printInfo) { + if (instance == NULL) + return; + + // XXXMB - do work here. +} + +void NPP_URLNotify(NPP instance, const char* url, NPReason reason, + void* notifyData) { + if (instance == NULL) + return; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->URLNotify(url, reason, notifyData); +} + +NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // XXXMB - do work here. + return NPERR_GENERIC_ERROR; +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) { + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + // XXXMB - do work here. + return NPERR_GENERIC_ERROR; +} + +int16 NPP_HandleEvent(NPP instance, void* event) { + if (instance == NULL) + return 0; + + NPAPIClient::PluginTest *plugin = + (NPAPIClient::PluginTest*)instance->pdata; + + return plugin->HandleEvent(event); +} + +void* NPP_GetJavaClass(void) { + // XXXMB - do work here. + return NULL; +} +} // extern "C" + + + + diff --git a/webkit/glue/plugins/test/plugin_client.h b/webkit/glue/plugins/test/plugin_client.h new file mode 100644 index 0000000..70df0fe --- /dev/null +++ b/webkit/glue/plugins/test/plugin_client.h @@ -0,0 +1,71 @@ +// Copyright 2008, 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 WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__ + +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +namespace NPAPIClient { + +// A PluginClient is a NPAPI Plugin. This class contains +// the bootstrapping functions used by the browser to load +// the plugin. +class PluginClient { + public: + // Although not documented in the NPAPI specification, this function + // gets the list of entry points in the NPAPI Plugin (client) for the + // NPAPI Host to call. + static NPError GetEntryPoints(NPPluginFuncs* pFuncs); + + // The browser calls this function only once: when a plug-in is loaded, + // before the first instance is created. This is the first function that + // the browser calls. NP_Initialize tells the plug-in that the browser has + // loaded it and provides global initialization. Allocate any memory or + // resources shared by all instances of your plug-in at this time. + static NPError Initialize(NPNetscapeFuncs* pFuncs); + + // The browser calls this function once after the last instance of your + // plug-in is destroyed, before unloading the plug-in library itself. Use + // NP_Shutdown to delete any data allocated in NP_Initialize to be shared + // by all instances of a plug-in. + static NPError Shutdown(); + + // The table of functions provided by the host. + static NPNetscapeFuncs *HostFunctions() { return host_functions_; } + + private: + static NPNetscapeFuncs* host_functions_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__ + diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc new file mode 100644 index 0000000..86fd917 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc @@ -0,0 +1,67 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h" + +#include "webkit/glue/plugins/test/plugin_client.h" + +namespace NPAPIClient { + +#define kUrl "javascript:window.location+\"\"" +#define kUrlStreamId 1 + +DeletePluginInStreamTest::DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + test_started_(false) { +} + +NPError DeletePluginInStreamTest::SetWindow(NPWindow* pNPWindow) { + if (!test_started_) { + std::string url = "self_delete_plugin_stream.html"; + HostFunctions()->geturlnotify(id(), url.c_str(), NULL, + reinterpret_cast<void*>(kUrlStreamId)); + test_started_ = true; + } + return NPERR_NO_ERROR; +} + +NPError DeletePluginInStreamTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + NPIdentifier delete_id = HostFunctions()->getstringidentifier("DeletePluginWithinScript"); + + NPObject *window_obj = NULL; + HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj); + + NPVariant rv; + HostFunctions()->invoke(id(), window_obj, delete_id, NULL, 0, &rv); + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h new file mode 100644 index 0000000..8eeaefb --- /dev/null +++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h @@ -0,0 +1,56 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests +class DeletePluginInStreamTest : public PluginTest { + public: + // Constructor. + DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions); + // + // NPAPI functions + // + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + private: + bool test_started_; + std::string self_url_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H + diff --git a/webkit/glue/plugins/test/plugin_execute_script_delete_test.cc b/webkit/glue/plugins/test/plugin_execute_script_delete_test.cc new file mode 100644 index 0000000..c18c91c --- /dev/null +++ b/webkit/glue/plugins/test/plugin_execute_script_delete_test.cc @@ -0,0 +1,53 @@ +// Copyright 2008, 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. + +#define STRSAFE_NO_DEPRECATE +#include "webkit/glue/plugins/test/plugin_execute_script_delete_test.h" +#include "webkit/glue/plugins/test/plugin_client.h" + +namespace NPAPIClient { + +ExecuteScriptDeleteTest::ExecuteScriptDeleteTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +int16 ExecuteScriptDeleteTest::HandleEvent(void* event) { + NPEvent* np_event = reinterpret_cast<NPEvent*>(event); + if (WM_PAINT == np_event->event ) { + NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions(); + NPUTF8* urlString = "javascript:DeletePluginWithinScript()"; + NPUTF8* targetString = NULL; + browser->geturl(id(), urlString, targetString); + SignalTestCompleted(); + } + // If this test failed, then we'd have crashed by now. + return PluginTest::HandleEvent(event); +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_execute_script_delete_test.h b/webkit/glue/plugins/test/plugin_execute_script_delete_test.h new file mode 100644 index 0000000..fe3c6ecf --- /dev/null +++ b/webkit/glue/plugins/test/plugin_execute_script_delete_test.h @@ -0,0 +1,50 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests the case where a plugin instance is deleted by invoking +// a javascript function in the context of a paint event. +class ExecuteScriptDeleteTest : public PluginTest { + public: + // Constructor. + ExecuteScriptDeleteTest(NPP id, NPNetscapeFuncs *host_functions); + // NPAPI HandleEvent handler + virtual int16 HandleEvent(void* event); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H + diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc new file mode 100644 index 0000000..1b6f1bd --- /dev/null +++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc @@ -0,0 +1,134 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_get_javascript_url_test.h" + + +// url for "self". +#define SELF_URL "javascript:window.location+\"\"" +// The identifier for the self url stream. +#define SELF_URL_STREAM_ID 1 + +// The identifier for the fetched url stream. +#define FETCHED_URL_STREAM_ID 2 + +// The maximum chunk size of stream data. +#define STREAM_CHUNK 197 + +namespace NPAPIClient { + +ExecuteGetJavascriptUrlTest::ExecuteGetJavascriptUrlTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + test_started_(false) { +} + +NPError ExecuteGetJavascriptUrlTest::SetWindow(NPWindow* pNPWindow) { + if (!test_started_) { + std::string url = SELF_URL; + HostFunctions()->geturlnotify(id(), url.c_str(), "_top", + reinterpret_cast<void*>(SELF_URL_STREAM_ID)); + test_started_ = true; + } + return NPERR_NO_ERROR; +} + +NPError ExecuteGetJavascriptUrlTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +int32 ExecuteGetJavascriptUrlTest::WriteReady(NPStream *stream) { + return STREAM_CHUNK; +} + +int32 ExecuteGetJavascriptUrlTest::Write(NPStream *stream, int32 offset, int32 len, + void *buffer) { + if (stream == NULL) + SetError("Write got null stream"); + if (len < 0 || len > STREAM_CHUNK) + SetError("Write got bogus stream chunk size"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + self_url_.append(static_cast<char*>(buffer), len); + break; + default: + SetError("Unexpected write callback"); + break; + } + // Pretend that we took all the data. + return len; +} + + +NPError ExecuteGetJavascriptUrlTest::DestroyStream(NPStream *stream, NPError reason) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + // don't care + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +void ExecuteGetJavascriptUrlTest::URLNotify(const char* url, NPReason reason, void* data) { + unsigned long stream_id = PtrToUlong(data); + switch (stream_id) { + case SELF_URL_STREAM_ID: + if (strcmp(url, SELF_URL) != 0) + SetError("URLNotify reported incorrect url for SELF_URL"); + if (self_url_.empty()) + SetError("Failed to obtain window location."); + SignalTestCompleted(); + break; + default: + SetError("Unexpected NewStream callback"); + break; + } +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.h b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h new file mode 100644 index 0000000..33b627e --- /dev/null +++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h @@ -0,0 +1,63 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests NPP_GetURLNotify for a javascript URL with _top +// as the target frame. +class ExecuteGetJavascriptUrlTest : public PluginTest { + public: + // Constructor. + ExecuteGetJavascriptUrlTest(NPP id, NPNetscapeFuncs *host_functions); + // + // NPAPI functions + // + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + virtual int32 WriteReady(NPStream *stream); + virtual int32 Write(NPStream *stream, int32 offset, int32 len, + void *buffer); + virtual NPError DestroyStream(NPStream *stream, NPError reason); + virtual void URLNotify(const char* url, NPReason reason, void* data); + + private: + bool test_started_; + std::string self_url_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H + diff --git a/webkit/glue/plugins/test/plugin_geturl_test.cc b/webkit/glue/plugins/test/plugin_geturl_test.cc new file mode 100644 index 0000000..33a2f17 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_geturl_test.cc @@ -0,0 +1,253 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_geturl_test.h" + +// url for "self". The %22%22 is to make a statement for javascript to +// evaluate and return. +#define SELF_URL "javascript:window.location+\"\"" + +// The identifier for the self url stream. +#define SELF_URL_STREAM_ID 1 + +// The identifier for the fetched url stream. +#define FETCHED_URL_STREAM_ID 2 + +// url for testing GetURL with a bogus URL. +#define BOGUS_URL "bogoproto:///x:/asdf.xysdhffieasdf.asdhj/" + +// The identifier for the bogus url stream. +#define BOGUS_URL_STREAM_ID 3 + +// The maximum chunk size of stream data. +#define STREAM_CHUNK 197 + +namespace NPAPIClient { + +PluginGetURLTest::PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + tests_started_(false), + tests_in_progress_(0), + test_file_handle_(INVALID_HANDLE_VALUE) { +} + +NPError PluginGetURLTest::SetWindow(NPWindow* pNPWindow) { + if (!tests_started_) { + tests_started_ = true; + + tests_in_progress_++; + + std::string url = SELF_URL; + HostFunctions()->geturlnotify(id(), url.c_str(), NULL, + reinterpret_cast<void*>(SELF_URL_STREAM_ID)); + + tests_in_progress_++; + std::string bogus_url = BOGUS_URL; + HostFunctions()->geturlnotify(id(), bogus_url.c_str(), NULL, + reinterpret_cast<void*>(BOGUS_URL_STREAM_ID)); + } + return NPERR_NO_ERROR; +} + +NPError PluginGetURLTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + break; + case FETCHED_URL_STREAM_ID: + { + std::string filename = self_url_; + if (filename.find("file:///", 0) != 0) { + SetError("Test expects a file-url."); + break; + } + + filename = filename.substr(8); // remove "file:///" + + test_file_handle_ = CreateFileA(filename.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (test_file_handle_ == INVALID_HANDLE_VALUE) + SetError("Could not open source file"); + } + break; + case BOGUS_URL_STREAM_ID: + SetError("Unexpected NewStream for BOGUS_URL"); + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +int32 PluginGetURLTest::WriteReady(NPStream *stream) { + unsigned long stream_id = PtrToUlong(stream->notifyData); + if (stream_id == BOGUS_URL_STREAM_ID) + SetError("Received WriteReady for BOGUS_URL"); + + return STREAM_CHUNK; +} + +int32 PluginGetURLTest::Write(NPStream *stream, int32 offset, int32 len, + void *buffer) { + if (stream == NULL) + SetError("Write got null stream"); + if (len < 0 || len > STREAM_CHUNK) + SetError("Write got bogus stream chunk size"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + self_url_.append(static_cast<char*>(buffer), len); + break; + case FETCHED_URL_STREAM_ID: + { + char read_buffer[STREAM_CHUNK]; + DWORD bytes = 0; + if (!ReadFile(test_file_handle_, read_buffer, len, + &bytes, NULL)) + SetError("Could not read data from source file"); + // Technically, readfile could return fewer than len + // bytes. But this is not likely. + if (bytes != len) + SetError("Did not read correct bytelength from source file"); + if (memcmp(read_buffer, buffer, len)) + SetError("Content mismatch between data and source!"); + } + break; + case BOGUS_URL_STREAM_ID: + SetError("Unexpected write callback for BOGUS_URL"); + break; + default: + SetError("Unexpected write callback"); + break; + } + // Pretend that we took all the data. + return len; +} + + +NPError PluginGetURLTest::DestroyStream(NPStream *stream, NPError reason) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + // don't care + break; + case FETCHED_URL_STREAM_ID: + { + char read_buffer[STREAM_CHUNK]; + DWORD bytes = 0; + if (!ReadFile(test_file_handle_, read_buffer, sizeof(read_buffer), + &bytes, NULL)) + SetError("Could not read data from source file"); + if (bytes != 0) + SetError("Data and source mismatch on length"); + CloseHandle(test_file_handle_); + } + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + return NPERR_NO_ERROR; +} + +void PluginGetURLTest::StreamAsFile(NPStream* stream, const char* fname) { + if (stream == NULL) + SetError("NewStream got null stream"); + + unsigned long stream_id = PtrToUlong(stream->notifyData); + switch (stream_id) { + case SELF_URL_STREAM_ID: + // don't care + break; + default: + SetError("Unexpected NewStream callback"); + break; + } +} + +void PluginGetURLTest::URLNotify(const char* url, NPReason reason, void* data) { + if (!tests_in_progress_) { + SetError("URLNotify received after tests completed"); + return; + } + + if (!url) { + SetError("URLNotify received NULL url"); + return; + } + + unsigned long stream_id = PtrToUlong(data); + switch (stream_id) { + case SELF_URL_STREAM_ID: + if (strcmp(url, SELF_URL) != 0) + SetError("URLNotify reported incorrect url for SELF_URL"); + + // We have our stream url. Go fetch it. + HostFunctions()->geturlnotify(id(), self_url_.c_str(), NULL, + reinterpret_cast<void*>(FETCHED_URL_STREAM_ID)); + break; + case FETCHED_URL_STREAM_ID: + if (!url || strcmp(url, self_url_.c_str()) != 0) + SetError("URLNotify reported incorrect url for FETCHED_URL"); + tests_in_progress_--; + break; + case BOGUS_URL_STREAM_ID: + if (reason != NPRES_NETWORK_ERR) { + std::string err = "BOGUS_URL received unexpected URLNotify status: "; + char buf[10]; + _itoa_s(reason, buf, 10, 10); + err.append(buf); + SetError(err); + } + tests_in_progress_--; + break; + default: + SetError("Unexpected NewStream callback"); + break; + } + + if (tests_in_progress_ == 0) + SignalTestCompleted(); +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_geturl_test.h b/webkit/glue/plugins/test/plugin_geturl_test.h new file mode 100644 index 0000000..07e41aa --- /dev/null +++ b/webkit/glue/plugins/test/plugin_geturl_test.h @@ -0,0 +1,71 @@ +// Copyright 2008, 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 WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The PluginGetURLTest test functionality of the NPN_GetURL +// and NPN_GetURLNotify methods. +// +// This test first discovers it's URL by sending a GetURL request +// for 'javascript:top.location'. After receiving that, the +// test will request the url itself (again via GetURL). +class PluginGetURLTest : public PluginTest { + public: + // Constructor. + PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions); + + // + // NPAPI functions + // + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + virtual int32 WriteReady(NPStream *stream); + virtual int32 Write(NPStream *stream, int32 offset, int32 len, + void *buffer); + virtual NPError DestroyStream(NPStream *stream, NPError reason); + virtual void StreamAsFile(NPStream* stream, const char* fname); + virtual void URLNotify(const char* url, NPReason reason, void* data); + + private: + bool tests_started_; + int tests_in_progress_; + std::string self_url_; + HANDLE test_file_handle_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.cc b/webkit/glue/plugins/test/plugin_new_fails_test.cc new file mode 100644 index 0000000..382bf4e --- /dev/null +++ b/webkit/glue/plugins/test/plugin_new_fails_test.cc @@ -0,0 +1,43 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_new_fails_test.h" + +namespace NPAPIClient { + +NewFailsTest::NewFailsTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError NewFailsTest::New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved) { + return NPERR_GENERIC_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.h b/webkit/glue/plugins/test/plugin_new_fails_test.h new file mode 100644 index 0000000..e864553 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_new_fails_test.h @@ -0,0 +1,46 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__ +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +class NewFailsTest : public PluginTest { + public: + NewFailsTest(NPP id, NPNetscapeFuncs *host_functions); + virtual NPError New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NPP_NEW_FAILS_TEST_H__ diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc new file mode 100644 index 0000000..24cc9e9 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc @@ -0,0 +1,189 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h" + +namespace NPAPIClient { + +const int kNPObjectLifetimeTimer = 100; +const int kNPObjectLifetimeTimerElapse = 2000; + +NPObject* NPObjectLifetimeTestInstance2::plugin_instance_object_ = NULL; + +NPObjectDeletePluginInNPN_Evaluate* + NPObjectDeletePluginInNPN_Evaluate::g_npn_evaluate_test_instance_ = NULL; + +NPObjectLifetimeTest::NPObjectLifetimeTest(NPP id, + NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + other_plugin_instance_object_(NULL) { +} + +NPError NPObjectLifetimeTest::SetWindow(NPWindow* pNPWindow) { + HWND window_handle = reinterpret_cast<HWND>(pNPWindow->window); + if (!::GetProp(window_handle, L"Plugin_Instance")) { + ::SetProp(window_handle, L"Plugin_Instance", this); + // We attempt to retreive the NPObject for the plugin instance identified + // by the NPObjectLifetimeTestInstance2 class as it may not have been + // instantiated yet. + SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse, + TimerProc); + } + return NPERR_NO_ERROR; +} + +void CALLBACK NPObjectLifetimeTest::TimerProc( + HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds) { + + KillTimer(window, kNPObjectLifetimeTimer); + NPObjectLifetimeTest* this_instance = + reinterpret_cast<NPObjectLifetimeTest*> + (::GetProp(window, L"Plugin_Instance")); + + this_instance->other_plugin_instance_object_ = + NPObjectLifetimeTestInstance2::plugin_instance_object_; + this_instance->HostFunctions()->retainobject( + this_instance->other_plugin_instance_object_); + + NPString script; + script.UTF8Characters = "javascript:DeleteSecondPluginInstance()"; + script.UTF8Length = static_cast<uint32_t>(strlen(script.UTF8Characters)); + + this_instance->HostFunctions()->geturlnotify( + this_instance->id(), "javascript:DeleteSecondPluginInstance()", NULL, + reinterpret_cast<void*>(1)); +} + +void NPObjectLifetimeTest::URLNotify(const char* url, NPReason reason, + void* data) { + // Create a "location" identifier. + NPIdentifier identifier = HostFunctions()->getstringidentifier("location"); + // Declare a local variant value. + NPVariant variantValue; + // Get the location property from the window object (which is another object). + bool b1 = HostFunctions()->getproperty(id(), other_plugin_instance_object_, + identifier, &variantValue ); + HostFunctions()->releaseobject(other_plugin_instance_object_); + other_plugin_instance_object_ = NULL; + // If this test failed, then we'd have crashed by now. + SignalTestCompleted(); +} + +NPObjectLifetimeTestInstance2::NPObjectLifetimeTestInstance2( + NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPObjectLifetimeTestInstance2::~NPObjectLifetimeTestInstance2() { + if (plugin_instance_object_) { + HostFunctions()->releaseobject(plugin_instance_object_); + plugin_instance_object_ = NULL; + } +} + +NPError NPObjectLifetimeTestInstance2::SetWindow(NPWindow* pNPWindow) { + if (!plugin_instance_object_) { + if (!HostFunctions()->getvalue(id(), NPNVWindowNPObject, + &plugin_instance_object_)) { + SetError("Failed to get NPObject for plugin instance2"); + SignalTestCompleted(); + return NPERR_GENERIC_ERROR; + } + } + + return NPERR_NO_ERROR; +} + + +NPObjectDeletePluginInNPN_Evaluate::NPObjectDeletePluginInNPN_Evaluate( + NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions), + plugin_instance_object_(NULL), + npn_evaluate_timer_proc_set_(false) { + g_npn_evaluate_test_instance_ = this; +} + +NPObjectDeletePluginInNPN_Evaluate::~NPObjectDeletePluginInNPN_Evaluate() { + if (plugin_instance_object_) { + HostFunctions()->releaseobject(plugin_instance_object_); + plugin_instance_object_ = NULL; + } +} + +NPError NPObjectDeletePluginInNPN_Evaluate::SetWindow(NPWindow* np_window) { + if (!::IsWindowVisible(reinterpret_cast<HWND>(np_window->window))) + return NPERR_NO_ERROR; + + HWND window_handle = reinterpret_cast<HWND>(np_window->window); + // We setup a timerproc to invoke NPN_Evaluate to destroy this plugin + // instance. This is to ensure that we don't destroy the plugin instance + // while it is being used in webkit as this leads to crashes and is a + // more accurate representation of the renderer crash as described in + // http://b/issue?id=1134683. + if (!npn_evaluate_timer_proc_set_) { + npn_evaluate_timer_proc_set_ = true; + SetTimer(window_handle, kNPObjectLifetimeTimer, kNPObjectLifetimeTimerElapse, + TimerProc); + } + + return NPERR_NO_ERROR; +} + +void CALLBACK NPObjectDeletePluginInNPN_Evaluate::TimerProc( + HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds) { + + KillTimer(window, kNPObjectLifetimeTimer); + NPObject *window_obj = NULL; + g_npn_evaluate_test_instance_->HostFunctions()->getvalue( + g_npn_evaluate_test_instance_->id(), NPNVWindowNPObject, + &window_obj); + + if (!window_obj) { + g_npn_evaluate_test_instance_->SetError( + "Failed to get NPObject for plugin instance2"); + g_npn_evaluate_test_instance_->SignalTestCompleted(); + return; + } + + std::string script = "javascript:DeletePluginWithinScript()"; + NPString script_string; + script_string.UTF8Characters = script.c_str(); + script_string.UTF8Length = + static_cast<unsigned int>(script.length()); + + NPVariant result_var; + NPError result = g_npn_evaluate_test_instance_->HostFunctions()->evaluate( + g_npn_evaluate_test_instance_->id(), window_obj, + &script_string, &result_var); + // If this test failed we would have crashed by now. +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h new file mode 100644 index 0000000..2a5d808 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h @@ -0,0 +1,96 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__ +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The NPObjectLifeTime class tests the case where a plugin has an NPObject +// which points to a different plugin instance on a different frame in the +// page and whether refcounts on this npobject are valid when the source frame +// is destroyed. +class NPObjectLifetimeTest : public PluginTest { + public: + // Constructor. + NPObjectLifetimeTest(NPP id, NPNetscapeFuncs *host_functions); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); + + virtual void URLNotify(const char* url, NPReason reason, void* data); + + protected: + NPObject* other_plugin_instance_object_; + static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds); +}; + +// The NPObjectLifetimeTestInstance2 class represents the plugin instance +// which is deleted by the NPObjectLifeTime class via a javascript function. +class NPObjectLifetimeTestInstance2 : public PluginTest { + public: + // Constructor. + NPObjectLifetimeTestInstance2(NPP id, NPNetscapeFuncs *host_functions); + ~NPObjectLifetimeTestInstance2(); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); + protected: + static NPObject* plugin_instance_object_; + friend class NPObjectLifetimeTest; + +}; + +// The NPObjectLifeTime class tests the case where a plugin instance is +// destroyed in NPN_Evaluate +class NPObjectDeletePluginInNPN_Evaluate : public PluginTest { + public: + // Constructor. + NPObjectDeletePluginInNPN_Evaluate(NPP id, NPNetscapeFuncs *host_functions); + ~NPObjectDeletePluginInNPN_Evaluate(); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); + + protected: + NPObject* plugin_instance_object_; + static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id, + unsigned long elapsed_milli_seconds); + private: + bool npn_evaluate_timer_proc_set_; + static NPObjectDeletePluginInNPN_Evaluate* g_npn_evaluate_test_instance_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc new file mode 100644 index 0000000..e7fa2fb --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc @@ -0,0 +1,68 @@ +// Copyright 2008, 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. + +#define STRSAFE_NO_DEPRECATE +#include <strsafe.h> +#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h" + +namespace NPAPIClient { + +NPObjectProxyTest::NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError NPObjectProxyTest::SetWindow(NPWindow* pNPWindow) { + NPIdentifier document_id = HostFunctions()->getstringidentifier("document"); + NPIdentifier create_text_node_id = HostFunctions()->getstringidentifier("createTextNode"); + NPIdentifier append_child_id = HostFunctions()->getstringidentifier("appendChild"); + + NPVariant docv; + NPObject *window_obj = NULL; + HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj); + + HostFunctions()->getproperty(id(), window_obj, document_id, &docv); + NPObject *doc = NPVARIANT_TO_OBJECT(docv); + + NPVariant strv; +#pragma warning(suppress: 4267) + STRINGZ_TO_NPVARIANT("div", strv); + + NPVariant textv; + HostFunctions()->invoke(id(), doc, create_text_node_id, &strv, 1, &textv); + + NPVariant v; + HostFunctions()->invoke(id(), doc, append_child_id, &textv, 1, &v); + + // If this test failed, then we'd have crashed by now. + SignalTestCompleted(); + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.h b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h new file mode 100644 index 0000000..e5c94d4 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h @@ -0,0 +1,53 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__ +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__ + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// The NPObjectProxyTest tests that when we proxy an NPObject that is itself +// a proxy, we don't create a new proxy but instead just use the original +// pointer. + +class NPObjectProxyTest : public PluginTest { + public: + // Constructor. + NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions); + + // NPAPI SetWindow handler. + virtual NPError SetWindow(NPWindow* pNPWindow); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_test.cc b/webkit/glue/plugins/test/plugin_test.cc new file mode 100644 index 0000000..043fef9 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_test.cc @@ -0,0 +1,160 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_test.h" +#include "webkit/glue/plugins/test/npapi_constants.h" + +namespace NPAPIClient { + +PluginTest::PluginTest(NPP id, NPNetscapeFuncs *host_functions) { + id_ = id; + id_->pdata = this; + host_functions_ = host_functions; +} + +NPError PluginTest::New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved) { + test_name_ = this->GetArgValue("name", argc, argn, argv); + test_id_ = this->GetArgValue("id", argc, argn, argv); + return NPERR_NO_ERROR; +} + +NPError PluginTest::SetWindow(NPWindow* pNPWindow) { + return NPERR_NO_ERROR; +} + +// It's a shame I have to implement URLEncode. But, using webkit's +// or using chrome's means a ball of string of dlls and dependencies that +// is very very long. After spending far too much time on it, +// I'll just encode it myself. Too bad Microsoft doesn't implement +// this in a reusable way either. Both webkit and chrome will +// end up using libicu, which is a string of dependencies we don't +// want. + +inline BYTE toHex(const BYTE &x) { + return x > 9 ? x + 55: x + 48; +} + +std::string URLEncode(const std::string &sIn) { + std::string sOut; + + const size_t length = sIn.length(); + for (size_t idx = 0; idx < length;) { + const char ch = sIn.at(idx); + if (isalnum(ch)) { + sOut.append(1, ch); + } else if (isspace(ch) && ((ch != '\n') && (ch != '\r'))) { + sOut.append(1, '+'); + } else { + sOut.append(1, '%'); + sOut.append(1, toHex(ch>>4)); + sOut.append(1, toHex(ch%16)); + } + idx++; + } + return sOut; +} + +void PluginTest::SignalTestCompleted() { + // To signal test completion, we expect a couple of + // javascript functions to be defined in the webpage + // which hosts this plugin: + // onSuccess(test_name, test_id) + // onFailure(test_name, test_id, error_message) + std::string script_result; + std::string script_url; + if (Succeeded()) { + script_url.append("onSuccess(\""); + script_url.append(test_name_); + script_url.append("\",\""); + script_url.append(test_id_); + script_url.append("\");"); + } else { + script_url.append("onFailure(\""); + script_url.append(test_name_); + script_url.append("\",\""); + script_url.append(test_id_); + script_url.append("\",\""); + script_url.append(test_status_); + script_url.append("\");"); + } + script_url = URLEncode(script_url); + script_result.append("javascript:"); + script_result.append(script_url); + host_functions_->geturl(id_, script_result.c_str(), "_self"); +} + +const char *PluginTest::GetArgValue(const char *name, const int16 argc, + const char *argn[], const char *argv[]) { + for (int idx = 0; idx < argc; idx++) + if (_stricmp(argn[idx], name) == 0) + return argv[idx]; + return NULL; +} + +void PluginTest::SetError(const std::string &msg) { + test_status_.append(msg); +} + +NPError PluginTest::NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype) { + // There is no default action here. + return NPERR_NO_ERROR; +} + +int32 PluginTest::WriteReady(NPStream *stream) { + // Take data in small chunks + return 4096; +} + +int32 PluginTest::Write(NPStream *stream, int32 offset, int32 len, + void *buffer) { + // Pretend that we took all the data. + return len; +} + +NPError PluginTest::DestroyStream(NPStream *stream, NPError reason) { + // There is no default action. + return NPERR_NO_ERROR; +} + +void PluginTest::StreamAsFile(NPStream* stream, const char* fname) { + // There is no default action. +} + +void PluginTest::URLNotify(const char* url, NPReason reason, void* data) { + // There is no default action +} + +int16 PluginTest::HandleEvent(void* event) { + // There is no default action + return 0; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_test.h b/webkit/glue/plugins/test/plugin_test.h new file mode 100644 index 0000000..0cdc1b7 --- /dev/null +++ b/webkit/glue/plugins/test/plugin_test.h @@ -0,0 +1,153 @@ +// Copyright 2008, 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 WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__ +#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__ + +#include <string> + +#include "base/string_util.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "third_party/npapi/bindings/npapi.h" + +namespace NPAPIClient { + +// A PluginTest represents an instance of the plugin, which in +// our case is a test case. +class PluginTest { + public: + // Constructor. + PluginTest(NPP id, NPNetscapeFuncs *host_functions); + + // Destructor + virtual ~PluginTest() {} + + // + // NPAPI Functions + // + virtual NPError New(uint16 mode, int16 argc, const char* argn[], + const char* argv[], NPSavedData* saved); + virtual NPError SetWindow(NPWindow* pNPWindow); + virtual NPError NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16* stype); + virtual int32 WriteReady(NPStream *stream); + virtual int32 Write(NPStream *stream, int32 offset, int32 len, + void *buffer); + virtual NPError DestroyStream(NPStream *stream, NPError reason); + virtual void StreamAsFile(NPStream* stream, const char* fname); + virtual void URLNotify(const char* url, NPReason reason, void* data); + virtual int16 HandleEvent(void* event); + // Returns true if the test has not had any errors. + bool Succeeded() { return test_status_.length() == 0; } + + // Sets an error for the test case. Appends the msg to the + // error that will be returned from the test. + void SetError(const std::string &msg); + + // Expect two string values are equal, and if not, logs an + // appropriate error about it. + void ExpectStringLowerCaseEqual(const std::string &val1, const std::string &val2) { + if (!LowerCaseEqualsASCII(val1, val2.c_str())) { + std::string err; + err = "Expected Equal for '"; + err.append(val1); + err.append("' and '"); + err.append(val2); + err.append("'"); + SetError(err); + } + }; + + // Expect two values to not be equal, and if they are + // logs an appropriate error about it. + void ExpectAsciiStringNotEqual(const char *val1, const char *val2) { + if (val1 == val2) { + std::string err; + err = "Expected Not Equal for '"; + err.append(val1); + err.append("' and '"); + err.append(val2); + err.append("'"); + SetError(err); + } + } + // Expect two integer values are equal, and if not, logs an + // appropriate error about it. + void ExpectIntegerEqual(int val1, int val2) { + if (val1 != val2) { + std::string err; + char buf[64]; // what's the right size? + err = "Expected Equal for '"; + sprintf_s(buf, "%d", val1); + err.append(buf); + err.append("' and '"); + sprintf_s(buf, "%d", val2); + err.append(buf); + err.append("'"); + SetError(err); + } + } + + + protected: + // Signals to the Test that invoked us that the test is + // completed. This is done by forcing the plugin to + // set a cookie in the browser window, which the test program + // is waiting for. Note - because this is done by + // using javascript, the browser must have the frame + // setup before the plugin calls this function. So plugin + // tests MUST NOT call this function prior to having + // received the SetWindow() callback from the browser. + void SignalTestCompleted(); + + // Helper function to lookup names in the input array. + // If the name is found, returns the value, otherwise + // returns NULL. + const char *GetArgValue(const char *name, const int16 argc, + const char *argn[], const char *argv[]); + + // Access to the list of functions provided + // by the NPAPI host. + NPNetscapeFuncs *HostFunctions() { return host_functions_; } + + // The NPP Identifier for this plugin instance. + NPP id() { return id_; } + + private: + NPP id_; + NPNetscapeFuncs * host_functions_; + std::string test_name_; + std::string test_id_; + std::string test_status_; +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H__ + diff --git a/webkit/glue/plugins/test/plugin_window_size_test.cc b/webkit/glue/plugins/test/plugin_window_size_test.cc new file mode 100644 index 0000000..33aeafa --- /dev/null +++ b/webkit/glue/plugins/test/plugin_window_size_test.cc @@ -0,0 +1,67 @@ +// Copyright 2008, 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 "webkit/glue/plugins/test/plugin_window_size_test.h" +#include "webkit/glue/plugins/test/plugin_client.h" + +namespace NPAPIClient { + +PluginWindowSizeTest::PluginWindowSizeTest(NPP id, + NPNetscapeFuncs *host_functions) + : PluginTest(id, host_functions) { +} + +NPError PluginWindowSizeTest::SetWindow(NPWindow* pNPWindow) { + if (!pNPWindow || + !::IsWindow(reinterpret_cast<HWND>(pNPWindow->window))) { + SetError("Invalid arguments passed in"); + return NPERR_INVALID_PARAM; + } + + RECT window_rect = {0}; + window_rect.left = pNPWindow->x; + window_rect.top = pNPWindow->y; + window_rect.right = pNPWindow->width; + window_rect.bottom = pNPWindow->height; + + if (!::IsRectEmpty(&window_rect)) { + RECT client_rect = {0}; + ::GetClientRect(reinterpret_cast<HWND>(pNPWindow->window), + &client_rect); + if (::IsRectEmpty(&client_rect)) { + SetError("The client rect of the plugin window is empty. Test failed"); + } + + SignalTestCompleted(); + } + + return NPERR_NO_ERROR; +} + +} // namespace NPAPIClient diff --git a/webkit/glue/plugins/test/plugin_window_size_test.h b/webkit/glue/plugins/test/plugin_window_size_test.h new file mode 100644 index 0000000..13b50aa --- /dev/null +++ b/webkit/glue/plugins/test/plugin_window_size_test.h @@ -0,0 +1,50 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H +#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H + +#include "webkit/glue/plugins/test/plugin_test.h" + +namespace NPAPIClient { + +// This class tests whether the plugin window has a non zero rect +// on the second SetWindow call. +class PluginWindowSizeTest : public PluginTest { + public: + // Constructor. + PluginWindowSizeTest(NPP id, NPNetscapeFuncs *host_functions); + // NPAPI SetWindow handler + virtual NPError SetWindow(NPWindow* pNPWindow); +}; + +} // namespace NPAPIClient + +#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H + diff --git a/webkit/glue/plugins/test/resource.h b/webkit/glue/plugins/test/resource.h new file mode 100644 index 0000000..2f2bf3b --- /dev/null +++ b/webkit/glue/plugins/test/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by npapitest.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/webkit/glue/plugins/webplugin_delegate_impl.cc b/webkit/glue/plugins/webplugin_delegate_impl.cc new file mode 100644 index 0000000..774cad7 --- /dev/null +++ b/webkit/glue/plugins/webplugin_delegate_impl.cc @@ -0,0 +1,1081 @@ +// Copyright 2008, 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 "webkit/glue/plugins/webplugin_delegate_impl.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/gfx/point.h" +#include "base/stats_counters.h" +#include "webkit/default_plugin/plugin_impl.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/plugins/plugin_stream_url.h" + +static StatsCounter windowless_queue(L"Plugin.ThrottleQueue"); + +static const wchar_t kNativeWindowClassName[] = L"NativeWindowClass"; +static const wchar_t kWebPluginDelegateProperty[] = + L"WebPluginDelegateProperty"; +static const wchar_t kPluginNameAtomProperty[] = L"PluginNameAtom"; +static const wchar_t kDummyActivationWindowName[] = L"DummyWindowForActivation"; +static const wchar_t kPluginOrigProc[] = L"OriginalPtr"; +static const wchar_t kPluginFlashThrottle[] = L"FlashThrottle"; + +// The fastest we are willing to process WM_USER+1 events for Flash. +// Flash can easily exceed the limits of our CPU if we don't throttle it. +// The throttle has been chosen by testing various delays and compromising +// on acceptable Flash performance and reasonable CPU consumption. +// +// I'd like to make the throttle delay variable, based on the amount of +// time currently required to paint Flash plugins. There isn't a good +// way to count the time spent in aggregate plugin painting, however, so +// this seems to work well enough. +static const int kFlashWMUSERMessageThrottleDelayMs = 5; + +std::list<MSG> WebPluginDelegateImpl::throttle_queue_; + +WebPluginDelegateImpl* WebPluginDelegateImpl::current_plugin_instance_ = NULL; + +WebPluginDelegateImpl* WebPluginDelegateImpl::Create( + const std::wstring& filename, + const std::string& mime_type, + HWND containing_window) { + scoped_refptr<NPAPI::PluginLib> plugin = + NPAPI::PluginLib::CreatePluginLib(filename); + if (plugin.get() == NULL) + return NULL; + + NPError err = plugin->NP_Initialize(); + if (err != NPERR_NO_ERROR) + return NULL; + + scoped_refptr<NPAPI::PluginInstance> instance = + plugin->CreateInstance(mime_type); + return new WebPluginDelegateImpl(containing_window, instance.get()); +} + +bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) { + // We use a buffer that is one char longer than we need to detect cases where + // kNativeWindowClassName is a prefix of the given window's class name. It + // happens that GetClassNameW will just silently truncate the class name to + // fit into the given buffer. + wchar_t class_name[arraysize(kNativeWindowClassName) + 1]; + if (!GetClassNameW(window, class_name, arraysize(class_name))) + return false; + return wcscmp(class_name, kNativeWindowClassName) == 0; +} + +bool WebPluginDelegateImpl::GetPluginNameFromWindow( + HWND window, std::wstring *plugin_name) { + if (NULL == plugin_name) { + return false; + } + if (!IsPluginDelegateWindow(window)) { + return false; + } + ATOM plugin_name_atom = reinterpret_cast<ATOM>( + GetPropW(window, kPluginNameAtomProperty)); + if (plugin_name_atom != 0) { + WCHAR plugin_name_local[MAX_PATH] = {0}; + GlobalGetAtomNameW(plugin_name_atom, + plugin_name_local, + ARRAYSIZE(plugin_name_local)); + *plugin_name = plugin_name_local; + return true; + } + return false; +} + +bool WebPluginDelegateImpl::IsDummyActivationWindow(HWND window) { + if (!IsWindow(window)) + return false; + + wchar_t window_title[MAX_PATH + 1] = {0}; + if (GetWindowText(window, window_title, arraysize(window_title))) { + return (0 == lstrcmpiW(window_title, kDummyActivationWindowName)); + } + return false; +} + +LRESULT CALLBACK WebPluginDelegateImpl::HandleEventMessageFilterHook( + int code, WPARAM wParam, LPARAM lParam) { + + DCHECK(current_plugin_instance_); + current_plugin_instance_->OnModalLoopEntered(); + return CallNextHookEx(NULL, code, wParam, lParam); +} + +WebPluginDelegateImpl::WebPluginDelegateImpl( + HWND containing_window, + NPAPI::PluginInstance *instance) + : parent_(containing_window), + instance_(instance), + quirks_(0), + plugin_(NULL), + windowless_(false), + windowed_handle_(NULL), + windowed_did_set_window_(false), + windowless_needs_set_window_(true), + plugin_wnd_proc_(NULL), + last_message_(0), + is_calling_wndproc(false), + initial_plugin_resize_done_(false), + dummy_window_for_activation_(NULL), + handle_event_message_filter_hook_(NULL), + handle_event_pump_messages_event_(NULL), + handle_event_depth_(0), + user_gesture_message_posted_(false), +#pragma warning(suppress: 4355) // can use this + user_gesture_msg_factory_(this), + load_manually_(false), + first_geometry_update_(true) { + memset(&window_, 0, sizeof(window_)); + + const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info(); + std::wstring filename = file_util::GetFilenameFromPath(plugin_info.file); + + if (instance_->mime_type() == "application/x-shockwave-flash" || + filename == L"npswf32.dll") { + // Flash only requests windowless plugins if we return a Mozilla user + // agent. + instance_->set_use_mozilla_user_agent(); + instance_->set_throttle_invalidate(true); + quirks_ |= PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE; + } else if (plugin_info.name.find(L"Windows Media Player") != + std::wstring::npos) { + // Windows Media Player needs two NPP_SetWindow calls. + quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE; + } else if (instance_->mime_type() == "audio/x-pn-realaudio-plugin" || + filename == L"nppl3260.dll") { + quirks_ |= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY; + } else if (plugin_info.name.find(L"VLC Multimedia Plugin") != + std::wstring::npos) { + // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window + // handle + quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY; + // VLC 0.8.6d and 0.8.6e crash if multiple instances are created. + quirks_ |= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES; + } +} + +WebPluginDelegateImpl::~WebPluginDelegateImpl() { + if (::IsWindow(dummy_window_for_activation_)) { + ::DestroyWindow(dummy_window_for_activation_); + } + + DestroyInstance(); + + if (!windowless_) + WindowedDestroyWindow(); + + if (handle_event_pump_messages_event_) { + CloseHandle(handle_event_pump_messages_event_); + } +} + +void WebPluginDelegateImpl::PluginDestroyed() { + delete this; +} + +bool WebPluginDelegateImpl::Initialize(const GURL& url, + char** argn, + char** argv, + int argc, + WebPlugin* plugin, + bool load_manually) { + plugin_ = plugin; + + instance_->set_web_plugin(plugin); + NPAPI::PluginInstance* old_instance = + NPAPI::PluginInstance::SetInitializingInstance(instance_); + + if (quirks_ & PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES) { + NPAPI::PluginLib* plugin_lib = instance()->plugin_lib(); + if (plugin_lib->instance_count() > 1) { + return false; + } + } + + bool start_result = instance_->Start(url, argn, argv, argc, load_manually); + + NPAPI::PluginInstance::SetInitializingInstance(old_instance); + + if (!start_result) + return false; + + windowless_ = instance_->windowless(); + if (windowless_) { + // For windowless plugins we should set the containing window handle + // as the instance window handle. This is what Safari does. Not having + // a valid window handle causes subtle bugs with plugins which retreive + // the window handle and validate the same. The window handle can be + // retreived via NPN_GetValue of NPNVnetscapeWindow. + instance_->set_window_handle(parent_); + CreateDummyWindowForActivation(); + handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); + } else { + if (!WindowedCreatePlugin()) + return false; + } + + plugin->SetWindow(windowed_handle_, handle_event_pump_messages_event_); + + load_manually_ = load_manually; + plugin_url_ = url.spec(); + return true; +} + +void WebPluginDelegateImpl::DestroyInstance() { + if (instance_ && (instance_->npp()->ndata != NULL)) { + // Shutdown all streams before destroying so that + // no streams are left "in progress". Need to do + // this before calling set_web_plugin(NULL) because the + // instance uses the helper to do the download. + instance_->CloseStreams(); + + window_.window = NULL; + if (!(quirks_ & PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY)) { + instance_->NPP_SetWindow(&window_); + } + + instance_->NPP_Destroy(); + + instance_->set_web_plugin(NULL); + + instance_ = 0; + } +} + +void WebPluginDelegateImpl::UpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + if (windowless_) { + window_.window = + reinterpret_cast<void *>(::GetDC(instance_->window_handle())); + WindowlessUpdateGeometry(window_rect, clip_rect); + ::ReleaseDC(instance_->window_handle(), + reinterpret_cast<HDC>(window_.window)); + } else { + WindowedUpdateGeometry(window_rect, clip_rect, visible); + } + + // Initiate a download on the plugin url. This should be done for the + // first update geometry sequence. + if (first_geometry_update_) { + first_geometry_update_ = false; + // An empty url corresponds to an EMBED tag with no src attribute. + if (!load_manually_ && !plugin_url_.empty()) { + instance_->SendStream(plugin_url_.c_str(), false, NULL); + } + } +} + +void WebPluginDelegateImpl::Paint(HDC hdc, const gfx::Rect& rect) { + if (windowless_) + WindowlessPaint(hdc, rect); +} + +void WebPluginDelegateImpl::Print(HDC hdc) { + // Disabling the call to NPP_Print as it causes a crash in + // flash in some cases. In any case this does not work as expected + // as the EMF meta file dc passed in needs to be created with the + // the plugin window dc as its sibling dc and the window rect + // in .01 mm units. +#if 0 + NPPrint npprint; + npprint.mode = NP_EMBED; + npprint.print.embedPrint.platformPrint = reinterpret_cast<void*>(hdc); + npprint.print.embedPrint.window = window_; + instance()->NPP_Print(&npprint); +#endif +} + +NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() { + return instance_->GetPluginScriptableObject(); +} + +void WebPluginDelegateImpl::DidFinishLoadWithReason(NPReason reason) { + instance()->DidFinishLoadWithReason(reason); +} + +int WebPluginDelegateImpl::GetProcessId() { + // We are in process, so the plugin pid is this current process pid. + return ::GetCurrentProcessId(); +} + +HWND WebPluginDelegateImpl::GetWindowHandle() { + return instance()->window_handle(); +} + +void WebPluginDelegateImpl::SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + instance()->SendJavaScriptStream(url, result, success, notify_needed, + notify_data); +} + +void WebPluginDelegateImpl::DidReceiveManualResponse( + const std::string& url, const std::string& mime_type, + const std::string& headers, uint32 expected_length, uint32 last_modified) { + instance()->DidReceiveManualResponse(url, mime_type, headers, + expected_length, last_modified); +} + +void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer, + int length) { + instance()->DidReceiveManualData(buffer, length); +} + +void WebPluginDelegateImpl::DidFinishManualLoading() { + instance()->DidFinishManualLoading(); +} + +void WebPluginDelegateImpl::DidManualLoadFail() { + instance()->DidManualLoadFail(); +} + +std::wstring WebPluginDelegateImpl::GetPluginPath() { + return instance()->plugin_lib()->plugin_info().file; +} + +void WebPluginDelegateImpl::InstallMissingPlugin() { + NPEvent evt; + evt.event = PluginInstallerImpl::kInstallMissingPluginMessage; + evt.lParam = 0; + evt.wParam = 0; + instance()->NPP_HandleEvent(&evt); +} + +void WebPluginDelegateImpl::WindowedUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + if (WindowedReposition(window_rect, clip_rect, visible) || + !windowed_did_set_window_) { + // Let the plugin know that it has been moved + WindowedSetWindow(); + } +} + +bool WebPluginDelegateImpl::WindowedCreatePlugin() { + DCHECK(!windowed_handle_); + + RegisterNativeWindowClass(); + + // The window will be sized and shown later. + windowed_handle_ = CreateWindowEx( + WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, + kNativeWindowClassName, + 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + 0, + 0, + 0, + 0, + parent_, + 0, + GetModuleHandle(NULL), + 0); + if (windowed_handle_ == 0) + return false; + + BOOL result = SetProp(windowed_handle_, kWebPluginDelegateProperty, this); + DCHECK(result == TRUE) << "SetProp failed, last error = " << GetLastError(); + // Get the name of the plugin, create an atom and set that in a window + // property. Use an atom so that other processes can access the name of + // the plugin that this window is hosting + if (instance_ != NULL) { + NPAPI::PluginLib* plugin_lib = instance()->plugin_lib(); + if (plugin_lib != NULL) { + std::wstring plugin_name = plugin_lib->plugin_info().name; + if (!plugin_name.empty()) { + ATOM plugin_name_atom = GlobalAddAtomW(plugin_name.c_str()); + DCHECK(0 != plugin_name_atom); + result = SetProp(windowed_handle_, + kPluginNameAtomProperty, + reinterpret_cast<HANDLE>(plugin_name_atom)); + DCHECK(result == TRUE) << "SetProp failed, last error = " << + GetLastError(); + } + } + } + + // Calling SetWindowLongPtrA here makes the window proc ASCII, which is + // required by at least the Shockwave Director plug-in. + SetWindowLongPtrA( + windowed_handle_, GWL_WNDPROC, reinterpret_cast<LONG>(DefWindowProcA)); + + return true; +} + +void WebPluginDelegateImpl::WindowedDestroyWindow() { + if (windowed_handle_ != NULL) { + // Unsubclass the window. + WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc == NativeWndProc) { + SetWindowLongPtr(windowed_handle_, + GWLP_WNDPROC, + reinterpret_cast<LONG>(plugin_wnd_proc_)); + } + + DestroyWindow(windowed_handle_); + windowed_handle_ = 0; + } +} + +// Erase all messages in the queue destined for a particular window. +// When windows are closing, callers should use this function to clear +// the queue. +// static +void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window) { + std::list<MSG>::iterator it; + for (it = throttle_queue_.begin(); it != throttle_queue_.end(); ) { + if (it->hwnd == window) { + it = throttle_queue_.erase(it); + windowless_queue.Decrement(); + } else { + it++; + } + } +} + +// Delayed callback for processing throttled messages. +// Throttled messages are aggregated globally across all plugins. +// static +void WebPluginDelegateImpl::OnThrottleMessage() { + // The current algorithm walks the list and processes the first + // message it finds for each plugin. It is important to service + // all active plugins with each pass through the throttle, otherwise + // we see video jankiness. + std::map<HWND, int> processed; + + std::list<MSG>::iterator it = throttle_queue_.begin(); + while (it != throttle_queue_.end()) { + const MSG& msg = *it; + if (processed.find(msg.hwnd) == processed.end()) { + WNDPROC proc = reinterpret_cast<WNDPROC>(msg.time); + CallWindowProc(proc, msg.hwnd, msg.message, msg.wParam, msg.lParam); + processed[msg.hwnd] = 1; + it = throttle_queue_.erase(it); + windowless_queue.Decrement(); + } else { + it++; + } + } + + if (throttle_queue_.size() > 0) + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage), + kFlashWMUSERMessageThrottleDelayMs); +} + +// Schedule a windows message for delivery later. +// static +void WebPluginDelegateImpl::ThrottleMessage(WNDPROC proc, HWND hwnd, + UINT message, WPARAM wParam, + LPARAM lParam) { + MSG msg; + msg.time = reinterpret_cast<DWORD>(proc); + msg.hwnd = hwnd; + msg.message = message; + msg.wParam = wParam; + msg.lParam = lParam; + throttle_queue_.push_back(msg); + windowless_queue.Increment(); + + if (throttle_queue_.size() == 1) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage), + kFlashWMUSERMessageThrottleDelayMs); + } +} + +// We go out of our way to find the hidden windows created by Flash for +// windowless plugins. We throttle the rate at which they deliver messages +// so that they will not consume outrageous amounts of CPU. +// static +LRESULT CALLBACK WebPluginDelegateImpl::FlashWindowlessWndProc(HWND hwnd, + UINT message, WPARAM wparam, LPARAM lparam) { + WNDPROC old_proc = reinterpret_cast<WNDPROC>(GetProp(hwnd, kPluginOrigProc)); + DCHECK(old_proc); + + switch (message) { + case WM_NCDESTROY: { + WebPluginDelegateImpl::ClearThrottleQueueForWindow(hwnd); + break; + } + // Flash may flood the message queue with WM_USER+1 message causing 100% CPU + // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We + // prevent this by throttling the messages. + case WM_USER + 1: { + WebPluginDelegateImpl::ThrottleMessage(old_proc, hwnd, message, wparam, + lparam); + return TRUE; + } + default: { + break; + } + } + return CallWindowProc(old_proc, hwnd, message, wparam, lparam); +} + +// Callback for enumerating the Flash windows. +BOOL CALLBACK EnumFlashWindows(HWND window, LPARAM arg) { + WNDPROC wnd_proc = reinterpret_cast<WNDPROC>(arg); + TCHAR class_name[1024]; + if (!RealGetWindowClass(window, class_name, + sizeof(class_name)/sizeof(TCHAR))) { + LOG(ERROR) << "RealGetWindowClass failure: " << GetLastError(); + return FALSE; + } + + if (wcscmp(class_name, L"SWFlash_PlaceholderX")) + return TRUE; + + WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( + GetWindowLongPtr(window, GWLP_WNDPROC)); + if (current_wnd_proc != wnd_proc) { + WNDPROC old_flash_proc = reinterpret_cast<WNDPROC>(SetWindowLongPtr( + window, GWLP_WNDPROC, + reinterpret_cast<LONG>(wnd_proc))); + DCHECK(old_flash_proc); + BOOL result = SetProp(window, kPluginOrigProc, old_flash_proc); + if (!result) { + LOG(ERROR) << "SetProp failed, last error = " << GetLastError(); + return FALSE; + } + } + + return TRUE; +} + +bool WebPluginDelegateImpl::CreateDummyWindowForActivation() { + DCHECK(!dummy_window_for_activation_); + dummy_window_for_activation_ = CreateWindowEx( + 0, + L"Static", + kDummyActivationWindowName, + WS_CHILD, + 0, + 0, + 0, + 0, + parent_, + 0, + GetModuleHandle(NULL), + 0); + + if (dummy_window_for_activation_ == 0) + return false; + + // Flash creates background windows which use excessive CPU in our + // environment; we wrap these windows and throttle them so that they don't + // get out of hand. + if (!EnumThreadWindows(::GetCurrentThreadId(), EnumFlashWindows, + reinterpret_cast<LPARAM>( + &WebPluginDelegateImpl::FlashWindowlessWndProc))) { + // Log that this happened. Flash will still work; it just means the + // throttle isn't installed (and Flash will use more CPU). + NOTREACHED(); + LOG(ERROR) << "Failed to wrap all windowless Flash windows"; + } + return true; +} + +void WebPluginDelegateImpl::MoveWindow(HWND window, + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + HRGN hrgn = ::CreateRectRgn(clip_rect.x(), + clip_rect.y(), + clip_rect.right(), + clip_rect.bottom()); + + // Note: System will own the hrgn after we call SetWindowRgn, + // so we don't need to call DeleteObject(hrgn) + ::SetWindowRgn(window, hrgn, FALSE); + + // Move the window now, but do not paint it. We expect to be painted later, + // when our parent window paints. + unsigned long flags = SWP_NOREDRAW; + if (visible) + flags |= SWP_SHOWWINDOW; + else + flags |= SWP_HIDEWINDOW; + + ::SetWindowPos(window, + NULL, + window_rect.x(), + window_rect.y(), + window_rect.width(), + window_rect.height(), + flags); +} + +bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + if (!windowed_handle_) { + NOTREACHED(); + return false; + } + + if (window_rect_ == window_rect && clip_rect_ == clip_rect && + initial_plugin_resize_done_) + return false; + + window_rect_ = window_rect; + clip_rect_ = clip_rect; + + if (!initial_plugin_resize_done_) { + // We need to ensure that the plugin process continues to reposition + // the plugin window until we receive an indication that it is now visible. + // Subsequent repositions will be done by the browser. + if (visible) + initial_plugin_resize_done_ = true; + // We created the window with 0 width and height since we didn't know it + // at the time. Now that we know the geometry, we we can update its size + // since the browser only calls SetWindowPos when scrolling occurs. + MoveWindow(windowed_handle_, window_rect, clip_rect, visible); + // Ensure that the entire window gets repainted. + ::InvalidateRect(windowed_handle_, NULL, FALSE); + } + + return true; +} + +void WebPluginDelegateImpl::WindowedSetWindow() { + if (!instance_) + return; + + if (!windowed_handle_) { + NOTREACHED(); + return; + } + + instance()->set_window_handle(windowed_handle_); + + DCHECK(!instance()->windowless()); + + window_.clipRect.top = clip_rect_.y(); + window_.clipRect.left = clip_rect_.x(); + window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); + window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); + window_.height = window_rect_.height(); + window_.width = window_rect_.width(); + window_.x = window_rect_.x(); + window_.y = window_rect_.y(); + + window_.window = windowed_handle_; + window_.type = NPWindowTypeWindow; + + // Reset this flag before entering the instance in case of side-effects. + windowed_did_set_window_ = true; + + NPError err = instance()->NPP_SetWindow(&window_); + if (quirks_ & PLUGIN_QUIRK_SETWINDOW_TWICE) + instance()->NPP_SetWindow(&window_); + + WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( + GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); + if (current_wnd_proc != NativeWndProc) { + plugin_wnd_proc_ = reinterpret_cast<WNDPROC>(SetWindowLongPtr( + windowed_handle_, GWLP_WNDPROC, reinterpret_cast<LONG>(NativeWndProc))); + } +} + +ATOM WebPluginDelegateImpl::RegisterNativeWindowClass() { + static bool have_registered_window_class = false; + if (have_registered_window_class == true) + return true; + + have_registered_window_class = true; + + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_DBLCLKS; + wcex.lpfnWndProc = DefWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetModuleHandle(NULL); + wcex.hIcon = 0; + wcex.hCursor = 0; + // Some plugins like windows media player 11 create child windows parented + // by our plugin window, where the media content is rendered. These plugins + // dont implement WM_ERASEBKGND, which causes painting issues, when the + // window where the media is rendered is moved around. DefWindowProc does + // implement WM_ERASEBKGND correctly if we have a valid background brush. + wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); + wcex.lpszMenuName = 0; + wcex.lpszClassName = kNativeWindowClassName; + wcex.hIconSm = 0; + + return RegisterClassEx(&wcex); +} + +LRESULT CALLBACK WebPluginDelegateImpl::NativeWndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + WebPluginDelegateImpl* delegate = reinterpret_cast<WebPluginDelegateImpl*>( + GetProp(hwnd, kWebPluginDelegateProperty)); + if (!delegate) { + NOTREACHED(); + return 0; + } + + if (message == delegate->last_message_ && + delegate->quirks() & PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY && + delegate->is_calling_wndproc) { + // Real may go into a state where it recursively dispatches the same event + // when subclassed. See https://bugzilla.mozilla.org/show_bug.cgi?id=192914 + // We only do the recursive check for Real because it's possible and valid + // for a plugin to synchronously dispatch a message to itself such that it + // looks like it's in recursion. + return TRUE; + } + + switch (message) { + case WM_NCDESTROY: { + RemoveProp(hwnd, kWebPluginDelegateProperty); + ATOM plugin_name_atom = reinterpret_cast <ATOM>( + RemoveProp(hwnd, kPluginNameAtomProperty)); + if (plugin_name_atom != 0) + GlobalDeleteAtom(plugin_name_atom); + ClearThrottleQueueForWindow(hwnd); + break; + } + // Flash may flood the message queue with WM_USER+1 message causing 100% CPU + // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We + // prevent this by throttling the messages. + case WM_USER + 1: { + if (delegate->quirks() & PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE) { + WebPluginDelegateImpl::ThrottleMessage(delegate->plugin_wnd_proc_, hwnd, + message, wparam, lparam); + return FALSE; + } + break; + } + default: { + break; + } + } + + delegate->last_message_ = message; + delegate->is_calling_wndproc = true; + + if (!delegate->user_gesture_message_posted_ && + IsUserGestureMessage(message)) { + delegate->user_gesture_message_posted_ = true; + + delegate->instance()->PushPopupsEnabledState(true); + + MessageLoop::current()->PostTask(FROM_HERE, + delegate->user_gesture_msg_factory_.NewRunnableMethod( + &WebPluginDelegateImpl::OnUserGestureEnd)); + } + + LRESULT result = CallWindowProc(delegate->plugin_wnd_proc_, hwnd, message, + wparam, lparam); + delegate->is_calling_wndproc = false; + return result; +} + +void WebPluginDelegateImpl::WindowlessUpdateGeometry( + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect) { + // Only resend to the instance if the geometry has changed. + if (window_rect == window_rect_ && clip_rect == clip_rect_) + return; + + // Set this flag before entering the instance in case of side-effects. + windowless_needs_set_window_ = true; + + // We will inform the instance of this change when we call NPP_SetWindow. + clip_rect_ = clip_rect; + + if (window_rect_ != window_rect) { + window_rect_ = window_rect; + + WindowlessSetWindow(true); + + WINDOWPOS win_pos = {0}; + win_pos.x = window_rect_.x(); + win_pos.y = window_rect_.y(); + win_pos.cx = window_rect_.width(); + win_pos.cy = window_rect_.height(); + + NPEvent pos_changed_event; + pos_changed_event.event = WM_WINDOWPOSCHANGED; + pos_changed_event.wParam = 0; + pos_changed_event.lParam = PtrToUlong(&win_pos); + + instance()->NPP_HandleEvent(&pos_changed_event); + } +} + +void WebPluginDelegateImpl::WindowlessPaint(HDC hdc, + const gfx::Rect& damage_rect) { + DCHECK(hdc); + + RECT damage_rect_win; + damage_rect_win.left = damage_rect.x(); // + window_rect_.x(); + damage_rect_win.top = damage_rect.y(); // + window_rect_.y(); + damage_rect_win.right = damage_rect_win.left + damage_rect.width(); + damage_rect_win.bottom = damage_rect_win.top + damage_rect.height(); + + window_.window = (void*)hdc; + + // TODO(darin): we should avoid calling NPP_SetWindow here since it may + // cause page layout to be invalidated. + + // We really don't need to continually call SetWindow. + // m_needsSetWindow flags when the geometry has changed. + if (windowless_needs_set_window_) + WindowlessSetWindow(false); + + NPEvent paint_event; + paint_event.event = WM_PAINT; + // NOTE: NPAPI is not 64bit safe. It puts pointers into 32bit values. + paint_event.wParam = PtrToUlong(hdc); + paint_event.lParam = PtrToUlong(&damage_rect_win); + static StatsRate plugin_paint(L"Plugin.Paint"); + StatsScope<StatsRate> scope(plugin_paint); + instance()->NPP_HandleEvent(&paint_event); +} + +void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { + if (!instance()) + return; + + if (window_rect_.IsEmpty()) // wait for geometry to be set. + return; + + DCHECK(instance()->windowless()); + + window_.clipRect.top = clip_rect_.y(); + window_.clipRect.left = clip_rect_.x(); + window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); + window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); + window_.height = window_rect_.height(); + window_.width = window_rect_.width(); + window_.x = window_rect_.x(); + window_.y = window_rect_.y(); + window_.type = NPWindowTypeDrawable; + + if (!force_set_window) + // Reset this flag before entering the instance in case of side-effects. + windowless_needs_set_window_ = false; + + NPError err = instance()->NPP_SetWindow(&window_); + DCHECK(err == NPERR_NO_ERROR); +} + +void WebPluginDelegateImpl::SetFocus() { + DCHECK(instance()->windowless()); + + NPEvent focus_event; + focus_event.event = WM_SETFOCUS; + focus_event.wParam = 0; + focus_event.lParam = 0; + + instance()->NPP_HandleEvent(&focus_event); +} + +bool WebPluginDelegateImpl::HandleEvent(NPEvent* event, + WebCursor* cursor) { + DCHECK(windowless_) << "events should only be received in windowless mode"; + DCHECK(cursor != NULL); + + // To ensure that the plugin receives keyboard events we set focus to the + // dummy window. + // TODO(iyengar) We need a framework in the renderer to identify which + // windowless plugin is under the mouse and to handle this. This would + // also require some changes in RenderWidgetHost to detect this in the + // WM_MOUSEACTIVATE handler and inform the renderer accordingly. + HWND prev_focus_window = NULL; + if (event->event == WM_RBUTTONDOWN) { + prev_focus_window = ::SetFocus(dummy_window_for_activation_); + } + + if (ShouldTrackEventForModalLoops(event)) { + // A windowless plugin can enter a modal loop in a NPP_HandleEvent call. + // For e.g. Flash puts up a context menu when we right click on the + // windowless plugin area. We detect this by setting up a message filter + // hook pror to calling NPP_HandleEvent on the plugin and unhook on + // return from NPP_HandleEvent. If the plugin does enter a modal loop + // in that context we unhook on receiving the first notification in + // the message filter hook. + handle_event_message_filter_hook_ = + SetWindowsHookEx(WH_MSGFILTER, HandleEventMessageFilterHook, NULL, + GetCurrentThreadId()); + } + + bool old_task_reentrancy_state = + MessageLoop::current()->NestableTasksAllowed(); + + current_plugin_instance_ = this; + + handle_event_depth_++; + + bool pop_user_gesture = false; + + if (IsUserGestureMessage(event->event)) { + pop_user_gesture = true; + instance()->PushPopupsEnabledState(true); + } + + bool ret = instance()->NPP_HandleEvent(event) != 0; + + if (pop_user_gesture) { + instance()->PopPopupsEnabledState(); + } + + handle_event_depth_--; + + current_plugin_instance_ = NULL; + + MessageLoop::current()->SetNestableTasksAllowed(old_task_reentrancy_state); + + if (handle_event_message_filter_hook_) { + UnhookWindowsHookEx(handle_event_message_filter_hook_); + handle_event_message_filter_hook_ = NULL; + } + + // We could have multiple NPP_HandleEvent calls nested together in case + // the plugin enters a modal loop. Reset the pump messages event when + // the outermost NPP_HandleEvent call unwinds. + if (handle_event_depth_ == 0) { + ResetEvent(handle_event_pump_messages_event_); + } + + if (::IsWindow(prev_focus_window)) { + ::SetFocus(prev_focus_window); + } + + if (WM_MOUSEMOVE == event->event) { + HCURSOR actual_cursor = ::GetCursor(); + *cursor = GetCursorType(actual_cursor); + } + + return ret; +} + +WebCursor::Type WebPluginDelegateImpl::GetCursorType( + HCURSOR cursor) const { + static HCURSOR standard_cursors[] = { + LoadCursor(NULL, IDC_ARROW), + LoadCursor(NULL, IDC_IBEAM), + LoadCursor(NULL, IDC_WAIT), + LoadCursor(NULL, IDC_CROSS), + LoadCursor(NULL, IDC_UPARROW), + LoadCursor(NULL, IDC_SIZE), + LoadCursor(NULL, IDC_ICON), + LoadCursor(NULL, IDC_SIZENWSE), + LoadCursor(NULL, IDC_SIZENESW), + LoadCursor(NULL, IDC_SIZEWE), + LoadCursor(NULL, IDC_SIZENS), + LoadCursor(NULL, IDC_SIZEALL), + LoadCursor(NULL, IDC_NO), + LoadCursor(NULL, IDC_HAND), + LoadCursor(NULL, IDC_APPSTARTING), + LoadCursor(NULL, IDC_HELP), + }; + + for (int cursor_index = 0; cursor_index < arraysize(standard_cursors); + cursor_index++) { + if (cursor == standard_cursors[cursor_index]) + return static_cast<WebCursor::Type>(cursor_index); + } + + return WebCursor::ARROW; +} + +WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient( + int resource_id, const std::string &url, bool notify_needed, + void *notify_data) { + if (notify_needed) { + instance()->SetURLLoadData(GURL(url.c_str()), notify_data); + } + std::string mime_type; + NPAPI::PluginStreamUrl *stream = instance()->CreateStream(resource_id, + url, + mime_type, + notify_needed, + notify_data); + return stream; +} + +void WebPluginDelegateImpl::URLRequestRouted(const std::string&url, + bool notify_needed, + void* notify_data) { + if (notify_needed) { + instance()->SetURLLoadData(GURL(url.c_str()), notify_data); + } +} + +void WebPluginDelegateImpl::OnModalLoopEntered() { + DCHECK(handle_event_pump_messages_event_ != NULL); + SetEvent(handle_event_pump_messages_event_); + + MessageLoop::current()->SetNestableTasksAllowed(true); + + UnhookWindowsHookEx(handle_event_message_filter_hook_); + handle_event_message_filter_hook_ = NULL; +} + +bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent* event) { + if (event->event == WM_RBUTTONDOWN) + return true; + return false; +} + +bool WebPluginDelegateImpl::IsUserGestureMessage(unsigned int message) { + switch (message) { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_KEYUP: + return true; + + default: + break; + } + + return false; +} + +void WebPluginDelegateImpl::OnUserGestureEnd() { + user_gesture_message_posted_ = false; + instance()->PopPopupsEnabledState(); +} diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h new file mode 100644 index 0000000..3b774e8 --- /dev/null +++ b/webkit/glue/plugins/webplugin_delegate_impl.h @@ -0,0 +1,287 @@ +// Copyright 2008, 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 WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ +#define WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ + +#include <string> +#include <list> + +#include "base/ref_counted.h" +#include "base/task.h" +#include "webkit/glue/webplugin_delegate.h" +#include "third_party/npapi/bindings/npapi.h" +#include "webkit/glue/webcursor.h" + +namespace NPAPI { + class PluginInstance; +}; + +// An implementation of WebPluginDelegate that proxies all calls to +// the plugin process. +class WebPluginDelegateImpl : public WebPluginDelegate { + public: + static WebPluginDelegateImpl* Create(const std::wstring& filename, + const std::string& mime_type, + HWND containing_window); + static bool IsPluginDelegateWindow(HWND window); + static bool GetPluginNameFromWindow(HWND window, std::wstring *plugin_name); + + // Returns true if the window handle passed in is that of the dummy + // activation window for windowless plugins. + static bool IsDummyActivationWindow(HWND window); + + // WebPluginDelegate implementation + virtual void PluginDestroyed(); + virtual bool Initialize(const GURL& url, + char** argn, + char** argv, + int argc, + WebPlugin* plugin, + bool load_manually); + virtual void UpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + virtual void Paint(HDC hdc, const gfx::Rect& rect); + virtual void Print(HDC hdc); + virtual void SetFocus(); // only called when windowless +// only called when windowless + virtual bool HandleEvent(NPEvent* event, + WebCursor* cursor); + virtual NPObject* GetPluginScriptableObject(); + virtual void DidFinishLoadWithReason(NPReason reason); + virtual int GetProcessId(); + virtual HWND GetWindowHandle(); + + virtual void FlushGeometryUpdates() { + } + virtual void SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, bool notify_needed, + int notify_data); + virtual void DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified); + virtual void DidReceiveManualData(const char* buffer, int length); + virtual void DidFinishManualLoading(); + virtual void DidManualLoadFail(); + virtual std::wstring GetPluginPath(); + virtual void InstallMissingPlugin(); + virtual WebPluginResourceClient* CreateResourceClient(int resource_id, + const std::string &url, + bool notify_needed, + void *notify_data); + + virtual void URLRequestRouted(const std::string&url, bool notify_needed, + void* notify_data); + bool windowless() const { + return windowless_; + } + + enum PluginQuirks { + PLUGIN_QUIRK_SETWINDOW_TWICE = 1, + PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE = 2, + PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY = 4, + PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY = 8, + PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES = 16, + }; + + int quirks() { return quirks_; } + + static void MoveWindow(HWND window, + const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible); + + private: + WebPluginDelegateImpl(HWND containing_window, + NPAPI::PluginInstance *instance); + ~WebPluginDelegateImpl(); + + //-------------------------- + // used for windowed plugins + void WindowedUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + // Create the native window. + // Returns true if the window is created (or already exists). + // Returns false if unable to create the window. + bool WindowedCreatePlugin(); + + // Destroy the native window. + void WindowedDestroyWindow(); + + // Reposition the native window to be in sync with the given geometry. + // Returns true if the native window has moved or been clipped differently. + bool WindowedReposition(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + + // Tells the plugin about the current state of the window. + // See NPAPI NPP_SetWindow for more information. + void WindowedSetWindow(); + + // Registers the window class for our window + ATOM RegisterNativeWindowClass(); + + // Our WndProc functions. + static LRESULT CALLBACK NativeWndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + static LRESULT CALLBACK FlashWindowlessWndProc( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + + // Used for throttling Flash messages. + static void ClearThrottleQueueForWindow(HWND window); + static void OnThrottleMessage(); + static void ThrottleMessage(WNDPROC proc, HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); + + //---------------------------- + // used for windowless plugins + void WindowlessUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect); + void WindowlessPaint(HDC hdc, const gfx::Rect& rect); + + // Tells the plugin about the current state of the window. + // See NPAPI NPP_SetWindow for more information. + void WindowlessSetWindow(bool force_set_window); + + + //----------------------------------------- + // used for windowed and windowless plugins + + NPAPI::PluginInstance* instance() { return instance_.get(); } + + // Closes down and destroys our plugin instance. + void DestroyInstance(); + + // Returns the cursor type. + // TODO(iyengar) Add support for custom cursors. + WebCursor::Type GetCursorType(HCURSOR cursor) const; + + // used for windowed plugins + HWND windowed_handle_; + bool windowed_did_set_window_; + gfx::Rect windowed_last_pos_; + + // this is an optimization to avoid calling SetWindow to the plugin + // when it is not necessary. Initially, we need to call SetWindow, + // and after that we only need to call it when the geometry changes. + // use this flag to indicate whether we really need it or not. + bool windowless_needs_set_window_; + + // used by windowed and windowless plugins + bool windowless_; + + WebPlugin* plugin_; + scoped_refptr<NPAPI::PluginInstance> instance_; + + // Original wndproc before we subclassed. + WNDPROC plugin_wnd_proc_; + + // Used to throttle WM_USER+1 messages in Flash. + uint32 last_message_; + bool is_calling_wndproc; + + HWND parent_; + NPWindow window_; + gfx::Rect window_rect_; + gfx::Rect clip_rect_; + int quirks_; + + // We only move/size the plugin window once after its creation. The + // rest of the moves are controlled by the browser. This flag controls + // this behaviour. + bool initial_plugin_resize_done_; + + // Windowless plugins don't have keyboard focus causing issues with the + // plugin not receiving keyboard events if the plugin enters a modal + // loop like TrackPopupMenuEx or MessageBox, etc. + // This is a basic issue with windows activation and focus arising due to + // the fact that these windows are created by different threads. Activation + // and focus are thread specific states, and if the browser has focus, + // the plugin may not have focus. + // To fix a majority of these activation issues we create a dummy visible + // child window to which we set focus whenever the windowless plugin + // receives a WM_LBUTTONDOWN/WM_RBUTTONDOWN message via NPP_HandleEvent. + HWND dummy_window_for_activation_; + bool CreateDummyWindowForActivation(); + + static std::list<MSG> throttle_queue_; + + // Returns true if the event passed in needs to be tracked for a potential + // modal loop. + static bool ShouldTrackEventForModalLoops(NPEvent* event); + + // The message filter hook procedure, which tracks modal loops entered by + // a plugin in the course of a NPP_HandleEvent call. + static LRESULT CALLBACK HandleEventMessageFilterHook(int code, WPARAM wParam, + LPARAM lParam); + + // Called by the message filter hook when the plugin enters a modal loop. + void OnModalLoopEntered(); + + // Returns true if the message passed in corresponds to a user gesture. + static bool IsUserGestureMessage(unsigned int message); + + // Indicates the end of a user gesture period. + void OnUserGestureEnd(); + + // Handle to the message filter hook + HHOOK handle_event_message_filter_hook_; + + // The current instance of the plugin which entered the modal loop. + static WebPluginDelegateImpl* current_plugin_instance_; + + // Event which is set when the plugin enters a modal loop in the course + // of a NPP_HandleEvent call. + HANDLE handle_event_pump_messages_event_; + + // Holds the depth of the HandleEvent callstack. + int handle_event_depth_; + + // This flag indicates whether we started tracking a user gesture message. + bool user_gesture_message_posted_; + + // Runnable Method Factory used to invoke the OnUserGestureEnd method + // asynchronously. + ScopedRunnableMethodFactory<WebPluginDelegateImpl> user_gesture_msg_factory_; + + // The url with which the plugin was instantiated. + std::string plugin_url_; + + // Indicates if the download would be initiated by the plugin or us. + bool load_manually_; + + // Indicates whether a geometry update sequence is the first. + bool first_geometry_update_; + + DISALLOW_EVIL_CONSTRUCTORS(WebPluginDelegateImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_PLUGIN_WEBPLUGIN_DELEGATE_IMPL_H__ diff --git a/webkit/glue/regular_expression_unittest.cc b/webkit/glue/regular_expression_unittest.cc new file mode 100644 index 0000000..a9e1cfa --- /dev/null +++ b/webkit/glue/regular_expression_unittest.cc @@ -0,0 +1,118 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "PlatformString.h" +#include "RegularExpression.h" +#pragma warning(pop) + +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/glue/glue_util.h" + +using std::wstring; +using webkit_glue::StdWStringToDeprecatedString; +using WebCore::DeprecatedString; +using WebCore::RegularExpression; + +namespace { + +class RegexTest : public testing::Test { +}; + +struct Match { + const std::wstring text; + const int position; + const int length; +}; + +} // namespace + +TEST(RegexTest, Basic) { + // Just make sure we're not completely broken. + const DeprecatedString pattern("the quick brown fox"); + RegularExpression regex(pattern, /* case sensitive */ true); + EXPECT_EQ(0, regex.match(DeprecatedString("the quick brown fox"))); + EXPECT_EQ(1, regex.match(DeprecatedString(" the quick brown fox"))); + EXPECT_EQ(3, regex.match(DeprecatedString("foothe quick brown foxbar"))); + + EXPECT_EQ(-1, regex.match(DeprecatedString("The quick brown FOX"))); + EXPECT_EQ(-1, regex.match(DeprecatedString("the quick brown fo"))); +} + +TEST(RegexTest, Unicode) { + // Make sure we get the right offsets for unicode strings. + + // Test 1 + wstring wstr_pattern(L"\x6240\x6709\x7f51\x9875"); + DeprecatedString pattern = StdWStringToDeprecatedString(wstr_pattern); + RegularExpression regex(pattern, /* case sensitive */ false); + + EXPECT_EQ(0, regex.match(StdWStringToDeprecatedString(wstr_pattern))); + EXPECT_EQ(1, regex.match(StdWStringToDeprecatedString( + wstring(L" ") + wstr_pattern))); + EXPECT_EQ(3, regex.match(StdWStringToDeprecatedString( + wstring(L"foo") + wstr_pattern + wstring(L"bar")))); + EXPECT_EQ(4, regex.match(StdWStringToDeprecatedString( + wstring(L"\x4e2d\x6587\x7f51\x9875") + wstr_pattern))); + + // Test 2, mixed length + wstr_pattern = L":[ \x2000]+:"; + pattern = StdWStringToDeprecatedString(wstr_pattern); + regex = RegularExpression(pattern, /* case sensitive */ false); + + const Match matches[] = { + { L": :", 0, 4 }, + { L" : : ", 2, 6 }, + { L" : \x2000 : ", 1, 5 }, + { L"\x6240\x6709\x7f51\x9875 : \x2000 \x2000 : ", 5, 7 }, + { L"", -1, -1 }, + { L"::", -1, -1 }, + }; + for (size_t i = 0; i < arraysize(matches); ++i) { + EXPECT_EQ(matches[i].position, regex.match(StdWStringToDeprecatedString( + wstring(matches[i].text)))); + EXPECT_EQ(matches[i].length, regex.matchedLength()); + } + + // Test 3, empty match + wstr_pattern = L"|x"; + pattern = StdWStringToDeprecatedString(wstr_pattern); + regex = RegularExpression(pattern, /* case sensitive */ false); + + const Match matches2[] = { + { L"", 0, 0 }, + }; + for (size_t i = 0; i < arraysize(matches2); ++i) { + EXPECT_EQ(matches2[i].position, regex.match(StdWStringToDeprecatedString( + wstring(matches2[i].text)))); + EXPECT_EQ(matches2[i].length, regex.matchedLength()); + } +} diff --git a/webkit/glue/resource_fetcher.cc b/webkit/glue/resource_fetcher.cc new file mode 100644 index 0000000..1a4bbd4 --- /dev/null +++ b/webkit/glue/resource_fetcher.cc @@ -0,0 +1,146 @@ +// Copyright 2008, 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 "webkit/glue/resource_fetcher.h" + +#pragma warning(push, 0) +#include "FrameLoader.h" +#include "ResourceHandle.h" +#include "ResourceRequest.h" +#pragma warning(pop) + +#undef LOG +#include "base/logging.h" +#include "webkit/glue/glue_util.h" +#include "net/url_request/url_request_status.h" + +using WebCore::ResourceError; +using WebCore::ResourceHandle; +using WebCore::ResourceResponse; + +ResourceFetcher::ResourceFetcher(const GURL& url, WebCore::Frame* frame, + Delegate* d) + : url_(url), delegate_(d), completed_(false) { + // Can't do anything without a frame. However, delegate can be NULL (so we + // can do a http request and ignore the results). + DCHECK(frame); + Start(frame); +} + +ResourceFetcher::~ResourceFetcher() { + if (!completed_) + loader_->cancel(); + loader_ = NULL; +} + +void ResourceFetcher::Cancel() { + if (!completed_) { + loader_->cancel(); + completed_ = true; + } +} + +void ResourceFetcher::Start(WebCore::Frame* frame) { + WebCore::FrameLoader* frame_loader = frame->loader(); + if (!frame_loader) { + // We put this on a 0 timer so the callback happens async (consistent with + // regular fetches). + start_failed_timer_.reset(new StartFailedTimer(this, + &ResourceFetcher::StartFailed)); + start_failed_timer_->startOneShot(0); + return; + } + + WebCore::ResourceRequest request(webkit_glue::GURLToKURL(url_)); + request.setFrame(frame); + + loader_ = ResourceHandle::create(request, this, NULL, false, false); +} + +void ResourceFetcher::StartFailed(StartFailedTimer* timer) { + didFail(NULL, ResourceError()); +} + +///////////////////////////////////////////////////////////////////////////// +// ResourceHandleClient methods +void ResourceFetcher::didReceiveResponse(ResourceHandle* resource_handle, + const ResourceResponse& response) { + ASSERT(!completed_); + // It's safe to use the ResourceResponse copy constructor + // (xmlhttprequest.cpp uses it). + response_ = response; +} + +void ResourceFetcher::didReceiveData(ResourceHandle* resource_handle, + const char* data, int length, + int total_length) { + ASSERT(!completed_); + if (length <= 0) + return; + + data_.append(data, length); +} + +void ResourceFetcher::didFinishLoading(ResourceHandle* resource_handle) { + ASSERT(!completed_); + completed_ = true; + + if (delegate_) + delegate_->OnURLFetchComplete(response_, data_); +} + +void ResourceFetcher::didFail(ResourceHandle* resource_handle, + const ResourceError& error) { + ASSERT(!completed_); + completed_ = true; + + // Go ahead and tell our delegate that we're done. Send an empty + // ResourceResponse and string. + if (delegate_) + delegate_->OnURLFetchComplete(ResourceResponse(), std::string()); +} + +///////////////////////////////////////////////////////////////////////////// +// A resource fetcher with a timeout + +ResourceFetcherWithTimeout::ResourceFetcherWithTimeout( + const GURL& url, WebCore::Frame* frame, double timeout_secs, Delegate* d) + : ResourceFetcher(url, frame, d) { + timeout_timer_.reset(new FetchTimer(this, + &ResourceFetcherWithTimeout::TimeoutFired)); + timeout_timer_->startOneShot(timeout_secs); +} + +void ResourceFetcherWithTimeout::TimeoutFired(FetchTimer* timer) { + if (!completed_) { + loader_->cancel(); + didFail(NULL, ResourceError()); + } +} diff --git a/webkit/glue/resource_fetcher.h b/webkit/glue/resource_fetcher.h new file mode 100644 index 0000000..57f038a --- /dev/null +++ b/webkit/glue/resource_fetcher.h @@ -0,0 +1,142 @@ +// Copyright 2008, 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. +// +// A wrapper around ResourceHandle and ResourceHandleClient that simplifies +// the download of an HTTP object. The interface is modeled after URLFetcher +// in the /chrome/browser. +// +// ResourceFetcher::Delegate::OnURLFetchComplete will be called async after +// the ResourceFetcher object is created. + +#ifndef WEBKIT_GLUE_RESOURCE_FETCHER_H__ +#define WEBKIT_GLUE_RESOURCE_FETCHER_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "googleurl/src/gurl.h" + +#pragma warning(push, 0) +#include "Frame.h" +#include "Timer.h" +#include "ResourceHandleClient.h" +#include "ResourceResponse.h" +#pragma warning(pop) + +class GURL; +class WebCore::ResourceHandle; + +class ResourceFetcher : public WebCore::ResourceHandleClient { + public: + class Delegate { + public: + // This will be called when the URL has been fetched, successfully or not. + // If there is a failure, response and data will both be empty. + // |response| and |data| are both valid until the URLFetcher instance is + // destroyed. + virtual void OnURLFetchComplete(const WebCore::ResourceResponse& response, + const std::string& data) = 0; + }; + + // We need a frame and frame loader to make requests. + ResourceFetcher(const GURL& url, WebCore::Frame* frame, Delegate* d); + ~ResourceFetcher(); + + // Stop the request and don't call the callback. + void Cancel(); + + bool completed() { return completed_; } + + // ResourceHandleClient methods + virtual void didReceiveResponse(WebCore::ResourceHandle* resource_handle, + const WebCore::ResourceResponse& response); + + virtual void didReceiveData(WebCore::ResourceHandle* resource_handle, + const char* data, int length, int total_length); + + virtual void didFinishLoading(WebCore::ResourceHandle* resource_handle); + + virtual void didFail(WebCore::ResourceHandle* resource_handle, + const WebCore::ResourceError& error); + + protected: + // The parent ResourceHandle + RefPtr<WebCore::ResourceHandle> loader_; + + // URL we're fetching + GURL url_; + + // Callback when we're done + Delegate* delegate_; + + // A copy of the original resource response + WebCore::ResourceResponse response_; + + // Set to true once the request is compelte. + bool completed_; + + private: + // If we fail to start the request, we still want to finish async. + typedef WebCore::Timer<ResourceFetcher> StartFailedTimer; + + // Start the actual download. + void Start(WebCore::Frame* frame); + + // Callback function if Start fails. + void StartFailed(StartFailedTimer* timer); + + // Timer for calling StartFailed async. + scoped_ptr<StartFailedTimer> start_failed_timer_; + + // Buffer to hold the content from the server. + std::string data_; +}; + +///////////////////////////////////////////////////////////////////////////// +// A resource fetcher with a timeout +class ResourceFetcherWithTimeout : public ResourceFetcher { + public: + ResourceFetcherWithTimeout(const GURL& url, WebCore::Frame* frame, double + timeout_secs, Delegate* d); + virtual ~ResourceFetcherWithTimeout() {} + + private: + typedef WebCore::Timer<ResourceFetcherWithTimeout> FetchTimer; + + // Callback for timer that limits how long we wait for the alternate error + // page server. If this timer fires and the request hasn't completed, we + // kill the request. + void TimeoutFired(FetchTimer* timer); + + // Limit how long we wait for the alternate error page server. + scoped_ptr<FetchTimer> timeout_timer_; +}; + +#endif // WEBKIT_GLUE_RESOURCE_FETCHER_H__ diff --git a/webkit/glue/resource_fetcher_unittest.cc b/webkit/glue/resource_fetcher_unittest.cc new file mode 100644 index 0000000..c9eef1f --- /dev/null +++ b/webkit/glue/resource_fetcher_unittest.cc @@ -0,0 +1,210 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "ResourceResponse.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/unittest_test_server.h" +#include "webkit/glue/webview.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/resource_fetcher.h" +#include "webkit/tools/test_shell/simple_resource_loader_bridge.h" +#include "webkit/tools/test_shell/test_shell_test.h" + +using WebCore::ResourceResponse; + +namespace { + + +class ResourceFetcherTests : public TestShellTest { + public: + void SetUp() { + TestShellTest::SetUp(); + } + void TearDown() { + TestShellTest::TearDown(); + } +}; + +static const int kMaxWaitTimeMs = 5000; +static const int kWaitIntervalMs = 100; + +class FetcherDelegate : public ResourceFetcher::Delegate { + public: + FetcherDelegate() + : timer_id_(0), completed_(false), time_elapsed_ms_(0) { + // Start a repeating timer waiting for the download to complete. The + // callback has to be a static function, so we hold on to our instance. + FetcherDelegate::instance_ = this; + timer_id_ = SetTimer(NULL, NULL, kWaitIntervalMs, + &FetcherDelegate::TimerCallback); + } + + virtual void OnURLFetchComplete(const ResourceResponse& response, + const std::string& data) { + response_ = response; + data_ = data; + completed_ = true; + KillTimer(NULL, timer_id_); + MessageLoop::current()->Quit(); + } + + bool completed() const { return completed_; } + bool timed_out() const { return time_elapsed_ms_ > kMaxWaitTimeMs; } + + int time_elapsed_ms() const { return time_elapsed_ms_; } + std::string data() const { return data_; } + ResourceResponse response() const { return response_; } + + // Wait for the request to complete or timeout. We use a loop here b/c the + // testing infrastructure (test_shell) can generate spurious calls to the + // MessageLoop's Quit method. + void WaitForResponse() { + while (!completed() && !timed_out()) + MessageLoop::current()->Run(); + } + + // Static timer callback, just passes through to instance version. + static VOID CALLBACK TimerCallback(HWND hwnd, UINT msg, UINT_PTR timer_id, + DWORD ms) { + instance_->TimerFired(hwnd, timer_id); + } + + void TimerFired(HWND hwnd, UINT_PTR timer_id) { + ASSERT_FALSE(completed_); + + if (timed_out()) { + printf("timer fired\n"); + KillTimer(hwnd, timer_id); + MessageLoop::current()->Quit(); + FAIL() << "fetch timed out"; + return; + } + + time_elapsed_ms_ += kWaitIntervalMs; + } + + static FetcherDelegate* instance_; + + private: + UINT_PTR timer_id_; + bool completed_; + int time_elapsed_ms_; + ResourceResponse response_; + std::string data_; +}; + +FetcherDelegate* FetcherDelegate::instance_ = NULL; + +} // namespace + +// Test a fetch from the test server. +TEST_F(ResourceFetcherTests, ResourceFetcherDownload) { + UnittestTestServer server; + + WebFrame* web_frame = test_shell_->webView()->GetMainFrame(); + // Not safe, but this is a unittest, so whatever. + WebFrameImpl* web_frame_impl = reinterpret_cast<WebFrameImpl*>(web_frame); + WebCore::Frame* frame = web_frame_impl->frame(); + + GURL url = server.TestServerPage("files/test_shell/index.html"); + scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate); + scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher( + url, frame, delegate.get())); + + delegate->WaitForResponse(); + + ASSERT_TRUE(delegate->completed()); + EXPECT_EQ(delegate->response().httpStatusCode(), 200); + std::string text = delegate->data(); + EXPECT_TRUE(text.find("What is this page?") != std::string::npos); + + // Test 404 response. + url = server.TestServerPage("files/thisfiledoesntexist.html"); + delegate.reset(new FetcherDelegate); + fetcher.reset(new ResourceFetcher(url, frame, delegate.get())); + + delegate->WaitForResponse(); + + ASSERT_TRUE(delegate->completed()); + EXPECT_EQ(delegate->response().httpStatusCode(), 404); + EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos); +} + +TEST_F(ResourceFetcherTests, ResourceFetcherDidFail) { + UnittestTestServer server; + WebFrame* web_frame = test_shell_->webView()->GetMainFrame(); + // Not safe, but this is a unittest, so whatever. + WebFrameImpl* web_frame_impl = reinterpret_cast<WebFrameImpl*>(web_frame); + WebCore::Frame* frame = web_frame_impl->frame(); + + // Try to fetch a page on a site that doesn't exist. + GURL url("http://localhost:1339/doesnotexist"); + scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate); + scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcher( + url, frame, delegate.get())); + + delegate->WaitForResponse(); + + // When we fail, we still call the Delegate callback but we pass in empty + // values. + EXPECT_TRUE(delegate->completed()); + EXPECT_TRUE(delegate->response().isNull()); + EXPECT_EQ(delegate->data(), std::string()); + EXPECT_TRUE(delegate->time_elapsed_ms() < kMaxWaitTimeMs); +} + +TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) { + UnittestTestServer server; + + WebFrame* web_frame = test_shell_->webView()->GetMainFrame(); + // Not safe, but this is a unittest, so whatever. + WebFrameImpl* web_frame_impl = reinterpret_cast<WebFrameImpl*>(web_frame); + WebCore::Frame* frame = web_frame_impl->frame(); + + // Grab a page that takes at least 1 sec to respond, but set the fetcher to + // timeout in 0 sec. + GURL url = server.TestServerPage("slow?1"); + scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate); + scoped_ptr<ResourceFetcher> fetcher(new ResourceFetcherWithTimeout( + url, frame, 0, delegate.get())); + + delegate->WaitForResponse(); + + // When we timeout, we still call the Delegate callback but we pass in empty + // values. + EXPECT_TRUE(delegate->completed()); + EXPECT_TRUE(delegate->response().isNull()); + EXPECT_EQ(delegate->data(), std::string()); + EXPECT_TRUE(delegate->time_elapsed_ms() < kMaxWaitTimeMs); +} diff --git a/webkit/glue/resource_handle_win.cc b/webkit/glue/resource_handle_win.cc new file mode 100644 index 0000000..1646f0a --- /dev/null +++ b/webkit/glue/resource_handle_win.cc @@ -0,0 +1,764 @@ +// Copyright 2008, 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. +// +// This file replaces WebCore/platform/network/win/ResourceHandleWin.cpp with a +// platform-neutral implementation that simply defers almost entirely to +// ResouceLoaderBridge. +// +// This uses the same ResourceHandle.h header file that the rest of WebKit +// uses, allowing us to avoid complicated changes. Our specific things are +// added on ResourceHandleInternal. The ResourceHandle owns the +// ResourceHandleInternal and passes off almost all processing to it. +// +// The WebKit version of this code keeps the ResourceHandle AddRef'd when +// there are any callbacks. This prevents the callbacks from occuring into +// destroyed objects. However, our destructors should always stop callbacks +// from happening, making this (hopefully) unnecessary. +// +// We preserve this behavior for safety. A client could count on this behavior +// and fire off a request, release it, and wait for callbacks to get the data +// as long as it doesn't care about canceling the request. Although this is +// dumb, we support it. We use pending_ to indicate this extra AddRef, which +// is done in start() and released in OnCompletedRequest. + +#include "config.h" + +#pragma warning(push, 0) +#include "CString.h" +#include "DocLoader.h" +#include "FormData.h" +#include "FrameLoader.h" +#include "LogWin.h" +#include "Page.h" +#include "ResourceError.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceHandleWin.h" // for Platform{Response,Data}Struct +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#pragma warning(pop) + +#undef LOG +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/time.h" +#include "base/string_util.h" +#include "base/string_tokenizer.h" +#include "webkit/glue/feed_preview.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/multipart_response_delegate.h" +#include "webkit/glue/resource_loader_bridge.h" +#include "webkit/glue/webframe_impl.h" +#include "net/base/data_url.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "net/base/load_flags.h" + +using webkit_glue::ResourceLoaderBridge; +using net::HttpResponseHeaders; + +namespace { + +// Extracts the information from a data: url. +bool GetInfoFromDataUrl(const GURL& url, + ResourceLoaderBridge::ResponseInfo* info, + std::string* data, URLRequestStatus* status) { + std::string mime_type; + std::string charset; + if (DataURL::Parse(url, &mime_type, &charset, data)) { + info->request_time = Time::Now(); + info->response_time = Time::Now(); + info->mime_type.swap(mime_type); + info->charset.swap(charset); + *status = URLRequestStatus(URLRequestStatus::SUCCESS, 0); + return true; + } + + *status = URLRequestStatus(URLRequestStatus::FAILED, net::ERR_INVALID_URL); + return false; +} + +} // namespace + +namespace WebCore { + +static void ExtractInfoFromHeaders(const HttpResponseHeaders* headers, + HTTPHeaderMap* header_map, + int* status_code, + String* status_text, + long long* expected_content_length) { + *status_code = headers->response_code(); + + // Set the status text (the returned status line is normalized). + const std::string& status = headers->GetStatusLine(); + StringTokenizer status_tokenizer(status, " "); + if (status_tokenizer.GetNext() && // identifies "HTTP/1.1" + status_tokenizer.GetNext() && // identifies "200" + status_tokenizer.GetNext()) // identifies first word of status text + *status_text = webkit_glue::StdStringToString( + std::string(status_tokenizer.token_begin(), status.end())); + + // Set the content length. + std::string length_val; + if (headers->EnumerateHeader(NULL, "content-length", &length_val)) + *expected_content_length = StringToInt64(length_val); + + // Build up the header map. Take care with duplicate headers. + void* iter = NULL; + std::string name, value; + while (headers->EnumerateHeaderLines(&iter, &name, &value)) { + String name_str = webkit_glue::StdStringToString(name); + String value_str = webkit_glue::StdStringToString(value); + + pair<HTTPHeaderMap::iterator, bool> result = + header_map->add(name_str, value_str); + if (!result.second) + result.first->second += ", " + value_str; + } +} + +static ResourceResponse MakeResourceResponse( + const KURL& kurl, + const ResourceLoaderBridge::ResponseInfo& info) { + int status_code = 0; + long long expected_content_length = info.content_length; + String status_text; + HTTPHeaderMap header_map; + + // It's okay if there are no headers + if (info.headers) + ExtractInfoFromHeaders(info.headers, + &header_map, + &status_code, + &status_text, + &expected_content_length); + + // TODO(darin): We should leverage HttpResponseHeaders for this, and this + // should be using the same code as ResourceDispatcherHost. + std::wstring suggested_filename; + if (info.headers) { + std::string disp_val; + if (info.headers->EnumerateHeader(NULL, "content-disposition", &disp_val)) { + suggested_filename = net_util::GetSuggestedFilename( + webkit_glue::KURLToGURL(kurl), disp_val, std::wstring()); + } + } + + ResourceResponse response(kurl, + webkit_glue::StdStringToString(info.mime_type), + expected_content_length, + webkit_glue::StdStringToString(info.charset), + webkit_glue::StdWStringToString(suggested_filename)); + + if (info.headers) { + Time time_val; + if (info.headers->GetLastModifiedValue(&time_val)) + response.setLastModifiedDate(time_val.ToTimeT()); + + // Compute expiration date + TimeDelta freshness_lifetime = + info.headers->GetFreshnessLifetime(info.response_time); + if (freshness_lifetime != TimeDelta()) { + Time now = Time::Now(); + TimeDelta current_age = + info.headers->GetCurrentAge(info.request_time, info.response_time, + now); + time_val = now + freshness_lifetime - current_age; + + response.setExpirationDate(time_val.ToTimeT()); + } else { + // WebKit uses 0 as a special expiration date that means never expire. + // 1 is a small enough value to let it always expire. + response.setExpirationDate(1); + } + } + + response.setHTTPStatusCode(status_code); + response.setHTTPStatusText(status_text); + response.setSecurityInfo(webkit_glue::StdStringToCString(info.security_info)); + + // WebKit doesn't provide a way for us to set expected content length after + // calling the constructor, so we parse the headers first and then swap in + // our HTTP header map. Ideally we would like a setter for expected content + // length (perhaps by abstracting ResourceResponse interface into + // ResourceResponseBase) but that would require forking. + const_cast<HTTPHeaderMap*>(&response.httpHeaderFields())->swap(header_map); + + return response; +} + +class ResourceHandleInternal : public ResourceLoaderBridge::Peer { + public: + ResourceHandleInternal(ResourceHandle* job, const ResourceRequest& r, + ResourceHandleClient* c); + ~ResourceHandleInternal(); + + // If the response parameter is null, then an asynchronous load is started. + bool Start(ResourceLoaderBridge::SyncLoadResponse* response); + + // Used to cancel an asynchronous load. + void Cancel(); + + // Used to suspend/resume an asynchronous load. + void SetDefersLoading(bool value); + + // ResourceLoaderBridge::Peer implementation + virtual void OnReceivedRedirect(const GURL& new_url); + virtual void OnReceivedResponse( + const ResourceLoaderBridge::ResponseInfo& info); + virtual void OnReceivedData(const char* data, int len); + virtual void OnCompletedRequest(const URLRequestStatus& status); + virtual std::string GetURLForDebugging(); + + // Handles a data: url internally instead of calling the bridge. + void HandleDataUrl(); + + // This is the bridge implemented by the embedder. + // The bridge is kept alive as long as the request is valid and we + // are ready for callbacks. + scoped_ptr<ResourceLoaderBridge> bridge_; + + // The resource loader that owns us + ResourceHandle* job_; + + // This is the object that receives various status messages (such as when the + // loader has received data). See definition for the exact messages that are + // sent to it. + ResourceHandleClient* client_; + + ResourceRequest request_; + + // Runnable Method Factory used to invoke later HandleDataUrl(). + ScopedRunnableMethodFactory<ResourceHandleInternal> data_url_factory_; + + int load_flags_; + + private: + // Set to true when we're waiting for data from the bridge, also indicating + // we have addrefed our job. + bool pending_; + + // Expected content length of the response + long long expected_content_length_; + + // NULL unless we are handling a multipart/x-mixed-replace request + scoped_ptr<MultipartResponseDelegate> multipart_delegate_; + + // NULL unless we are handling a feed:// request. + scoped_ptr<FeedClientProxy> feed_client_proxy_; +}; + +ResourceHandleInternal::ResourceHandleInternal(ResourceHandle* job, + const ResourceRequest& r, + ResourceHandleClient* c) + : job_(job), + client_(c), + request_(r), + load_flags_(net::LOAD_NORMAL), + pending_(false), + expected_content_length_(-1), + multipart_delegate_(NULL), +#pragma warning(suppress: 4355) // can use this + data_url_factory_(this) { +} + +ResourceHandleInternal::~ResourceHandleInternal() { + DCHECK(!pending_); +} + +void ResourceHandleInternal::HandleDataUrl() { + ResourceLoaderBridge::ResponseInfo info; + URLRequestStatus status; + std::string data; + + if (GetInfoFromDataUrl(webkit_glue::KURLToGURL(request_.url()), &info, &data, + &status)) { + OnReceivedResponse(info); + + if (data.size()) + OnReceivedData(data.c_str(), data.size()); + } + + OnCompletedRequest(status); + + // We are done using the object. ResourceHandle and ResourceHandleInternal + // might be destroyed now. + job_->deref(); +} + +bool ResourceHandleInternal::Start( + ResourceLoaderBridge::SyncLoadResponse* sync_load_response) { + DCHECK(!bridge_.get()); + + // The WebFrame is the Frame's FrameWinClient + WebFrameImpl* webframe = + request_.frame() ? WebFrameImpl::FromFrame(request_.frame()) : NULL; + + CString method = request_.httpMethod().latin1(); + GURL referrer(webkit_glue::StringToStdWString(request_.httpReferrer())); + + // Compute the URL of the load. + GURL url = webkit_glue::KURLToGURL(request_.url()); + if (url.SchemeIs("feed:")) { + // Feed URLs are special, they actually mean "http". + url_canon::Replacements<char> replacements; + replacements.SetScheme("http", url_parse::Component(0, 4)); + url = url.ReplaceComponents(replacements); + + // Replace our client with a client that understands previewing feeds + // and forwards the feeds along to the original client. + feed_client_proxy_.reset(new FeedClientProxy(client_)); + client_ = feed_client_proxy_.get(); + } + + // Inherit the policy URL from the request's frame. However, if the request + // is for a main frame, the current document's policyBaseURL is the old + // document, so we leave policyURL empty to indicate that the request is a + // first-party request. + GURL policy_url; + if (request_.resourceType() != ResourceType::MAIN_FRAME && + request_.frame() && request_.frame()->document()) { + policy_url = GURL(webkit_glue::StringToStdWString( + request_.frame()->document()->policyBaseURL())); + } + + // Translate the table of request headers to a formatted string blob + String headerBuf; + const HTTPHeaderMap& headerMap = request_.httpHeaderFields(); + + // In some cases, WebCore doesn't add an Accept header, but not having the + // header confuses some web servers. See bug 808613. + // Note: headerMap uses case-insenstive keys, so this will find Accept as + // as well. + if (!headerMap.contains("accept")) + request_.addHTTPHeaderField("Accept", "*/*"); + + const String crlf(L"\r\n"); + const String sep(L": "); + for (HTTPHeaderMap::const_iterator it = headerMap.begin(); + it != headerMap.end(); ++it) { + // Skip over referrer headers found in the header map because we already + // pulled it out as a separate parameter. We likewise prune the UA since + // that will be added back by the network layer. + if (equalIgnoringCase((*it).first, "referer") || + equalIgnoringCase((*it).first, "user-agent")) + continue; + // WinInet dies if blank headers are set. TODO(darin): Is this still an + // issue now that we are using WinHTTP? + if ((*it).first.isEmpty()) { + webframe->frame()->page()->chrome()->addMessageToConsole( + JSMessageSource, + ErrorMessageLevel, + "Refused to set blank header", + 1, + String()); + continue; + } + if (!headerBuf.isEmpty()) + headerBuf.append(crlf); + headerBuf.append((*it).first + sep + (*it).second); + } + + switch (request_.cachePolicy()) { + case ReloadIgnoringCacheData: + // Required by LayoutTests/http/tests/misc/refresh-headers.php + load_flags_ |= net::LOAD_VALIDATE_CACHE; + break; + case ReturnCacheDataElseLoad: + load_flags_ |= net::LOAD_PREFERRING_CACHE; + break; + case ReturnCacheDataDontLoad: + load_flags_ |= net::LOAD_ONLY_FROM_CACHE; + break; + } + + // TODO(jcampan): in the non out-of-process plugin case the request does not + // have a origin_pid. Find a better place to set this. + int origin_pid = request_.originPid(); + if (origin_pid == 0) + origin_pid = ::GetCurrentProcessId(); + + bool mixed_content = + webkit_glue::KURLToGURL(request_.mainDocumentURL()).SchemeIsSecure() && + !url.SchemeIsSecure(); + + if (url.SchemeIs("data")) { + if (sync_load_response) { + // This is a sync load. Do the work now. + sync_load_response->url = url; + std::string data; + GetInfoFromDataUrl(sync_load_response->url, sync_load_response, + &sync_load_response->data, + &sync_load_response->status); + } else { + pending_ = true; + job_->ref(); // to be released when we get a OnCompletedRequest. + job_->ref(); // to be released when HandleDataUrl is completed. + MessageLoop::current()->PostTask(FROM_HERE, + data_url_factory_.NewRunnableMethod( + &ResourceHandleInternal::HandleDataUrl)); + } + return true; + } + + // TODO(darin): is latin1 really correct here? It is if the strings are + // already ASCII (i.e., if they are already escaped properly). + // TODO(brettw) this should take parameter encoding into account when + // creating the GURLs. + bridge_.reset(ResourceLoaderBridge::Create( + webframe, + webkit_glue::CStringToStdString(method), + url, + policy_url, + referrer, + webkit_glue::CStringToStdString(headerBuf.latin1()), + load_flags_, + origin_pid, + request_.resourceType(), + mixed_content)); + if (!bridge_.get()) + return false; + + if (request_.httpBody()) { + // GET and HEAD requests shouldn't have http bodies. + DCHECK(method != "GET" && method != "HEAD"); + const Vector<FormDataElement>& elements = request_.httpBody()->elements(); + size_t n = elements.size(); + for (size_t i = 0; i < n; ++i) { + const FormDataElement& e = elements[static_cast<unsigned>(i)]; + if (e.m_type == FormDataElement::data) { + if (e.m_data.size() > 0) { + // WebKit sometimes gives up empty data to append. These aren't + // necessary so we just optimize those out here. + bridge_->AppendDataToUpload(e.m_data.data(), + static_cast<int>(e.m_data.size())); + } + } else { + bridge_->AppendFileToUpload( + webkit_glue::StringToStdWString(e.m_filename)); + } + } + } + + if (sync_load_response) { + bridge_->SyncLoad(sync_load_response); + return true; + } + + bool rv = bridge_->Start(this); + if (rv) { + pending_ = true; + job_->ref(); // to be released when we get a OnCompletedRequest. + } else { + bridge_.reset(); + } + + return rv; +} + +void ResourceHandleInternal::Cancel() { + // The bridge will still send OnCompletedRequest, which will deref() us, + // so we don't do that here. + if (bridge_.get()) + bridge_->Cancel(); + + // Ensure that we do not notify the multipart delegate anymore as it has + // its own pointer to the client. + multipart_delegate_.reset(); + + // Do not make any further calls to the client. + client_ = NULL; +} + +void ResourceHandleInternal::SetDefersLoading(bool value) { + if (bridge_.get()) + bridge_->SetDefersLoading(value); +} + +// ResourceLoaderBridge::Peer impl -------------------------------------------- + +void ResourceHandleInternal::OnReceivedRedirect(const GURL& new_url) { + DCHECK(pending_); + + KURL url = webkit_glue::GURLToKURL(new_url); + + // TODO(darin): need a way to properly initialize a ResourceResponse + ResourceResponse response(request_.url(), String(), -1, String(), String()); + + ResourceRequest new_request(url); + + // TODO(darin): we need to setup new_request to reflect the fact that we + // for example drop the httpBody when following a POST request that is + // redirected to a GET request. + + if (client_) + client_->willSendRequest(job_, new_request, response); + + // + // TODO(darin): since new_request is sent as a mutable reference, it is + // possible that willSendRequest may expect to be able to modify it. + // + // andresca on #webkit confirms that that is intentional, so we'll need + // to rework the ResourceLoaderBridge to give us control over what URL + // is really loaded (and with what headers) when a redirect is encountered. + // + + request_ = new_request; +} + +void ResourceHandleInternal::OnReceivedResponse( + const ResourceLoaderBridge::ResponseInfo& info) { + DCHECK(pending_); + + // TODO(darin): need a way to properly initialize a ResourceResponse + ResourceResponse response = MakeResourceResponse(request_.url(), info); + + expected_content_length_ = response.expectedContentLength(); + + if (client_) + client_->didReceiveResponse(job_, response); + + // we may have been cancelled after didReceiveResponse, which would leave us + // without a client and therefore without much need to do multipart handling. + + DCHECK(!multipart_delegate_.get()); + if (client_ && info.headers && response.isMultipart()) { + std::string content_type; + info.headers->EnumerateHeader(NULL, "content-type", &content_type); + + std::string boundary = net_util::GetHeaderParamValue(content_type, + "boundary"); + TrimString(boundary, " \"", &boundary); + // If there's no boundary, just handle the request normally. In the gecko + // code, nsMultiMixedConv::OnStartRequest throws an exception. + if (!boundary.empty()) { + multipart_delegate_.reset(new MultipartResponseDelegate(client_, job_, + response, boundary)); + } + } + + // TODO(darin): generate willCacheResponse callback. debug mac webkit to + // determine when it should be called. +} + +void ResourceHandleInternal::OnReceivedData(const char* data, int data_len) { + DCHECK(pending_); + + if (client_) { + // TODO(darin): figure out what to pass for lengthReceived. from reading + // the loader code, it looks like this is supposed to be the content-length + // value, but it seems really wacky to include that here! we have to debug + // webkit on mac to figure out what this should be. + + // TODO(jackson): didReceiveData expects an int, but an expected content + // length is an int64, so we do our best to fit it inside an int. The only + // code that cares currently about this value is the Inspector, so beware + // that the Inspector's network panel might under-represent the size of + // some resources if they're larger than a gigabyte. + int lengthReceived = static_cast<int>(expected_content_length_); + if (lengthReceived != expected_content_length_) // overflow occurred + lengthReceived = -1; + + if (!multipart_delegate_.get()) { + client_->didReceiveData(job_, data, data_len, lengthReceived); + } else { + // AddData will make the appropriate calls to client_->didReceiveData + // and client_->didReceiveResponse + multipart_delegate_->OnReceivedData(data, data_len); + } + } +} + +void ResourceHandleInternal::OnCompletedRequest( + const URLRequestStatus& status) { + if (multipart_delegate_.get()) { + multipart_delegate_->OnCompletedRequest(); + multipart_delegate_.reset(NULL); + } + + pending_ = false; + + if (client_) { + if (status.status() != URLRequestStatus::SUCCESS) { + int error_code; + if (status.status() == URLRequestStatus::HANDLED_EXTERNALLY) { + // By marking this request as aborted we insure that we don't navigate + // to an error page. + error_code = net::ERR_ABORTED; + } else { + error_code = status.os_error(); + } + // TODO(tc): fill in these fields properly + ResourceError error(net::kErrorDomain, + error_code, + request_.url().string(), + String() /*localized description*/); + client_->didFail(job_, error); + } else { + client_->didFinishLoading(job_); + } + } + + job_->deref(); // may destroy our owner and hence |this| +} + +std::string ResourceHandleInternal::GetURLForDebugging() { + return webkit_glue::CStringToStdString(request_.url().string().latin1()); +} + +// ResourceHandle ------------------------------------------------------------- + +ResourceHandle::ResourceHandle(const ResourceRequest& request, + ResourceHandleClient* client, + bool defersLoading, + bool shouldContentSniff, + bool mightDownloadFromHandle) +#pragma warning(suppress: 4355) // it's okay to pass |this| here! + : d(new ResourceHandleInternal(this, request, client)) { + // TODO(darin): figure out what to do with the two bool params +} + +PassRefPtr<ResourceHandle> ResourceHandle::create(const ResourceRequest& request, + ResourceHandleClient* client, + Frame* deprecated, + bool defersLoading, + bool shouldContentSniff, + bool mightDownloadFromHandle) { + RefPtr<ResourceHandle> newHandle( + new ResourceHandle(request, client, defersLoading, shouldContentSniff, + mightDownloadFromHandle)); + + if (newHandle->start(NULL)) + return newHandle.release(); + + return NULL; +} + +const ResourceRequest& ResourceHandle::request() const { + return d->request_; +} + +ResourceHandleClient* ResourceHandle::client() const { + return d->client_; +} + +void ResourceHandle::setClient(ResourceHandleClient* client) { + d->client_ = client; +} + +void ResourceHandle::setDefersLoading(bool value) { + d->SetDefersLoading(value); +} + +bool ResourceHandle::start(Frame* deprecated) { + return d->Start(NULL); +} + +void ResourceHandle::clearAuthentication() { + // TODO(darin): do something here. it looks like the ResourceLoader calls + // this method when it is canceled. i have no idea why it does this. +} + +void ResourceHandle::cancel() { + d->Cancel(); +} + +ResourceHandle::~ResourceHandle() { +} + +PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() { + return NULL; +} + +/*static*/ bool ResourceHandle::loadsBlocked() { + return false; // this seems to be related to sync XMLHttpRequest... +} + +/*static*/ bool ResourceHandle::supportsBufferedData() { + return false; // the loader will buffer manually if it needs to +} + +/*static*/ void ResourceHandle::loadResourceSynchronously( + const ResourceRequest& request, ResourceError& error, + ResourceResponse& response, Vector<char>& data, Frame*) { + + RefPtr<ResourceHandle> handle = + new ResourceHandle(request, NULL, false, false, false); + + ResourceLoaderBridge::SyncLoadResponse sync_load_response; + if (!handle->d->Start(&sync_load_response)) { + // TODO(darin): what should the error code really be? + error = ResourceError(net::kErrorDomain, + net::ERR_FAILED, + request.url().string(), + String() /* localized description */); + return; + } + + KURL kurl = webkit_glue::GURLToKURL(sync_load_response.url); + + // TODO(tc): If there's an error during the load, we don't set the response. + // For file loads, we may want to include a more descriptive status code or + // status text. + const URLRequestStatus::Status& status = sync_load_response.status.status(); + if (status != URLRequestStatus::SUCCESS && + status != URLRequestStatus::HANDLED_EXTERNALLY) { + error = ResourceError(net::kErrorDomain, + sync_load_response.status.os_error(), + kurl.string(), + String() /* localized description */); + return; + } + + response = MakeResourceResponse(kurl, sync_load_response); + + data.clear(); + data.append(sync_load_response.data.data(), + sync_load_response.data.size()); +} + +// static +bool ResourceHandle::willLoadFromCache(ResourceRequest& request) { + // + // This method is used to determine if a POST request can be repeated from + // cache, but you cannot really know until you actually try to read from the + // cache. Even if we checked now, something else could come along and wipe + // out the cache entry by the time we fetch it. + // + // So, we always say yes here, which allows us to generate an ERR_CACHE_MISS + // if the request cannot be serviced from cache. We force the 'DontLoad' + // cache policy at this point to ensure that we never hit the network for + // this request. + // + DCHECK(request.httpMethod() == "POST"); + request.setCachePolicy(ReturnCacheDataDontLoad); + return true; +} + +} // namespace WebCore diff --git a/webkit/glue/resource_loader_bridge.h b/webkit/glue/resource_loader_bridge.h new file mode 100644 index 0000000..ba2fd0c --- /dev/null +++ b/webkit/glue/resource_loader_bridge.h @@ -0,0 +1,225 @@ +// Copyright 2008, 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. +// +// The intent of this file is to provide a type-neutral abstraction between +// Chrome and WebKit for resource loading. This pure-virtual interface is +// implemented by the embedder, which also provides a factory method Create +// to instantiate this object. +// +// One of these objects will be created by WebKit for each request. WebKit +// will own the pointer to the bridge, and will delete it when the request is +// no longer needed. +// +// In turn, the bridge's owner on the WebKit end will implement the Peer +// interface, which we will use to communicate notifications back. + +#ifndef RESOURCE_LOADER_BRIDGE_H__ +#define RESOURCE_LOADER_BRIDGE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/time.h" +#include "googleurl/src/gurl.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request_status.h" +#include "webkit/glue/resource_type.h" + +class WebFrame; + +namespace webkit_glue { + +class ResourceLoaderBridge { + public: + struct ResponseInfo { + // The time at which the request was made that resulted in this response. + // For cached responses, this time could be "far" in the past. + Time request_time; + + // The time at which the response headers were received. For cached + // responses, this time could be "far" in the past. + Time response_time; + + // The response headers or NULL if the URL type does not support headers. + scoped_refptr<net::HttpResponseHeaders> headers; + + // The mime type of the response. This may be a derived value. + std::string mime_type; + + // The character encoding of the response or none if not applicable to the + // response's mime type. This may be a derived value. + std::string charset; + + // An opaque string carrying security information pertaining to this + // response. This may include information about the SSL connection used. + std::string security_info; + + // Content length if available. -1 if not available + int64 content_length; + }; + + // generated by the bridge. This is implemented by our custom resource loader + // within webkit. The Peer and it's bridge should have identical lifetimes + // as they represent each end of a communication channel. + // + // These callbacks mirror URLRequest::Delegate and the order and conditions + // in which they will be called are identical. See url_request.h for more + // information. + class Peer { + public: + // Called as upload progress is made. + // note: only for requests with LOAD_ENABLE_UPLOAD_PROGRESS set + virtual void OnUploadProgress(uint64 position, uint64 size) {}; + + // Called when a redirect occurs. + virtual void OnReceivedRedirect(const GURL& new_url) = 0; + + // Called when response headers are available (after all redirects have + // been followed). + virtual void OnReceivedResponse(const ResponseInfo& info) = 0; + + // Called when a chunk of response data is available. This method may + // be called multiple times or not at all if an error occurs. + virtual void OnReceivedData(const char* data, int len) = 0; + + // Called when the response is complete. This method signals completion of + // the resource load. + virtual void OnCompletedRequest(const URLRequestStatus& status) = 0; + + // Returns the URL of the request, which allows us to display it in + // debugging situations. + virtual std::string GetURLForDebugging() = 0; + }; + + // use Create() for construction, but anybody can delete at any time, + // INCLUDING during processing of callbacks. + virtual ~ResourceLoaderBridge() {} + + // Call this method to make a new instance. The method name is a HTTP-style + // method name (e.g., "GET" or "POST"). The URL should be an absolute URL + // encoded in ASCII per the rules of RFC-2396. The referrer parameter is + // optional (may be NULL) and is a URL with similar constraints in how it + // must be encoded. + // + // For HTTP(S) POST requests, the AppendDataToUpload and AppendFileToUpload + // methods may be called to construct the body of the request. + // + // For HTTP(S) requests, the headers parameter can be a \r\n-delimited and + // \r\n-terminated list of MIME headers. They should be ASCII-encoded using + // the standard MIME header encoding rules. The headers parameter can also + // be null if no extra request headers need to be set. + // + // The WebFrame passed to this function provides context about the origin + // of the resource request. + // + // policy_url is the URL of the document in the top-level window, which may be + // checked by the third-party cookie blocking policy. + // + // load_flags is composed of the values defined in url_request_load_flags.h + // + // mixed_content when true indicates that the resource associated with this + // request is over HTTP when the main page was loaded over HTTPS. + // + // request_type indicates if the current request is the main frame load, a + // sub-frame load, or a sub objects load. + static ResourceLoaderBridge* Create(WebFrame* frame, + const std::string& method, + const GURL& url, + const GURL& policy_url, + const GURL& referrer, + const std::string& headers, + int load_flags, + int origin_pid, + ResourceType::Type request_type, + bool mixed_content); + + // Call this method before calling Start() to append a chunk of binary data + // to the request body. May only be used with HTTP(S) POST requests. + virtual void AppendDataToUpload(const char* data, int data_len) = 0; + + // Call this method before calling Start() to append the contents of a file + // to the request body. May only be used with HTTP(S) POST requests. + void AppendFileToUpload(const std::wstring& file_path) { + AppendFileRangeToUpload(file_path, 0, kuint64max); + } + + // Call this method before calling Start() to append the contents of a file + // to the request body. May only be used with HTTP(S) POST requests. + virtual void AppendFileRangeToUpload(const std::wstring& file_path, + uint64 offset, uint64 length) = 0; + + // Call this method to initiate the request. If this method succeeds, then + // the peer's methods will be called asynchronously to report various events. + virtual bool Start(Peer* peer) = 0; + + // Call this method to cancel a request that is in progress. This method + // causes the request to immediately transition into the 'done' state. The + // OnCompletedRequest method will be called asynchronously; this assumes + // the peer is still valid. + virtual void Cancel() = 0; + + // Call this method to suspend or resume a load that is in progress. This + // method may only be called after a successful call to the Start method. + virtual void SetDefersLoading(bool value) = 0; + + // See the SyncLoad method declared below. (The name of this struct is not + // suffixed with "Info" because it also contains the response data.) + struct SyncLoadResponse : ResponseInfo { + // The response status. + URLRequestStatus status; + + // The final URL of the response. This may differ from the request URL in + // the case of a server redirect. + GURL url; + + // The response data. + std::string data; + }; + + // Call this method to load the resource synchronously (i.e., in one shot). + // This is an alternative to the Start method. Be warned that this method + // will block the calling thread until the resource is fully downloaded or an + // error occurs. It could block the calling thread for a long time, so only + // use this if you really need it! There is also no way for the caller to + // interrupt this method. Errors are reported via the status field of the + // response parameter. + virtual void SyncLoad(SyncLoadResponse* response) = 0; + + protected: + // construction must go through Create() + ResourceLoaderBridge() {} + + private: + DISALLOW_EVIL_CONSTRUCTORS(ResourceLoaderBridge); +}; + +} // namespace webkit_glue + +#endif // RESOURCE_LOADER_BRIDGE__ diff --git a/webkit/glue/resource_type.h b/webkit/glue/resource_type.h new file mode 100644 index 0000000..d426bcf --- /dev/null +++ b/webkit/glue/resource_type.h @@ -0,0 +1,61 @@ +// Copyright 2008, 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 WEBKIT_GLUE_RESOURCE_TYPE_H__ +#define WEBKIT_GLUE_RESOURCE_TYPE_H__ + + +class ResourceType { + public: + enum Type { + MAIN_FRAME = 0, // top level page + SUB_FRAME, // frame or iframe + SUB_RESOURCE, // a resource like images, js, css + OBJECT, // an object (or embed) tag for a plugin, + // or a resource that a plugin requested. + }; + + static bool ValidType(int32 type) { + return type >= MAIN_FRAME && type <= OBJECT; + } + + static Type FromInt(int32 type) { + return static_cast<Type>(type); + } + + static bool IsFrame(ResourceType::Type type) { + return type == MAIN_FRAME || type == SUB_FRAME; + } + + private: + // Don't instantiate this class. + ResourceType(); + ~ResourceType(); +}; +#endif // WEBKIT_GLUE_RESOURCE_TYPE_H__ diff --git a/webkit/glue/resources/README.txt b/webkit/glue/resources/README.txt new file mode 100644 index 0000000..678a136 --- /dev/null +++ b/webkit/glue/resources/README.txt @@ -0,0 +1,50 @@ +The following files in this directory are copied from the Mozilla source tree, +where they are licensed under the MPL/GPL/LGPL. While binary files do not +contain a license block, I have pasted the relevant Mozilla license block at +the bottom of this file. + +aliasb.cur +broken-image.gif +cell.cur +col_resize.cur +copy.cur +grab.cur +grabbing.cur +row_resize.cur +vertical_text.cur +zoom_in.cur +zoom_out.cur + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ diff --git a/webkit/glue/resources/aliasb.cur b/webkit/glue/resources/aliasb.cur Binary files differnew file mode 100644 index 0000000..8d9ac94 --- /dev/null +++ b/webkit/glue/resources/aliasb.cur diff --git a/webkit/glue/resources/broken-image.gif b/webkit/glue/resources/broken-image.gif Binary files differnew file mode 100644 index 0000000..735e10c --- /dev/null +++ b/webkit/glue/resources/broken-image.gif diff --git a/webkit/glue/resources/cell.cur b/webkit/glue/resources/cell.cur Binary files differnew file mode 100644 index 0000000..decfbdc --- /dev/null +++ b/webkit/glue/resources/cell.cur diff --git a/webkit/glue/resources/col_resize.cur b/webkit/glue/resources/col_resize.cur Binary files differnew file mode 100644 index 0000000..8f7f675 --- /dev/null +++ b/webkit/glue/resources/col_resize.cur diff --git a/webkit/glue/resources/copy.cur b/webkit/glue/resources/copy.cur Binary files differnew file mode 100644 index 0000000..87f1519c --- /dev/null +++ b/webkit/glue/resources/copy.cur diff --git a/webkit/glue/resources/dash.png b/webkit/glue/resources/dash.png Binary files differnew file mode 100644 index 0000000..1747604 --- /dev/null +++ b/webkit/glue/resources/dash.png diff --git a/webkit/glue/resources/feed.html b/webkit/glue/resources/feed.html new file mode 100644 index 0000000..c6a5747d --- /dev/null +++ b/webkit/glue/resources/feed.html @@ -0,0 +1,18 @@ +<html> + <head> + <title>Feed Content</title> + <style type='text/css'> + body { + margin: 5ex 7ex; + font-family: sans-serif; + } + </style> + </head> + <body> + <p>This is a <a href="http://en.wikipedia.org/wiki/Web_feed">feed</a> that + can be displayed in a feed reader.</p> + + <p><a href="http://www.google.com/ig/add?feedurl={{URL}}">Subscribe via + Google</a>.</p> + </body> +</html> diff --git a/webkit/glue/resources/row_resize.cur b/webkit/glue/resources/row_resize.cur Binary files differnew file mode 100644 index 0000000..a7369d3 --- /dev/null +++ b/webkit/glue/resources/row_resize.cur diff --git a/webkit/glue/resources/vertical_text.cur b/webkit/glue/resources/vertical_text.cur Binary files differnew file mode 100644 index 0000000..3de04eb --- /dev/null +++ b/webkit/glue/resources/vertical_text.cur diff --git a/webkit/glue/resources/webkit_strings_ar.xtb b/webkit/glue/resources/webkit_strings_ar.xtb new file mode 100644 index 0000000..f348455 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_ar.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="ar"> +<translation id="7658239707568436148">إلغاء</translation> +<translation id="3789841737615482174">تثبيت</translation> +<translation id="8141602879876242471">هذا فهرس يمكن البحث فيه. أدخل كلمات البحث الرئيسية:</translation> +<translation id="2653659639078652383">تقديم</translation> +<translation id="5939518447894949180">إعادة تعيين</translation> +<translation id="7364796246159120393">اختيار ملف</translation> +<translation id="2548326553472216322">لا توجد عمليات بحث جرت مؤخرًا</translation> +<translation id="6663448176199120256">عمليات البحث الأخيرة</translation> +<translation id="1235745349614807883">مسح عمليات البحث الأخيرة</translation> +<translation id="6807599807928161586">منطقة الويب</translation> +<translation id="3040011195152428237">رابط</translation> +<translation id="5048533449481078685">محدِد القائمة</translation> +<translation id="8244226242650769279">مخطط صورة</translation> +<translation id="8597182159515967513">العنوان</translation> +<translation id="5944544982112848342">2048 (درجة عالية)</translation> +<translation id="2846343701378493991">1024 (درجة متوسطة)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">لم يتم تثبيت المكون الإضافي <ph name="PLUGIN"/></translation> +<translation id="4838490908464673667">لم يتم تثبيت المكون الإضافي المطلوب</translation> +<translation id="3600343118165084788">انقر هنا لتنزيل مكون إضافي</translation> +<translation id="8281246460978372009">بعد تثبيت المكون الإضافي، انقر هنا للتحديث</translation> +<translation id="679352192834563463">لا يتوفر أي مكون إضافي لعرض هذا المحتوى</translation> +<translation id="8662565117025751661">جارٍ تنزيل مكون إضافي...</translation> +<translation id="3771786644471114952">الحصول على مكون إضافي</translation> +<translation id="1275511093094545429">يلزم توفر مكون <ph name="PLUGIN"/> الإضافي</translation> +<translation id="3825324228893189080">يلزم توفر مكون إضافي آخر</translation> +<translation id="4420062214988137980">فشل تثبيت المكون الإضافي</translation> +<translation id="4317653869502688143">يُرجى تأكيد رغبتك في تثبيت المكون الإضافي <ph name="PLUGIN"/>. يجب تثبيت المكونات الإضافية التي تثق فيها فقط.</translation> +<translation id="3926627843712816530">يُرجى تأكيد رغبتك في تثبيت هذا المكون الإضافي. يجب تثبيت المكونات الإضافية التي تثق فيها فقط.</translation> +<translation id="1383141426028388991">فشل تثبيت مكون إضافي من <ph name="URL"/></translation> +<translation id="6845533974506654842">اضغط</translation> +<translation id="1842960171412779397">تحديد</translation> +<translation id="6119846243427417423">تنشيط</translation> +<translation id="5476505524087279545">عدم تحديد</translation> +<translation id="838869780401515933">تحديد</translation> +<translation id="4202807286478387388">انتقال</translation> +<translation id="795667975304826397">لم يتم اختيار أي ملف</translation> +<translation id="8964020114565522021">سحب ملف إلى هنا</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_bg.xtb b/webkit/glue/resources/webkit_strings_bg.xtb new file mode 100644 index 0000000..79558b7 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_bg.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="bg"> +<translation id="7658239707568436148">Отказ</translation> +<translation id="3789841737615482174">Инсталиране</translation> +<translation id="8141602879876242471">В този индекс може да се търси. Въведете ключови думи за търсене:</translation> +<translation id="2653659639078652383">Изпращане</translation> +<translation id="5939518447894949180">Повторно задаване</translation> +<translation id="7364796246159120393">Избор на файл</translation> +<translation id="2548326553472216322">Няма скорошни търсения</translation> +<translation id="6663448176199120256">Скорошни търсения</translation> +<translation id="1235745349614807883">Изчистване на скорошните търсения</translation> +<translation id="6807599807928161586">уеб зона</translation> +<translation id="3040011195152428237">връзка</translation> +<translation id="5048533449481078685">списъчен показалец</translation> +<translation id="8244226242650769279">карта с изображения</translation> +<translation id="8597182159515967513">заглавие</translation> +<translation id="5944544982112848342">2048 (висока степен на сложност)</translation> +<translation id="2846343701378493991">1024 (средна степен на сложност)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Приставката за <ph name="PLUGIN"/> не е инсталирана</translation> +<translation id="4838490908464673667">Необходимата приставка не е инсталирана</translation> +<translation id="3600343118165084788">Кликнете тук, за да изтеглите приставката</translation> +<translation id="8281246460978372009">След като инсталирате приставката, кликнете тук, за да опресните прозореца</translation> +<translation id="679352192834563463">Няма приставка, с която може да се покаже това съдържание</translation> +<translation id="8662565117025751661">Приставката се изтегля...</translation> +<translation id="3771786644471114952">Изтегляне на приставката</translation> +<translation id="1275511093094545429">Необходима е приставка за <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Изисква се допълнителна приставка</translation> +<translation id="4420062214988137980">Инсталирането на приставката не бе успешно</translation> +<translation id="4317653869502688143">Моля, потвърдете, че искате да инсталирате приставката за <ph name="PLUGIN"/>. Инсталирайте само тези приставки, които считате за надеждни.</translation> +<translation id="3926627843712816530">Моля, потвърдете, че искате да инсталирате тази приставка. Инсталирайте само тези приставки, които считате за надеждни.</translation> +<translation id="1383141426028388991">Инсталирането на приставката от <ph name="URL"/> не бе успешно</translation> +<translation id="6845533974506654842">натискане</translation> +<translation id="1842960171412779397">Избиране</translation> +<translation id="6119846243427417423">активиране</translation> +<translation id="5476505524087279545">премахване на отметката</translation> +<translation id="838869780401515933">отмятане</translation> +<translation id="4202807286478387388">преминаване</translation> +<translation id="795667975304826397">Няма избран файл</translation> +<translation id="8964020114565522021">Плъзнете файла тук</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_ca.xtb b/webkit/glue/resources/webkit_strings_ca.xtb new file mode 100644 index 0000000..4b8a689 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_ca.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="ca"> +<translation id="7658239707568436148">Cancel·la</translation> +<translation id="3789841737615482174">Instal·la</translation> +<translation id="8141602879876242471">És un índex on es poden realitzar cerques. Introdueix els termes de cerca:</translation> +<translation id="2653659639078652383">Envia</translation> +<translation id="5939518447894949180">Restablir</translation> +<translation id="7364796246159120393">Selecciona el fitxer</translation> +<translation id="2548326553472216322">No hi ha cerques recents</translation> +<translation id="6663448176199120256">Cerques recents</translation> +<translation id="1235745349614807883">Esborra les cerques recents</translation> +<translation id="6807599807928161586">àrea web</translation> +<translation id="3040011195152428237">enllaç</translation> +<translation id="5048533449481078685">marcador de llistes</translation> +<translation id="8244226242650769279">mapa d'imatges</translation> +<translation id="8597182159515967513">Capçalera</translation> +<translation id="5944544982112848342">2048 (Gran)</translation> +<translation id="2846343701378493991">1024 (Mitjà)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">El complement <ph name="PLUGIN"/> no està instal·lat</translation> +<translation id="4838490908464673667">El complement necessari no està instal·lat</translation> +<translation id="3600343118165084788">Fes clic aquí per baixar el complement</translation> +<translation id="8281246460978372009">Després d'instal·lar el complement, fes clic aquí per actualitzar.</translation> +<translation id="679352192834563463">No hi ha cap complement disponible per mostrar aquest contingut</translation> +<translation id="8662565117025751661">S'està baixant el complement...</translation> +<translation id="3771786644471114952">Obtén el complement</translation> +<translation id="1275511093094545429">Es necessita el complement <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Es necessita un complement addicional</translation> +<translation id="4420062214988137980">Error en la instal·lació del complement</translation> +<translation id="4317653869502688143">Confirma que vols instal·lar el complement <ph name="PLUGIN"/>. Només hauries d'instal·lar complements fiables.</translation> +<translation id="3926627843712816530">Confirma que vols instal·lar aquest complement. Només hauries d'instal·lar complements fiables.</translation> +<translation id="1383141426028388991">Error en instal·lar el complement des de <ph name="URL"/></translation> +<translation id="6845533974506654842">prem</translation> +<translation id="1842960171412779397">selecciona</translation> +<translation id="6119846243427417423">activa</translation> +<translation id="5476505524087279545">desmarca</translation> +<translation id="838869780401515933">marca</translation> +<translation id="4202807286478387388">salta</translation> +<translation id="795667975304826397">No heu seleccionat cap fitxer.</translation> +<translation id="8964020114565522021">Arrossegueu el fitxer aquí.</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_cs.xtb b/webkit/glue/resources/webkit_strings_cs.xtb new file mode 100644 index 0000000..92b2e91 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_cs.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="cs"> +<translation id="7658239707568436148">Zrušit</translation> +<translation id="3789841737615482174">Instalovat</translation> +<translation id="8141602879876242471">Toto je prohledávatelný index. Zadejte hledaná klíčová slova:</translation> +<translation id="2653659639078652383">Odeslat</translation> +<translation id="5939518447894949180">Resetovat</translation> +<translation id="7364796246159120393">Vybrat soubor</translation> +<translation id="2548326553472216322">Žádná nedávná vyhledávání</translation> +<translation id="6663448176199120256">Nedávná vyhledávání</translation> +<translation id="1235745349614807883">Smazat nedávná vyhledávání</translation> +<translation id="6807599807928161586">oblast webu</translation> +<translation id="3040011195152428237">odkaz</translation> +<translation id="5048533449481078685">značka seznamu</translation> +<translation id="8244226242650769279">obrázková mapa</translation> +<translation id="8597182159515967513">záhlaví</translation> +<translation id="5944544982112848342">2048 (vysoká kvalita)</translation> +<translation id="2846343701378493991">1024 (Střední kvalita)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Není nainstalován plugin <ph name="PLUGIN"/></translation> +<translation id="4838490908464673667">Požadovaný plugin není nainstalován</translation> +<translation id="3600343118165084788">Pro stažení pluginu klikněte sem</translation> +<translation id="8281246460978372009">Po instalaci pluginu klikněte sem pro obnovení</translation> +<translation id="679352192834563463">Není k dispozici žádný plugin pro zobrazení tohoto obsahu</translation> +<translation id="8662565117025751661">Probíhá stahování pluginu...</translation> +<translation id="3771786644471114952">Získat plugin</translation> +<translation id="1275511093094545429">Je potřeba plugin <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Je potřeba další plugin</translation> +<translation id="4420062214988137980">Nepodařilo se nainstalovat plugin</translation> +<translation id="4317653869502688143">Potvrďte prosím, že si přejete instalovat plugin <ph name="PLUGIN"/>. Měli byste instalovat pouze takové pluginy, kterým důvěřujete.</translation> +<translation id="3926627843712816530">Potvrďte, prosím, že si přejete instalovat tento plugin. Měli byste instalovat pouze takové pluginy, kterým důvěřujete.</translation> +<translation id="1383141426028388991">Nepodařilo se nainstalovat plugin z <ph name="URL"/></translation> +<translation id="6845533974506654842">zmáčknout</translation> +<translation id="1842960171412779397">zvolit</translation> +<translation id="6119846243427417423">aktivovat</translation> +<translation id="5476505524087279545">odstranit zaškrtnutí</translation> +<translation id="838869780401515933">zaškrtnout</translation> +<translation id="4202807286478387388">přejít</translation> +<translation id="795667975304826397">Nebyl zvolen žádný soubor</translation> +<translation id="8964020114565522021">Přetáhnout soubor sem</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_da.xtb b/webkit/glue/resources/webkit_strings_da.xtb new file mode 100644 index 0000000..30f2494 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_da.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="da"> +<translation id="7658239707568436148">Annuller</translation> +<translation id="3789841737615482174">Installer</translation> +<translation id="8141602879876242471">Der kan søges i dette indeks. Indtast søge-nøgleord:</translation> +<translation id="2653659639078652383">Indsend</translation> +<translation id="5939518447894949180">Nulstil</translation> +<translation id="7364796246159120393">Vælg fil</translation> +<translation id="2548326553472216322">Ingen nylige søgninger</translation> +<translation id="6663448176199120256">Nylige søgninger</translation> +<translation id="1235745349614807883">Slet nylige søgninger</translation> +<translation id="6807599807928161586">webområde</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">listemarkering</translation> +<translation id="8244226242650769279">billedekort</translation> +<translation id="8597182159515967513">overskrift</translation> +<translation id="5944544982112848342">2048 (Høj klasse)</translation> +<translation id="2846343701378493991">1024 (Mellemklasse)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> plugin er ikke installeret</translation> +<translation id="4838490908464673667">Det krævede plugin er ikke installeret</translation> +<translation id="3600343118165084788">Klik her for at downloade plugin</translation> +<translation id="8281246460978372009">Når du har installeret plugin'et, skal du klikke her for at opdatere</translation> +<translation id="679352192834563463">Intet tilgængeligt plugin kan vise dette indhold</translation> +<translation id="8662565117025751661">Downloader plugin ...</translation> +<translation id="3771786644471114952">Hent plugin</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> Plugin påkrævet</translation> +<translation id="3825324228893189080">Ekstra plugin påkrævet</translation> +<translation id="4420062214988137980">Installation af plugin mislykkedes</translation> +<translation id="4317653869502688143">Bekræft venligst, at du ønsker at installere plugin'et <ph name="PLUGIN"/>. Du bør udelukkende installere plugins, som du stoler på.</translation> +<translation id="3926627843712816530">Bekræft venligst, at du gerne vil installere dette plugin. Du bør udelukkende installere plugins, som du stoler på.</translation> +<translation id="1383141426028388991">Mislykket installation af plugin fra <ph name="URL"/></translation> +<translation id="6845533974506654842">tryk</translation> +<translation id="1842960171412779397">vælg</translation> +<translation id="6119846243427417423">aktiver</translation> +<translation id="5476505524087279545">fjern markering</translation> +<translation id="838869780401515933">marker</translation> +<translation id="4202807286478387388">hop</translation> +<translation id="795667975304826397">Der er ikke valgt nogen fil</translation> +<translation id="8964020114565522021">Træk filer hertil</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_de.xtb b/webkit/glue/resources/webkit_strings_de.xtb new file mode 100644 index 0000000..48a5f68 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_de.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="de"> +<translation id="7658239707568436148">Abbrechen</translation> +<translation id="3789841737615482174">Installieren</translation> +<translation id="8141602879876242471">Dieser Index kann durchsucht werden. Geben Sie Suchbegriffe ein:</translation> +<translation id="2653659639078652383">Senden</translation> +<translation id="5939518447894949180">Zurücksetzen</translation> +<translation id="7364796246159120393">Datei auswählen</translation> +<translation id="2548326553472216322">Keine vor kurzem durchgeführte Suchanfragen</translation> +<translation id="6663448176199120256">Vor kurzem durchgeführte Suchanfragen</translation> +<translation id="1235745349614807883">Vor kurzem durchgeführte Suchanfragen löschen</translation> +<translation id="6807599807928161586">Webbereich</translation> +<translation id="3040011195152428237">Link</translation> +<translation id="5048533449481078685">Listenmarkierung</translation> +<translation id="8244226242650769279">Imagemap</translation> +<translation id="8597182159515967513">Kopfzeile</translation> +<translation id="5944544982112848342">2048 (High Grade)</translation> +<translation id="2846343701378493991">1024 (mittlere Stufe)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Plug-in <ph name="PLUGIN"/> nicht installiert</translation> +<translation id="4838490908464673667">Das erforderliche Plug-in ist nicht installiert</translation> +<translation id="3600343118165084788">Klicken Sie hier, um das Plug-in herunterzuladen.</translation> +<translation id="8281246460978372009">Klicken Sie nach der Installation des Plug-ins hier, um eine Aktualisierung durchzuführen.</translation> +<translation id="679352192834563463">Kein Plug-in zum Anzeigen dieses Contents verfügbar</translation> +<translation id="8662565117025751661">Plug-in wird heruntergeladen...</translation> +<translation id="3771786644471114952">Plug-in abrufen</translation> +<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> erforderlich</translation> +<translation id="3825324228893189080">Zusätzliches Plug-in erforderlich</translation> +<translation id="4420062214988137980">Plug-in-Installation fehlgeschlagen</translation> +<translation id="4317653869502688143">Bestätigen Sie, dass Sie das Plug-in <ph name="PLUGIN"/> installieren möchten. Sie sollten nur vertrauenswürdige Plug-ins installieren.</translation> +<translation id="3926627843712816530">Bestätigen Sie, dass Sie dieses Plug-in installieren möchten. Sie sollten nur vertrauenswürdige Plug-ins installieren.</translation> +<translation id="1383141426028388991">Installieren des Plug-ins von <ph name="URL"/> fehlgeschlagen</translation> +<translation id="6845533974506654842">klicken</translation> +<translation id="1842960171412779397">auswählen</translation> +<translation id="6119846243427417423">aktivieren</translation> +<translation id="5476505524087279545">Auswahl aufheben</translation> +<translation id="838869780401515933">auswählen</translation> +<translation id="4202807286478387388">springen</translation> +<translation id="795667975304826397">Keine Datei ausgewählt</translation> +<translation id="8964020114565522021">Datei hier ablegen</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_el.xtb b/webkit/glue/resources/webkit_strings_el.xtb new file mode 100644 index 0000000..aca3a3c --- /dev/null +++ b/webkit/glue/resources/webkit_strings_el.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="el"> +<translation id="7658239707568436148">Ακύρωση</translation> +<translation id="3789841737615482174">Εγκατάσταση</translation> +<translation id="8141602879876242471">Πρόκειται για ευρετήριο με δυνατότητα αναζήτησης. Πληκτρολογήστε λέξεις-κλειδιά αναζήτησης:</translation> +<translation id="2653659639078652383">Υποβολή</translation> +<translation id="5939518447894949180">Επαναφορά</translation> +<translation id="7364796246159120393">Επιλογή αρχείου</translation> +<translation id="2548326553472216322">Δεν υπάρχουν πρόσφατες αναζητήσεις</translation> +<translation id="6663448176199120256">Πρόσφατες αναζητήσεις</translation> +<translation id="1235745349614807883">Εκκαθάριση πρόσφατων αναζητήσεων</translation> +<translation id="6807599807928161586">περιοχή ιστού</translation> +<translation id="3040011195152428237">σύνδεσμος</translation> +<translation id="5048533449481078685">δείκτης λίστας</translation> +<translation id="8244226242650769279">χάρτης εικόνας</translation> +<translation id="8597182159515967513">επικεφαλίδα</translation> +<translation id="5944544982112848342">2048 (Υψηλός βαθμός)</translation> +<translation id="2846343701378493991">1024 (Μέτριος βαθμός)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Η προσθήκη <ph name="PLUGIN"/> δεν έχει εγκατασταθεί</translation> +<translation id="4838490908464673667">Η απαιτούμενη προσθήκη δεν έχει εγκατασταθεί</translation> +<translation id="3600343118165084788">Κάντε κλικ εδώ για να κατεβάσετε την προσθήκη</translation> +<translation id="8281246460978372009">Μετά την εγκατάσταση της προσθήκης, κάντε κλικ εδώ για ανανέωση</translation> +<translation id="679352192834563463">Δεν υπάρχει διαθέσιμη προσθήκη για την εμφάνιση του περιεχομένου</translation> +<translation id="8662565117025751661">Λήψη προσθήκης...</translation> +<translation id="3771786644471114952">Λήψη προσθήκης</translation> +<translation id="1275511093094545429">Χρειάζεται η προσθήκη <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Απαιτείται επιπλέον προσθήκη</translation> +<translation id="4420062214988137980">Η εγκατάσταση της προσθήκης απέτυχε</translation> +<translation id="4317653869502688143">Επιβεβαιώστε ότι θέλετε να εγκαταστήσετε την προσθήκη <ph name="PLUGIN"/>. Πρέπει να εγκαθιστάτε μόνο προσθήκες που θεωρείτε αξιόπιστες.</translation> +<translation id="3926627843712816530">Επιβεβαιώστε ότι θέλετε να εγκαταστήσετε αυτή την προσθήκη. Πρέπει να εγκαθιστάτε μόνο προσθήκες που θεωρείτε αξιόπιστες.</translation> +<translation id="1383141426028388991">Η εγκατάσταση της προσθήκης από τη διεύθυνση <ph name="URL"/> απέτυχε</translation> +<translation id="6845533974506654842">πατήστε</translation> +<translation id="1842960171412779397">επιλογή</translation> +<translation id="6119846243427417423">ενεργοποίηση</translation> +<translation id="5476505524087279545">απενεργοποίηση</translation> +<translation id="838869780401515933">ενεργοποίηση</translation> +<translation id="4202807286478387388">μεταπήδηση</translation> +<translation id="795667975304826397">Δεν έχει επιλεγεί κανένα αρχείο</translation> +<translation id="8964020114565522021">Σύρετε το αρχείο εδώ</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_en-GB.xtb b/webkit/glue/resources/webkit_strings_en-GB.xtb new file mode 100644 index 0000000..c06499c --- /dev/null +++ b/webkit/glue/resources/webkit_strings_en-GB.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="en-GB"> +<translation id="7658239707568436148">Cancel</translation> +<translation id="3789841737615482174">Install</translation> +<translation id="8141602879876242471">This is a searchable index. Enter search keywords:</translation> +<translation id="2653659639078652383">Submit</translation> +<translation id="5939518447894949180">Reset</translation> +<translation id="7364796246159120393">Choose File</translation> +<translation id="2548326553472216322">No recent searches</translation> +<translation id="6663448176199120256">Recent Searches</translation> +<translation id="1235745349614807883">Clear Recent Searches</translation> +<translation id="6807599807928161586">web area</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">list marker</translation> +<translation id="8244226242650769279">image map</translation> +<translation id="8597182159515967513">heading</translation> +<translation id="5944544982112848342">2048 (High Grade)</translation> +<translation id="2846343701378493991">1024 (Medium Grade)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> plug-in is not installed</translation> +<translation id="4838490908464673667">The required plug-in is not installed</translation> +<translation id="3600343118165084788">Click here to download plug-in</translation> +<translation id="8281246460978372009">After installing the plug-in, click here to refresh</translation> +<translation id="679352192834563463">No plug-in available to display this content</translation> +<translation id="8662565117025751661">Downloading plug-in...</translation> +<translation id="3771786644471114952">Get Plug-in</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> plug-in needed</translation> +<translation id="3825324228893189080">Additional plug-in needed</translation> +<translation id="4420062214988137980">Plug-in installation failed</translation> +<translation id="4317653869502688143">Please confirm that you would like to install the <ph name="PLUGIN"/> plug-in. You should only install plug-ins that you trust.</translation> +<translation id="3926627843712816530">Please confirm that you would like to install this plug-in. You should only install plug-ins that you trust.</translation> +<translation id="1383141426028388991">Failed to install plug-in from <ph name="URL"/></translation> +<translation id="6845533974506654842">press</translation> +<translation id="1842960171412779397">select</translation> +<translation id="6119846243427417423">activate</translation> +<translation id="5476505524087279545">untick</translation> +<translation id="838869780401515933">tick</translation> +<translation id="4202807286478387388">jump</translation> +<translation id="795667975304826397">No file chosen</translation> +<translation id="8964020114565522021">Drag file here</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_es-419.xtb b/webkit/glue/resources/webkit_strings_es-419.xtb new file mode 100644 index 0000000..c6f1f60 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_es-419.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="es-419"> +<translation id="7658239707568436148">Cancelar</translation> +<translation id="3789841737615482174">Instalar</translation> +<translation id="8141602879876242471">Se trata de un índice que admite búsquedas. Escribe las palabras clave de búsqueda:</translation> +<translation id="2653659639078652383">Enviar</translation> +<translation id="5939518447894949180">Restablecer</translation> +<translation id="7364796246159120393">Seleccionar archivo</translation> +<translation id="2548326553472216322">No hay búsquedas recientes</translation> +<translation id="6663448176199120256">Búsquedas recientes</translation> +<translation id="1235745349614807883">Eliminar búsquedas recientes</translation> +<translation id="6807599807928161586">área web</translation> +<translation id="3040011195152428237">enlace</translation> +<translation id="5048533449481078685">marcador de listas</translation> +<translation id="8244226242650769279">mapa de imágenes</translation> +<translation id="8597182159515967513">cabecera</translation> +<translation id="5944544982112848342">2048 (Grado elevado)</translation> +<translation id="2846343701378493991">1024 (Mediano)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">El plug-in <ph name="PLUGIN"/> no está instalado</translation> +<translation id="4838490908464673667">El plug-in necesario no está instalado.</translation> +<translation id="3600343118165084788">Haz clic aquí para descargar el plug-in.</translation> +<translation id="8281246460978372009">Tras instalar el complemento, haz clic aquí para actualizar.</translation> +<translation id="679352192834563463">No hay ningún plug-in disponible para mostrar este contenido</translation> +<translation id="8662565117025751661">Descargando plug-in...</translation> +<translation id="3771786644471114952">Obtener plug-in</translation> +<translation id="1275511093094545429">Se requiere el plug-in <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Se necesita un plug-in adicional</translation> +<translation id="4420062214988137980">Error en la instalación del plug-in</translation> +<translation id="4317653869502688143">Confirma que deseas instalar el plug-in <ph name="PLUGIN"/>. Solo debes instalar plug-in fiables.</translation> +<translation id="3926627843712816530">Confirma que deseas instalar este plug-in. Solo debes instalar plug-in fiables.</translation> +<translation id="1383141426028388991">Error al instalar el plug-in desde <ph name="URL"/></translation> +<translation id="6845533974506654842">hacer clic</translation> +<translation id="1842960171412779397">seleccionar</translation> +<translation id="6119846243427417423">activar</translation> +<translation id="5476505524087279545">desmarcar</translation> +<translation id="838869780401515933">marcar</translation> +<translation id="4202807286478387388">saltar</translation> +<translation id="795667975304826397">No se eligió ningún archivo</translation> +<translation id="8964020114565522021">Arrastre el archivo hasta aquí</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_es.xtb b/webkit/glue/resources/webkit_strings_es.xtb new file mode 100644 index 0000000..39bc5f0 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_es.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="es"> +<translation id="7658239707568436148">Cancelar</translation> +<translation id="3789841737615482174">Instalar</translation> +<translation id="8141602879876242471">Se trata de un índice que admite búsquedas. Introduce las palabras clave de búsqueda:</translation> +<translation id="2653659639078652383">Enviar</translation> +<translation id="5939518447894949180">Restablecer</translation> +<translation id="7364796246159120393">Seleccionar archivo</translation> +<translation id="2548326553472216322">No hay búsquedas recientes</translation> +<translation id="6663448176199120256">Búsquedas recientes</translation> +<translation id="1235745349614807883">Eliminar búsquedas recientes</translation> +<translation id="6807599807928161586">área web</translation> +<translation id="3040011195152428237">enlace</translation> +<translation id="5048533449481078685">marcador de listas</translation> +<translation id="8244226242650769279">mapa de imágenes</translation> +<translation id="8597182159515967513">cabecera</translation> +<translation id="5944544982112848342">2048 (Grado elevado)</translation> +<translation id="2846343701378493991">1024 (Mediano)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">El plug-in <ph name="PLUGIN"/> no está instalado</translation> +<translation id="4838490908464673667">El plug-in necesario no está instalado.</translation> +<translation id="3600343118165084788">Haz clic aquí para descargar el plug-in.</translation> +<translation id="8281246460978372009">Tras instalar el complemento, haz clic aquí para actualizar.</translation> +<translation id="679352192834563463">No hay ningún plug-in disponible para mostrar este contenido</translation> +<translation id="8662565117025751661">Descargando plug-in...</translation> +<translation id="3771786644471114952">Obtener plug-in</translation> +<translation id="1275511093094545429">Se requiere el plug-in <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Se necesita un plug-in adicional</translation> +<translation id="4420062214988137980">Error en la instalación del plug-in</translation> +<translation id="4317653869502688143">Confirma que deseas instalar el plug-in <ph name="PLUGIN"/>. Solo debes instalar plug-in fiables.</translation> +<translation id="3926627843712816530">Confirma que deseas instalar este plug-in. Solo debes instalar plug-in fiables.</translation> +<translation id="1383141426028388991">Error al instalar el plug-in desde <ph name="URL"/></translation> +<translation id="6845533974506654842">pulsar</translation> +<translation id="1842960171412779397">seleccionar</translation> +<translation id="6119846243427417423">activar</translation> +<translation id="5476505524087279545">desmarcar</translation> +<translation id="838869780401515933">marcar</translation> +<translation id="4202807286478387388">saltar</translation> +<translation id="795667975304826397">No se ha seleccionado ningun archivo</translation> +<translation id="8964020114565522021">Arrastrar archivo hasta aquí</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_et.xtb b/webkit/glue/resources/webkit_strings_et.xtb new file mode 100644 index 0000000..4b4c10e --- /dev/null +++ b/webkit/glue/resources/webkit_strings_et.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="et"> +<translation id="7658239707568436148">Loobu</translation> +<translation id="3789841737615482174">Installi</translation> +<translation id="8141602879876242471">See on otsitav indeks. Sisestage otsingu jaoks märksõnad:</translation> +<translation id="2653659639078652383">Esita</translation> +<translation id="5939518447894949180">Lähtesta</translation> +<translation id="7364796246159120393">Vali fail</translation> +<translation id="2548326553472216322">Pole viimaseid otsingud</translation> +<translation id="6663448176199120256">Viimased otsingud</translation> +<translation id="1235745349614807883">Kustuta viimased otsingud</translation> +<translation id="6807599807928161586">veebiala</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">loendilooja</translation> +<translation id="8244226242650769279">hüperpilt</translation> +<translation id="8597182159515967513">pealkiri</translation> +<translation id="5944544982112848342">2048 (kõrge)</translation> +<translation id="2846343701378493991">1024 (keskmine)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> lisandmoodulit ei installita</translation> +<translation id="4838490908464673667">Nõutav lisandmoodul pole installitud</translation> +<translation id="3600343118165084788">Lisandmooduli allalaadimiseks klõpsake siia.</translation> +<translation id="8281246460978372009">Pärast lisandmooduli installimist klõpsake värskendamiseks siia</translation> +<translation id="679352192834563463">Selle sisu kuvamiseks pole saadaval lisandmoodulit</translation> +<translation id="8662565117025751661">Lisandmooduli allalaadimine...</translation> +<translation id="3771786644471114952">Hangi lisandmoodul</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> vajalik lisandmoodul</translation> +<translation id="3825324228893189080">Vajalik täiendav lisandmoodul</translation> +<translation id="4420062214988137980">Lisandmooduli install ebaõnnestus</translation> +<translation id="4317653869502688143">Palun kinnitage, et soovite installida selle lisandmooduli <ph name="PLUGIN"/>. Te peaksite installima lisandmooduleid, mida usaldate.</translation> +<translation id="3926627843712816530">Palun kinnitage, et soovite installida selle lisandmooduli. Te peaksite installima lisandmooduleid, mida usaldate.</translation> +<translation id="1383141426028388991">Lisandmooduli installimine asukohast <ph name="URL"/> ebaõnnestus.</translation> +<translation id="6845533974506654842">vajuta</translation> +<translation id="1842960171412779397">vali</translation> +<translation id="6119846243427417423">aktiveeri</translation> +<translation id="5476505524087279545">eemalda mrgistus</translation> +<translation id="838869780401515933">mrgista</translation> +<translation id="4202807286478387388">liigu</translation> +<translation id="795667975304826397">Ühtegi faili pole valitud</translation> +<translation id="8964020114565522021">Lohistage fail siia</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_fi.xtb b/webkit/glue/resources/webkit_strings_fi.xtb new file mode 100644 index 0000000..040bded --- /dev/null +++ b/webkit/glue/resources/webkit_strings_fi.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="fi"> +<translation id="7658239707568436148">Peruuta</translation> +<translation id="3789841737615482174">Asenna</translation> +<translation id="8141602879876242471">Tämä on haettavissa oleva hakemisto. Anna hakusanat:</translation> +<translation id="2653659639078652383">Lähetä</translation> +<translation id="5939518447894949180">Tyhjennä</translation> +<translation id="7364796246159120393">Valitse tiedosto</translation> +<translation id="2548326553472216322">Ei viimeisimpiä hakuja</translation> +<translation id="6663448176199120256">Viimeisimmät haut</translation> +<translation id="1235745349614807883">Poista viimeisimmät haut</translation> +<translation id="6807599807928161586">verkkoalue</translation> +<translation id="3040011195152428237">linkki</translation> +<translation id="5048533449481078685">luettelon merkitsijä</translation> +<translation id="8244226242650769279">kuvakartta</translation> +<translation id="8597182159515967513">otsikko</translation> +<translation id="5944544982112848342">2048 (korkea taso)</translation> +<translation id="2846343701378493991">1024 (keskitaso)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Laajennusta <ph name="PLUGIN"/> ei asennettu</translation> +<translation id="4838490908464673667">Vaadittua laajennusta ei ole asennettu</translation> +<translation id="3600343118165084788">Lataa laajennus napsauttamalla tätä</translation> +<translation id="8281246460978372009">Päivitä laajennuksen asennuksen jälkeen napsauttamalla tästä</translation> +<translation id="679352192834563463">Tämän sisällön näyttämiseen ei ole saatavissa laajennusta</translation> +<translation id="8662565117025751661">Ladataan laajennusta...</translation> +<translation id="3771786644471114952">Hae laajennus</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> laajennus tarvitaan</translation> +<translation id="3825324228893189080">Lisälaajennus tarvitaan</translation> +<translation id="4420062214988137980">Laajennuksen asennus epäonnistui</translation> +<translation id="4317653869502688143">Vahvista, että haluat asentaa laajennuksen <ph name="PLUGIN"/>. Sinun tulee asentaa vain luotettavia laajennuksia.</translation> +<translation id="3926627843712816530">Vahvista, että haluat asentaa tämän laajennuksen. Sinun tulee asentaa vain luotettavia laajennuksia.</translation> +<translation id="1383141426028388991">Laajennuksen asennus osoitteesta <ph name="URL"/> epäonnistui</translation> +<translation id="6845533974506654842">paina</translation> +<translation id="1842960171412779397">Valitse</translation> +<translation id="6119846243427417423">aktivoi</translation> +<translation id="5476505524087279545">poista valinta</translation> +<translation id="838869780401515933">valitse</translation> +<translation id="4202807286478387388">siirry</translation> +<translation id="795667975304826397">Ei valittua tiedostoa</translation> +<translation id="8964020114565522021">Vedä tiedosto tähän</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_fil.xtb b/webkit/glue/resources/webkit_strings_fil.xtb new file mode 100644 index 0000000..cf13799 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_fil.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="fil"> +<translation id="7658239707568436148">Ikansela</translation> +<translation id="3789841737615482174">Install</translation> +<translation id="8141602879876242471">Isa itong paghahanap ng index. Ipasok ang paghahanap sa mga keyword:</translation> +<translation id="2653659639078652383">Isumite</translation> +<translation id="5939518447894949180">I-reset</translation> +<translation id="7364796246159120393">Pumili ng File</translation> +<translation id="2548326553472216322">Walang kamakailang mga paghahanap</translation> +<translation id="6663448176199120256">Kasalukuyang Mga Paghahanap</translation> +<translation id="1235745349614807883">Lisiman ang Kasalukuyang Mga Paghahanap</translation> +<translation id="6807599807928161586">web area</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">Ilista ang marker</translation> +<translation id="8244226242650769279">mapa ng imahe</translation> +<translation id="8597182159515967513">heading</translation> +<translation id="5944544982112848342">2048 (Pinakamataas na Marka)</translation> +<translation id="2846343701378493991">1024 (Katamtamang Grado)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> hindi na-install ang plugin</translation> +<translation id="4838490908464673667">Ang kinakailangan na plugin ay hindi na-install</translation> +<translation id="3600343118165084788">Mag-click dito upagn mai-download ang plugin</translation> +<translation id="8281246460978372009">Matapos ang pag-install ng plugin, mag-click dito upang mag-refresh</translation> +<translation id="679352192834563463">Walang magagamit na plugin na ipapakita sa nilalaman nito</translation> +<translation id="8662565117025751661">Nagda-download ng plugin...</translation> +<translation id="3771786644471114952">Kunin ang Plugin</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> kinakailangang plugin</translation> +<translation id="3825324228893189080">Kailangan ng karagdagang plugin</translation> +<translation id="4420062214988137980">Nabigong pag-install sa plugin</translation> +<translation id="4317653869502688143">Mangyaring kumpirmahin na nais mong i-install ang <ph name="PLUGIN"/> plugin. Dapat mo lamang i-install ang mga plugin na pinagkakatiwalaan mo.</translation> +<translation id="3926627843712816530">Mangyaring kumpirmahin na dapat gusto mong i-install sa plugin na ito. Dapat mo lamang i-install ang mga plugin na pinagkakatiwalaan mo.</translation> +<translation id="1383141426028388991">Nabigong na-install ang plugin mula sa <ph name="URL"/></translation> +<translation id="6845533974506654842">pindutin</translation> +<translation id="1842960171412779397">piliin</translation> +<translation id="6119846243427417423">isaaktibo</translation> +<translation id="5476505524087279545">i-uncheck</translation> +<translation id="838869780401515933">I-tsek</translation> +<translation id="4202807286478387388">tumalon</translation> +<translation id="795667975304826397">Walang napiling file</translation> +<translation id="8964020114565522021">Kaldkarin dito ang file</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_fr.xtb b/webkit/glue/resources/webkit_strings_fr.xtb new file mode 100644 index 0000000..0af3156 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_fr.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="fr"> +<translation id="7658239707568436148">Annuler</translation> +<translation id="3789841737615482174">Installer</translation> +<translation id="8141602879876242471">Vous pouvez lancer des recherches dans cet index. Pour cela, entrez des mots clés de recherche :</translation> +<translation id="2653659639078652383">Valider</translation> +<translation id="5939518447894949180">Réinitialiser</translation> +<translation id="7364796246159120393">Choisissez un fichier</translation> +<translation id="2548326553472216322">Aucune recherche récente</translation> +<translation id="6663448176199120256">Recherches récentes</translation> +<translation id="1235745349614807883">Effacer les recherches récentes</translation> +<translation id="6807599807928161586">Zone Web</translation> +<translation id="3040011195152428237">Lien</translation> +<translation id="5048533449481078685">marqueur de liste</translation> +<translation id="8244226242650769279">image map</translation> +<translation id="8597182159515967513">en-tête</translation> +<translation id="5944544982112848342">2048 (haute sécurité)</translation> +<translation id="2846343701378493991">1024 (sécurité moyenne)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/> × <ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Le plug-in <ph name="PLUGIN"/> n'est pas installé.</translation> +<translation id="4838490908464673667">Le plug-in requis n'est pas installé.</translation> +<translation id="3600343118165084788">Cliquez ici pour télécharger le plug-in</translation> +<translation id="8281246460978372009">Après l'installation du plug-in, cliquez ici pour actualiser.</translation> +<translation id="679352192834563463">Aucun plug-in disponible pour afficher ce contenu</translation> +<translation id="8662565117025751661">Téléchargement du plug-in...</translation> +<translation id="3771786644471114952">Ajouter le plug-in</translation> +<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> requis</translation> +<translation id="3825324228893189080">Plug-in supplémentaire requis</translation> +<translation id="4420062214988137980">Échec de l'installation du plug-in</translation> +<translation id="4317653869502688143">Veuillez confirmer que vous souhaitez installer le plug-in <ph name="PLUGIN"/>. Veillez à installer uniquement des plug-ins approuvés.</translation> +<translation id="3926627843712816530">Veuillez confirmer que vous souhaitez installer ce plug-in. Vous devez uniquement installer des plug-ins approuvés.</translation> +<translation id="1383141426028388991">Échec de l'installation du plug-in depuis <ph name="URL"/></translation> +<translation id="6845533974506654842">appuyer</translation> +<translation id="1842960171412779397">sélectionner</translation> +<translation id="6119846243427417423">activer</translation> +<translation id="5476505524087279545">décocher</translation> +<translation id="838869780401515933">cocher</translation> +<translation id="4202807286478387388">accéder</translation> +<translation id="795667975304826397">Aucun fichier choisi</translation> +<translation id="8964020114565522021">Placer le fichier ici</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_he.xtb b/webkit/glue/resources/webkit_strings_he.xtb new file mode 100644 index 0000000..f6cc4ac --- /dev/null +++ b/webkit/glue/resources/webkit_strings_he.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="he"> +<translation id="4317653869502688143">אשר שברצונך להתקין את התוסף <ph name="PLUGIN"/>. עליך להתקין רק תוספים אתה בוטח.</translation> +<translation id="3926627843712816530">אנא אשר שברצונך להתקין תוסף זה. עליך להתקין רק תוספים שבהם אתה בוטח.</translation> +<translation id="1383141426028388991">התקנת התוסף מ-<ph name="URL"/> נכשלה</translation> +<translation id="7658239707568436148">ביטול</translation> +<translation id="6119846243427417423">הפעל</translation> +<translation id="6845533974506654842">לחץ</translation> +<translation id="838869780401515933">סמן</translation> +<translation id="7364796246159120393">בחר קובץ</translation> +<translation id="1235745349614807883">הסר חיפושים אחרונים</translation> +<translation id="3600343118165084788">לחץ כאן להורדת התוסף</translation> +<translation id="8597182159515967513">כותרת</translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> אינו מותקן</translation> +<translation id="2846343701378493991">1024 (Medium Grade)</translation> +<translation id="8141602879876242471">זהו אינדקס שניתן לבצע בו חיפוש. הזן מילות מפתח לחיפוש:</translation> +<translation id="3771786644471114952">קבל תוסף</translation> +<translation id="4202807286478387388">קפוץ</translation> +<translation id="679352192834563463">אין תוסף זמין להצגת תוכן זה</translation> +<translation id="3825324228893189080">דרוש תוסף נוסף</translation> +<translation id="3789841737615482174">התקן</translation> +<translation id="795667975304826397">לא נבחר קובץ</translation> +<translation id="8244226242650769279">מפת תמונות</translation> +<translation id="6663448176199120256">חיפושים אחרונים</translation> +<translation id="2653659639078652383">שלח</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="4838490908464673667">התוסף המבוקש אינו מותקן</translation> +<translation id="1275511093094545429">נדרש plugin <ph name="PLUGIN"/></translation> +<translation id="8964020114565522021">גרור את הקובץ לכאן</translation> +<translation id="5939518447894949180">אפס</translation> +<translation id="8281246460978372009">לאחר התקנת התוסף, לחץ כאן לרענון</translation> +<translation id="6807599807928161586">אזור אינטרנט</translation> +<translation id="3040011195152428237">קישור</translation> +<translation id="4420062214988137980">התקנת פלגא-אין נכשלה</translation> +<translation id="5944544982112848342">2048 (High Grade)</translation> +<translation id="5048533449481078685">סמן רשימה</translation> +<translation id="2548326553472216322">אין חיפושים אחרונים</translation> +<translation id="5476505524087279545">בטל סימון</translation> +<translation id="1842960171412779397">בחר</translation> +<translation id="8662565117025751661">מוריד תוסף...</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_hi.xtb b/webkit/glue/resources/webkit_strings_hi.xtb new file mode 100644 index 0000000..3ee472c --- /dev/null +++ b/webkit/glue/resources/webkit_strings_hi.xtb @@ -0,0 +1,36 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="hi"> +<translation id="7658239707568436148">रद्द करें</translation> +<translation id="3789841737615482174">स्थापित करें</translation> +<translation id="8141602879876242471">यह एक खोजने योग्य इंडेक्स है. खोज कुंजीशब्द प्रविष्ट करें :</translation> +<translation id="2653659639078652383">जमा करें</translation> +<translation id="5939518447894949180">पुन: सेट करें</translation> +<translation id="7364796246159120393">फ़ाइल चुनें</translation> +<translation id="2548326553472216322">हाल ही में कोई खोज नहीं</translation> +<translation id="6663448176199120256">हाल ही में की गई खोजें</translation> +<translation id="1235745349614807883">हाल ही की खोजें साफ़ करें</translation> +<translation id="6807599807928161586">वेब क्षेत्र</translation> +<translation id="3040011195152428237">लिंक</translation> +<translation id="5048533449481078685">सूची चिन्हक</translation> +<translation id="8244226242650769279">चित्र मैप</translation> +<translation id="8597182159515967513">हेडिंग</translation> +<translation id="5944544982112848342">2048 (उच्च ग्रेड)</translation> +<translation id="2846343701378493991">1024 (मध्यम ग्रेड)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> प्लगइन स्थापित नहीं किया गया है</translation> +<translation id="4838490908464673667">आवश्यक प्लगइन स्थापित नहीं है</translation> +<translation id="3600343118165084788">प्लगइन डाउनलोड करने के लिए यहाँ क्लिक करें</translation> +<translation id="8281246460978372009">प्लगइन स्थापित करने के बाद, पुन: ताज़ा करने के लिए यहाँ क्लिक करें</translation> +<translation id="679352192834563463">इस सामग्री को प्रदर्शित करने के लिए कोई प्लगइन उपलब्ध नहीं है</translation> +<translation id="8662565117025751661">प्लगइन डाउनलोड कर रहा है ...</translation> +<translation id="3771786644471114952">प्लगइन प्राप्त करें</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> प्लगइन आवश्यक</translation> +<translation id="3825324228893189080">अतिरिक्त प्लगइन आवश्यक</translation> +<translation id="4420062214988137980">प्लगइन स्थापना असफ़ल रही</translation> +<translation id="4317653869502688143">कृपया पुष्टि करें कि आप <ph name="PLUGIN"/> प्लगइन स्थापित करना चाहते हैं. आपके केवल वे प्लगइन स्थापित करने चाहिए जिनपर आप भरोसा करते हों.</translation> +<translation id="3926627843712816530">कृपया पुष्टि करें कि आप यह प्लगइन स्थापित करना चाहते हैं. आपके केवल वे प्लगइन स्थापित करने चाहिए जिनपर आप भरोसा करते हों.</translation> +<translation id="1383141426028388991"><ph name="URL"/> से प्लगइन स्थापित करने में असफ़ल रहा</translation> +<translation id="795667975304826397">कोई फाइल नहीं चुनी गई</translation> +<translation id="8964020114565522021">फाइल खींचकर यहाँ लाएं</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_hr.xtb b/webkit/glue/resources/webkit_strings_hr.xtb new file mode 100644 index 0000000..4c1cfb8 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_hr.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="hr"> +<translation id="7658239707568436148">Odustani</translation> +<translation id="3789841737615482174">Instaliraj</translation> +<translation id="8141602879876242471">Ovaj je indeks moguće pretraživati. Unesite ključne riječi za pretraživanje:</translation> +<translation id="2653659639078652383">Pošalji</translation> +<translation id="5939518447894949180">Ponovno postavi</translation> +<translation id="7364796246159120393">Odaberi datoteku</translation> +<translation id="2548326553472216322">Nema najnovijih pretraživanja</translation> +<translation id="6663448176199120256">Najnovija pretraživanja</translation> +<translation id="1235745349614807883">Obriši najnovija pretraživanja</translation> +<translation id="6807599807928161586">web područje</translation> +<translation id="3040011195152428237">veza</translation> +<translation id="5048533449481078685">oznaka popisa</translation> +<translation id="8244226242650769279">karta slika</translation> +<translation id="8597182159515967513">naslov</translation> +<translation id="5944544982112848342">2048 (visoki stupanj)</translation> +<translation id="2846343701378493991">1024 (srednji)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Nije instaliran dodatak za <ph name="PLUGIN"/></translation> +<translation id="4838490908464673667">Potreban dodatak nije instaliran</translation> +<translation id="3600343118165084788">Kliknite ovdje za preuzimanje dodatka</translation> +<translation id="8281246460978372009">Nakon instalacije dodatka, kliknite ovdje za osvježenje stranice</translation> +<translation id="679352192834563463">Nema dostupnog dodatka za prikaz ovog sadržaja</translation> +<translation id="8662565117025751661">Preuzimanje dodatka...</translation> +<translation id="3771786644471114952">Dohvati dodatak</translation> +<translation id="1275511093094545429">Potreban je dodatak za <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Potreban je dodatak</translation> +<translation id="4420062214988137980">Nije uspjela instalacija dodatka</translation> +<translation id="4317653869502688143">Potvrdite da želite instalirati dodatak za <ph name="PLUGIN"/> Instalirajte samo one dodatke kojima vjerujete.</translation> +<translation id="3926627843712816530">Potvrdite da želite instalirati ovaj dodatak. Instalirajte samo one dodatke kojima vjerujete.</translation> +<translation id="1383141426028388991">Nije uspjela instalacija dodatka s <ph name="URL"/></translation> +<translation id="6845533974506654842">pritisni</translation> +<translation id="1842960171412779397">odaberi</translation> +<translation id="6119846243427417423">aktiviraj</translation> +<translation id="5476505524087279545">ukloni oznaku</translation> +<translation id="838869780401515933">označi</translation> +<translation id="4202807286478387388">skoči</translation> +<translation id="795667975304826397">Nije odabrana niti jedna datoteka.</translation> +<translation id="8964020114565522021">Povucite datoteku ovamo</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_hu.xtb b/webkit/glue/resources/webkit_strings_hu.xtb new file mode 100644 index 0000000..9d467c8 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_hu.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="hu"> +<translation id="7658239707568436148">Mégse</translation> +<translation id="3789841737615482174">Telepítés</translation> +<translation id="8141602879876242471">Ez egy kereshető index. Írjon be keresési kulcsszavakat:</translation> +<translation id="2653659639078652383">Elküldés</translation> +<translation id="5939518447894949180">Visszaállítás</translation> +<translation id="7364796246159120393">Fájl kiválasztása</translation> +<translation id="2548326553472216322">Nincsenek friss keresések</translation> +<translation id="6663448176199120256">Friss keresések</translation> +<translation id="1235745349614807883">Friss keresések törlése</translation> +<translation id="6807599807928161586">internetes terület</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">listajelölő</translation> +<translation id="8244226242650769279">képtérkép</translation> +<translation id="8597182159515967513">fejléc</translation> +<translation id="5944544982112848342">2048 (magasfokú)</translation> +<translation id="2846343701378493991">1024 (Közepes)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">A(z) <ph name="PLUGIN"/> plugin nem lett telepítve</translation> +<translation id="4838490908464673667">A szükséges plugin nem lett telepítve</translation> +<translation id="3600343118165084788">Ha ide kattint, letöltheti a plugint</translation> +<translation id="8281246460978372009">A plugin telepítését követően ide kattintson a frissítéshez</translation> +<translation id="679352192834563463">Nincs elérhető plugin a tartalom megjelenítéséhez</translation> +<translation id="8662565117025751661">Plugin letöltése...</translation> +<translation id="3771786644471114952">Plugin beszerzése</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> plugin szükséges</translation> +<translation id="3825324228893189080">További plugin szükséges</translation> +<translation id="4420062214988137980">A plugin telepítése nem sikerült</translation> +<translation id="4317653869502688143">Kérjük, erősítse meg, hogy telepíteni szeretné a(z) <ph name="PLUGIN"/> plugint. Csak azokat a plugineket telepítse, amelyekben megbízik.</translation> +<translation id="3926627843712816530">Kérjük, erősítse meg, hogy telepíteni szeretné ezt a plugint. Csak azokat a plugineket telepítse, amelyekben megbízik.</translation> +<translation id="1383141426028388991">A plugin telepítése nem sikerült a következő helyről: <ph name="URL"/></translation> +<translation id="6845533974506654842">Gomb lenyomása</translation> +<translation id="1842960171412779397">Kiválasztás</translation> +<translation id="6119846243427417423">Aktiválás</translation> +<translation id="5476505524087279545">Megjelölés eltávolítása</translation> +<translation id="838869780401515933">Megjelölés</translation> +<translation id="4202807286478387388">Mehet</translation> +<translation id="795667975304826397">Nem lett fájl kiválasztva</translation> +<translation id="8964020114565522021">Húzza át ide a fájlt</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_id.xtb b/webkit/glue/resources/webkit_strings_id.xtb new file mode 100644 index 0000000..5b68dce --- /dev/null +++ b/webkit/glue/resources/webkit_strings_id.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="id"> +<translation id="7658239707568436148">Batal</translation> +<translation id="3789841737615482174">Instal</translation> +<translation id="8141602879876242471">Terdapat indeks yang dapat dicari. Masukkan kata kunci penelusuran:</translation> +<translation id="2653659639078652383">Kirim</translation> +<translation id="5939518447894949180">Atur ulang</translation> +<translation id="7364796246159120393">Pilih Berkas</translation> +<translation id="2548326553472216322">Tidak ada penelusuran terkini</translation> +<translation id="6663448176199120256">Penelusuran Barusan</translation> +<translation id="1235745349614807883">Hapus Penelusuran Barusan</translation> +<translation id="6807599807928161586">area Web</translation> +<translation id="3040011195152428237">tautan</translation> +<translation id="5048533449481078685">penanda daftar</translation> +<translation id="8244226242650769279">gambar peta</translation> +<translation id="8597182159515967513">kepala</translation> +<translation id="5944544982112848342">2048 (Tingkat Tinggi)</translation> +<translation id="2846343701378493991">1024 (Tingkat Menengah)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/> - <ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> plug-in belum terinstal</translation> +<translation id="4838490908464673667">Plug-in yang diperlukan belum terinstal</translation> +<translation id="3600343118165084788">Klik di sini untuk mengunduh plug-in</translation> +<translation id="8281246460978372009">Setelah menginstal plug-in, klik di sini untuk me-refresh</translation> +<translation id="679352192834563463">Tidak tersedia plug-in untuk menampilkan konten ini</translation> +<translation id="8662565117025751661">Mengunduh plug-in...</translation> +<translation id="3771786644471114952">Dapatkan Plug-in</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> diperlukan plug-in</translation> +<translation id="3825324228893189080">Diperlukan plug-in tambahan</translation> +<translation id="4420062214988137980">Penginstalan plug-in gagal</translation> +<translation id="4317653869502688143">Konfirmasikan bahwa Anda ingin menginstal plug-in<ph name="PLUGIN"/>. Sebaiknya hanya instal plug-in yang Anda percaya.</translation> +<translation id="3926627843712816530">Konfirmasikan bahwa Anda ingin menginstal plug-in ini. Sebaiknya hanya instal plug-in yang Anda percaya.</translation> +<translation id="1383141426028388991">Gagal menginstal plug-in dari <ph name="URL"/></translation> +<translation id="6845533974506654842">tekan</translation> +<translation id="1842960171412779397">pilih</translation> +<translation id="6119846243427417423">aktifkan</translation> +<translation id="5476505524087279545">batalkan centang</translation> +<translation id="838869780401515933">centangi</translation> +<translation id="4202807286478387388">lompati</translation> +<translation id="795667975304826397">Tidak ada file yang dipilih</translation> +<translation id="8964020114565522021">Tarik file ke sini</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_it.xtb b/webkit/glue/resources/webkit_strings_it.xtb new file mode 100644 index 0000000..4cce7eb --- /dev/null +++ b/webkit/glue/resources/webkit_strings_it.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="it"> +<translation id="7658239707568436148">Annulla</translation> +<translation id="3789841737615482174">Installa</translation> +<translation id="8141602879876242471">Questo è un indice di ricerca. Inserisci le parole chiave di ricerca:</translation> +<translation id="2653659639078652383">Invia</translation> +<translation id="5939518447894949180">Ripristina</translation> +<translation id="7364796246159120393">Scegli file</translation> +<translation id="2548326553472216322">Nessuna ricerca recente</translation> +<translation id="6663448176199120256">Ricerche recenti</translation> +<translation id="1235745349614807883">Cancella ricerche recenti</translation> +<translation id="6807599807928161586">area web</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">indicatore elenco</translation> +<translation id="8244226242650769279">image map</translation> +<translation id="8597182159515967513">intestazione</translation> +<translation id="5944544982112848342">2048 (alta qualità)</translation> +<translation id="2846343701378493991">1024 (Medium Grade)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Plug-in <ph name="PLUGIN"/> non installato</translation> +<translation id="4838490908464673667">Il plug-in richiesto non è installato</translation> +<translation id="3600343118165084788">Fai clic qui per scaricare il plug-in</translation> +<translation id="8281246460978372009">Dopo aver installato il plug-in, fai clic qui per aggiornare</translation> +<translation id="679352192834563463">Nessun plug-in disponibile per visualizzare il contenuto</translation> +<translation id="8662565117025751661">Download del plugin in corso...</translation> +<translation id="3771786644471114952">Aggiungi plug-in</translation> +<translation id="1275511093094545429">Plugin <ph name="PLUGIN"/> necessario</translation> +<translation id="3825324228893189080">Plug-in aggiuntivo necessario</translation> +<translation id="4420062214988137980">Installazione plug-in non riuscita</translation> +<translation id="4317653869502688143">Conferma l'installazione del plug-in <ph name="PLUGIN"/>. Ti invitiamo a installare solo i plug-in che ritieni affidabili.</translation> +<translation id="3926627843712816530">Conferma l'installazione del plug-in. Ti invitiamo a installare solo i plug-in che ritieni affidabili.</translation> +<translation id="1383141426028388991">Installazione plug-in da <ph name="URL"/> non riuscita</translation> +<translation id="6845533974506654842">premi</translation> +<translation id="1842960171412779397">seleziona</translation> +<translation id="6119846243427417423">attiva</translation> +<translation id="5476505524087279545">deseleziona</translation> +<translation id="838869780401515933">seleziona</translation> +<translation id="4202807286478387388">vai</translation> +<translation id="795667975304826397">Nessun file selezionato</translation> +<translation id="8964020114565522021">Trascina il file qui</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_ja.xtb b/webkit/glue/resources/webkit_strings_ja.xtb new file mode 100644 index 0000000..6bc856e --- /dev/null +++ b/webkit/glue/resources/webkit_strings_ja.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="ja"> +<translation id="7658239707568436148">キャンセル</translation> +<translation id="3789841737615482174">インストール</translation> +<translation id="8141602879876242471">このインデックスは検索できます。キーワードを入力してください:</translation> +<translation id="2653659639078652383">送信</translation> +<translation id="5939518447894949180">リセット</translation> +<translation id="7364796246159120393">ファイルを選択</translation> +<translation id="2548326553472216322">最近の検索はありません</translation> +<translation id="6663448176199120256">最近の検索</translation> +<translation id="1235745349614807883">最近の検索履歴を消去</translation> +<translation id="6807599807928161586">ウェブ領域</translation> +<translation id="3040011195152428237">リンク</translation> +<translation id="5048533449481078685">リスト マーカー</translation> +<translation id="8244226242650769279">イメージ マップ</translation> +<translation id="8597182159515967513">見出し</translation> +<translation id="5944544982112848342">2048 (高)</translation> +<translation id="2846343701378493991">1024 (中)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> プラグインはインストールされていません</translation> +<translation id="4838490908464673667">必要なプラグインがインストールされていません</translation> +<translation id="3600343118165084788">ここをクリックしてプラグインをダウンロード</translation> +<translation id="8281246460978372009">プラグインをインストールした後は、ここをクリックして更新してください</translation> +<translation id="679352192834563463">このコンテンツの表示に使用できるプラグインはありません</translation> +<translation id="8662565117025751661">プラグインをダウンロードしています...</translation> +<translation id="3771786644471114952">プラグインの取得</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> プラグインが必要です</translation> +<translation id="3825324228893189080">追加の必要なプラグイン</translation> +<translation id="4420062214988137980">プラグインのインストールが失敗しました</translation> +<translation id="4317653869502688143"><ph name="PLUGIN"/> プラグインのインストールを確認してください。信頼できるプラグインだけインストールしてください。</translation> +<translation id="3926627843712816530">このプラグインのインストールを確認してください。信頼できるプラグインだけインストールしてください。</translation> +<translation id="1383141426028388991"><ph name="URL"/> からのプラグインのインストールに失敗しました</translation> +<translation id="6845533974506654842">押す</translation> +<translation id="1842960171412779397">選択</translation> +<translation id="6119846243427417423">アクティブにする</translation> +<translation id="5476505524087279545">チェックを外す</translation> +<translation id="838869780401515933">チェックを付ける</translation> +<translation id="4202807286478387388">ジャンプ</translation> +<translation id="795667975304826397">ファイルが選択されていません</translation> +<translation id="8964020114565522021">ファイルをここにドラッグ</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_ko.xtb b/webkit/glue/resources/webkit_strings_ko.xtb new file mode 100644 index 0000000..b688b9d --- /dev/null +++ b/webkit/glue/resources/webkit_strings_ko.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="ko"> +<translation id="7658239707568436148">취소</translation> +<translation id="3789841737615482174">설치</translation> +<translation id="8141602879876242471">이것은 검색 색인합니다. 검색 키워드 입력:</translation> +<translation id="2653659639078652383">제출</translation> +<translation id="5939518447894949180">재설정</translation> +<translation id="7364796246159120393">파일 선택</translation> +<translation id="2548326553472216322">최근 수행된 검색 없음</translation> +<translation id="6663448176199120256">최근 수행된 검색</translation> +<translation id="1235745349614807883">최근 검색 삭제</translation> +<translation id="6807599807928161586">웹 영역</translation> +<translation id="3040011195152428237">링크</translation> +<translation id="5048533449481078685">목록 표시기</translation> +<translation id="8244226242650769279">이미지 지도</translation> +<translation id="8597182159515967513">항목</translation> +<translation id="5944544982112848342">2048(높은 등급)</translation> +<translation id="2846343701378493991">1024(중간 등급)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> 플러그인이 설치되지 않음</translation> +<translation id="4838490908464673667">필수 플러그인이 설치되지 않음</translation> +<translation id="3600343118165084788">여기를 클릭하여 플러그인을 다운로드합니다.</translation> +<translation id="8281246460978372009">플러그인 설치 후 여기를 클릭하여 새로고침</translation> +<translation id="679352192834563463">이 콘텐츠를 표시하는 데 사용할 플러그인 없음</translation> +<translation id="8662565117025751661">플러그인 다운로드 중...</translation> +<translation id="3771786644471114952">플러그인 가져오기</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> 플러그인 필요</translation> +<translation id="3825324228893189080">추가 플러그인 필요</translation> +<translation id="4420062214988137980">플러그인 설치 실패</translation> +<translation id="4317653869502688143"><ph name="PLUGIN"/> 플러그인을 설치할 것인지 확인해 주세요. 신뢰하는 플러그인만 설치해야 합니다.</translation> +<translation id="3926627843712816530">해당 플러그인을 설치할 것인지 확인해 주세요. 신뢰하는 플러그인만 설치해야 합니다.</translation> +<translation id="1383141426028388991"><ph name="URL"/>의 플러그인 설치 실패</translation> +<translation id="6845533974506654842">누르기</translation> +<translation id="1842960171412779397">선택</translation> +<translation id="6119846243427417423">활성화</translation> +<translation id="5476505524087279545">선택취소</translation> +<translation id="838869780401515933">선택</translation> +<translation id="4202807286478387388">건너뛰기</translation> +<translation id="795667975304826397">선택된 파일 없음</translation> +<translation id="8964020114565522021">여기로 파일 드래그</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_lt.xtb b/webkit/glue/resources/webkit_strings_lt.xtb new file mode 100644 index 0000000..4d88715 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_lt.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="lt"> +<translation id="7658239707568436148">Atšaukti</translation> +<translation id="3789841737615482174">Diegti</translation> +<translation id="8141602879876242471">Tai yra ieškotinas indeksas. Įveskite paieškos raktinių žodžių:</translation> +<translation id="2653659639078652383">Pateikti</translation> +<translation id="5939518447894949180">Nustatyti iš naujo</translation> +<translation id="7364796246159120393">Pasirinkti failą</translation> +<translation id="2548326553472216322">Pastaruoju metu paieškų nevykdyta</translation> +<translation id="6663448176199120256">Naujausios paieškos</translation> +<translation id="1235745349614807883">Išvalyti pastarąsias paieškas</translation> +<translation id="6807599807928161586">interneto sritis</translation> +<translation id="3040011195152428237">nuoroda</translation> +<translation id="5048533449481078685">sąrašo žymeklis</translation> +<translation id="8244226242650769279">paveikslėlio žemėlapis</translation> +<translation id="8597182159515967513">antraštė</translation> +<translation id="5944544982112848342">2048 (Aukšto laipsnio)</translation> +<translation id="2846343701378493991">1024 (vidutinio lygio)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> papildinys neįdiegtas</translation> +<translation id="4838490908464673667">Neįdiegtas reikalingas papildinys</translation> +<translation id="3600343118165084788">Spustelėkite čia, kad atsisiųstumėte papildinį</translation> +<translation id="8281246460978372009">Įdiegę papildinį paspauskite čia, kad būtų perkrautas puslapis</translation> +<translation id="679352192834563463">Nėra papildinio, galinčio parodyti šį turinį</translation> +<translation id="8662565117025751661">Siunčiamas papildinys...</translation> +<translation id="3771786644471114952">Siųsti papildinį</translation> +<translation id="1275511093094545429">Reikia <ph name="PLUGIN"/> papildinio (-ių)</translation> +<translation id="3825324228893189080">Reikia papidomo papildinio</translation> +<translation id="4420062214988137980">Papildinio įdiegti nepavyko</translation> +<translation id="4317653869502688143">Patvirtinkite, kad norite diegti <ph name="PLUGIN"/> papildinį. Diekite tik tuos papildinius, kuriais tikrai pasitikite.</translation> +<translation id="3926627843712816530">Patvirtinkite, kad norite diegti šį papildinį. Diekite tik tuos papildinius, kuriais tikrai pasitikite.</translation> +<translation id="1383141426028388991">Nepavyko įdiegti papildinio iš <ph name="URL"/></translation> +<translation id="6845533974506654842">paspausti</translation> +<translation id="1842960171412779397">pasirinkti</translation> +<translation id="6119846243427417423">aktyvinti</translation> +<translation id="5476505524087279545">Nuimti žymėjimą</translation> +<translation id="838869780401515933">tikrinti</translation> +<translation id="4202807286478387388">peršokti</translation> +<translation id="795667975304826397">Nepasirinktas joks failas</translation> +<translation id="8964020114565522021">Vilkite failą čia</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_lv.xtb b/webkit/glue/resources/webkit_strings_lv.xtb new file mode 100644 index 0000000..ce2afd5 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_lv.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="lv"> +<translation id="7658239707568436148">Atcelt</translation> +<translation id="3789841737615482174">Iestatīt</translation> +<translation id="8141602879876242471">Šis ir indekss ar meklēšanas iespējām. Ievadīt meklēšanas atslēgvārdus:</translation> +<translation id="2653659639078652383">Iesniegt</translation> +<translation id="5939518447894949180">Atiestatīt</translation> +<translation id="7364796246159120393">Izvēlieties failu</translation> +<translation id="2548326553472216322">Nav nesenu meklējumu</translation> +<translation id="6663448176199120256">Neseni meklējumi</translation> +<translation id="1235745349614807883">Dzēst nesenos meklējumus</translation> +<translation id="6807599807928161586">tīmekļa apgabals</translation> +<translation id="3040011195152428237">saite</translation> +<translation id="5048533449481078685">sarakstu marķieris</translation> +<translation id="8244226242650769279">attēlu karte</translation> +<translation id="8597182159515967513">Virsraksts</translation> +<translation id="5944544982112848342">2048 (Augsta Atzīme)</translation> +<translation id="2846343701378493991">1024 (Vidēja Atzīme)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> spraudnis nav iestatīts</translation> +<translation id="4838490908464673667">Nepieciešamais spraudnis nav iestatīts</translation> +<translation id="3600343118165084788">Klikšķiniet šeit, lai lejupielādētu spraudni</translation> +<translation id="8281246460978372009">Pēc spraudņa iestatīšanas, nospiediet šeit, lai atsvaidzinātu</translation> +<translation id="679352192834563463">Nav pieejamu spraudņu, lai attēlotu saturu</translation> +<translation id="8662565117025751661">Spraudņu lejupielāde...</translation> +<translation id="3771786644471114952">Iegūt spraudni</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> nepieciešams spraudnis</translation> +<translation id="3825324228893189080">Nepieciešams papildus spraudnis</translation> +<translation id="4420062214988137980">Spraudņa iestatīšana neizdevās</translation> +<translation id="4317653869502688143">Lūdzu, apstipriniet, ka vēlaties iestatīt <ph name="PLUGIN"/> spraudni. Jums vajadzētu iestatīt tikai tos spraudņus, kuriem uzticaties.</translation> +<translation id="3926627843712816530">Lūdzu, apstipriniet, ka vēlaties iestatīt šo spraudni. Jums vajadzētu iestatīt tikai tos spraudņus, kuriem uzticaties.</translation> +<translation id="1383141426028388991">Neizdevās iestatīt spraudni no <ph name="URL"/></translation> +<translation id="6845533974506654842">nospiest</translation> +<translation id="1842960171412779397">Atlasiet</translation> +<translation id="6119846243427417423">aktivizt</translation> +<translation id="5476505524087279545">neprbaudt</translation> +<translation id="838869780401515933">prbaudt</translation> +<translation id="4202807286478387388">lekt</translation> +<translation id="795667975304826397">Nav izvēlēts neviens fails</translation> +<translation id="8964020114565522021">Ievelciet failu šeit</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_nl.xtb b/webkit/glue/resources/webkit_strings_nl.xtb new file mode 100644 index 0000000..b0bd112 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_nl.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="nl"> +<translation id="7658239707568436148">Annuleren</translation> +<translation id="3789841737615482174">Installeren</translation> +<translation id="8141602879876242471">Dit is een doorzoekbare index. Geef zoekwoorden op:</translation> +<translation id="2653659639078652383">Verzenden</translation> +<translation id="5939518447894949180">Herstellen</translation> +<translation id="7364796246159120393">Bestand kiezen</translation> +<translation id="2548326553472216322">Geen recente zoekopdrachten</translation> +<translation id="6663448176199120256">Recente zoekopdrachten</translation> +<translation id="1235745349614807883">Recente zoekopdrachten wissen</translation> +<translation id="6807599807928161586">webgedeelte</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">lijstmarkering</translation> +<translation id="8244226242650769279">image map</translation> +<translation id="8597182159515967513">kop</translation> +<translation id="5944544982112848342">2048 (hoog niveau)</translation> +<translation id="2846343701378493991">1024 (gemiddeld niveau)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">De plug-in <ph name="PLUGIN"/> is niet geïnstalleerd</translation> +<translation id="4838490908464673667">De vereiste plug-in is niet geïnstalleerd</translation> +<translation id="3600343118165084788">Klik hier om de plug-in te downloaden</translation> +<translation id="8281246460978372009">Klik hier na het installeren van de invoegtoepassing om te vernieuwen</translation> +<translation id="679352192834563463">Er is geen plug-in beschikbaar om deze inhoud weer te geven</translation> +<translation id="8662565117025751661">Plug-in downloaden...</translation> +<translation id="3771786644471114952">Plug-in ophalen</translation> +<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> vereist</translation> +<translation id="3825324228893189080">Aanvullende plug-in vereist</translation> +<translation id="4420062214988137980">Installatie van plug-in mislukt</translation> +<translation id="4317653869502688143">Bevestig dat u de plugin <ph name="PLUGIN"/> wilt installeren. Installeer alleen plug-ins die u vertrouwt.</translation> +<translation id="3926627843712816530">Bevestig dat u deze plug-in wilt installeren. Installeer alleen plug-ins die u vertrouwt.</translation> +<translation id="1383141426028388991">Kan plug-in niet installeren van <ph name="URL"/></translation> +<translation id="6845533974506654842">Indrukken</translation> +<translation id="1842960171412779397">Selecteren</translation> +<translation id="6119846243427417423">Activeren</translation> +<translation id="5476505524087279545">Deselecteren</translation> +<translation id="838869780401515933">Selecteren</translation> +<translation id="4202807286478387388">Gaan naar</translation> +<translation id="795667975304826397">Geen bestand gekozen</translation> +<translation id="8964020114565522021">Sleep bestand hier naartoe</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_no.xtb b/webkit/glue/resources/webkit_strings_no.xtb new file mode 100644 index 0000000..a36b0c4 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_no.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="no"> +<translation id="7658239707568436148">Avbryt</translation> +<translation id="3789841737615482174">Installer</translation> +<translation id="8141602879876242471">Dette er en søkbar indeks. Angi søkeordene:</translation> +<translation id="2653659639078652383">Send</translation> +<translation id="5939518447894949180">Tilbakestill</translation> +<translation id="7364796246159120393">Velg fil</translation> +<translation id="2548326553472216322">Ingen nylige søk</translation> +<translation id="6663448176199120256">Nylige søk</translation> +<translation id="1235745349614807883">Fjern nylige søk</translation> +<translation id="6807599807928161586">nettområde</translation> +<translation id="3040011195152428237">kobling</translation> +<translation id="5048533449481078685">listemarkør</translation> +<translation id="8244226242650769279">bildekart</translation> +<translation id="8597182159515967513">overskrift</translation> +<translation id="5944544982112848342">2048 (sterk)</translation> +<translation id="2846343701378493991">1024 (middels)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Tilleggsmodulen <ph name="PLUGIN"/> er ikke installert</translation> +<translation id="4838490908464673667">Den nødvendige tilleggsmodulen er ikke installert</translation> +<translation id="3600343118165084788">Klikk her for å laste ned tilleggsmodulen</translation> +<translation id="8281246460978372009">Når tilleggsmodulen er installert, klikker du her for å oppdatere</translation> +<translation id="679352192834563463">Ingen tilleggsmodul tilgjengelig for å vise dette innholdet</translation> +<translation id="8662565117025751661">Laster ned tilleggsmodul...</translation> +<translation id="3771786644471114952">Få tilleggsmodul</translation> +<translation id="1275511093094545429">Tilleggsmodulen <ph name="PLUGIN"/> kreves</translation> +<translation id="3825324228893189080">Ytterligere tilleggsmodul kreves</translation> +<translation id="4420062214988137980">Installasjon av tilleggsmodul mislyktes</translation> +<translation id="4317653869502688143">Du må bekrefte om du vil installere tilleggsmodulen <ph name="PLUGIN"/>. Du bør bare installere moduler som du stoler på.</translation> +<translation id="3926627843712816530">Du må bekrefte om du vil installere tilleggsmodulen. Du bør bare installere tilleggsmoduler som du stoler på.</translation> +<translation id="1383141426028388991">Fikk ikke installert tilleggsmodulen fra <ph name="URL"/></translation> +<translation id="6845533974506654842">trykk</translation> +<translation id="1842960171412779397">velg</translation> +<translation id="6119846243427417423">aktiver</translation> +<translation id="5476505524087279545">fjern merke</translation> +<translation id="838869780401515933">merk av</translation> +<translation id="4202807286478387388">hopp</translation> +<translation id="795667975304826397">Ingen fil valgt</translation> +<translation id="8964020114565522021">Dra filen hit</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_pl.xtb b/webkit/glue/resources/webkit_strings_pl.xtb new file mode 100644 index 0000000..0acd58a --- /dev/null +++ b/webkit/glue/resources/webkit_strings_pl.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="pl"> +<translation id="7658239707568436148">Anuluj</translation> +<translation id="3789841737615482174">Zainstaluj</translation> +<translation id="8141602879876242471">Ten indeks można przeszukiwać. Wprowadź wyszukiwane słowa kluczowe:</translation> +<translation id="2653659639078652383">Prześlij</translation> +<translation id="5939518447894949180">Resetuj</translation> +<translation id="7364796246159120393">Wybierz plik</translation> +<translation id="2548326553472216322">Brak ostatnich wyszukiwań</translation> +<translation id="6663448176199120256">Ostatnie wyszukiwania</translation> +<translation id="1235745349614807883">Wyczyść ostatnie wyszukiwania</translation> +<translation id="6807599807928161586">obszar sieci</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">znacznik listy</translation> +<translation id="8244226242650769279">mapa grafiki</translation> +<translation id="8597182159515967513">nagłówek</translation> +<translation id="5944544982112848342">2048 (wysoki poziom)</translation> +<translation id="2846343701378493991">1024 (średni poziom)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Dodatek plug-in <ph name="PLUGIN"/> nie został zainstalowany</translation> +<translation id="4838490908464673667">Wymagany dodatek plug-in nie został zainstalowany</translation> +<translation id="3600343118165084788">Kliknij tutaj, aby pobrać dodatek plug-in</translation> +<translation id="8281246460978372009">Po zainstalowaniu dodatku plug-in kliknij tutaj, aby odświeżyć</translation> +<translation id="679352192834563463">Brak dostępnego dodatku plug-in umożliwiającego wyświetlenie tej treści</translation> +<translation id="8662565117025751661">Pobieranie dodatku plug-in...</translation> +<translation id="3771786644471114952">Pobierz dodatek plug-in</translation> +<translation id="1275511093094545429">Wymagany dodatek plug-in <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Wymagany dodatkowy dodatek plug-in</translation> +<translation id="4420062214988137980">Instalacja dodatku plug-in nie powiodła się</translation> +<translation id="4317653869502688143">Potwierdź instalację dodatku plug-in <ph name="PLUGIN"/>. Należy instalować wyłącznie zaufane dodatki plug-in.</translation> +<translation id="3926627843712816530">Potwierdź instalację tego dodatku plug-in. Należy instalować wyłącznie zaufane dodatki plug-in.</translation> +<translation id="1383141426028388991">Nie można zainstalować dodatku plug-in z <ph name="URL"/></translation> +<translation id="6845533974506654842">naciśnij</translation> +<translation id="1842960171412779397">wybierz</translation> +<translation id="6119846243427417423">aktywuj</translation> +<translation id="5476505524087279545">odznacz</translation> +<translation id="838869780401515933">zaznacz</translation> +<translation id="4202807286478387388">przejdź</translation> +<translation id="795667975304826397">Nie wybrano pliku</translation> +<translation id="8964020114565522021">Przeciągnij plik tutaj</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_pt-BR.xtb b/webkit/glue/resources/webkit_strings_pt-BR.xtb new file mode 100644 index 0000000..8466b83 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_pt-BR.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="pt-BR"> +<translation id="7658239707568436148">Cancelar</translation> +<translation id="3789841737615482174">Instalar</translation> +<translation id="8141602879876242471">Este é um índice pesquisável. Insira palavras-chave de pesquisa:</translation> +<translation id="2653659639078652383">Enviar</translation> +<translation id="5939518447894949180">Redefinir</translation> +<translation id="7364796246159120393">Escolher arquivo</translation> +<translation id="2548326553472216322">Nenhuma pesquisa recente</translation> +<translation id="6663448176199120256">Pesquisas recentes</translation> +<translation id="1235745349614807883">Limpar pesquisas recentes</translation> +<translation id="6807599807928161586">área da web</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">marcador de lista</translation> +<translation id="8244226242650769279">mapa da imagem</translation> +<translation id="8597182159515967513">cabeçalho</translation> +<translation id="5944544982112848342">2048 (Nota alta)</translation> +<translation id="2846343701378493991">1024 (Nota média)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">O plug-in <ph name="PLUGIN"/> não está instalado</translation> +<translation id="4838490908464673667">O plug-in necessário não está instalado</translation> +<translation id="3600343118165084788">Clique aqui para fazer download do plug-in</translation> +<translation id="8281246460978372009">Depois de instalar o plug-in, clique aqui para atualizar</translation> +<translation id="679352192834563463">Nenhum plug-in disponível para exibir este conteúdo</translation> +<translation id="8662565117025751661">Fazendo download do plug-in...</translation> +<translation id="3771786644471114952">Obter plug-in</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> plug-in necessário</translation> +<translation id="3825324228893189080">Plug-in adicional necessário</translation> +<translation id="4420062214988137980">Falha de instalação de plug-in</translation> +<translation id="4317653869502688143">Confirme se deseja instalar este plug-in <ph name="PLUGIN"/>. Instale somente plug-ins confiáveis.</translation> +<translation id="3926627843712816530">Confirme se deseja instalar este plug-in. Instale somente plug-ins confiáveis.</translation> +<translation id="1383141426028388991">Falha ao instalar plug-in a partir de <ph name="URL"/></translation> +<translation id="6845533974506654842">pressione</translation> +<translation id="1842960171412779397">selecione</translation> +<translation id="6119846243427417423">ativar</translation> +<translation id="5476505524087279545">desmarcar</translation> +<translation id="838869780401515933">marcar</translation> +<translation id="4202807286478387388">pular</translation> +<translation id="795667975304826397">Nenhum arquivo selecionado</translation> +<translation id="8964020114565522021">Arraste o arquivo até aquil</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_pt-PT.xtb b/webkit/glue/resources/webkit_strings_pt-PT.xtb new file mode 100644 index 0000000..afc7d4b1 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_pt-PT.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="pt-PT"> +<translation id="7658239707568436148">Cancelar</translation> +<translation id="3789841737615482174">Instalar</translation> +<translation id="8141602879876242471">Este índice é pesquisável. Introduza palavras-chave de pesquisa:</translation> +<translation id="2653659639078652383">Submeter</translation> +<translation id="5939518447894949180">Repor</translation> +<translation id="7364796246159120393">Escolher ficheiro</translation> +<translation id="2548326553472216322">Nenhuma pesquisa recente</translation> +<translation id="6663448176199120256">Pesquisas recentes</translation> +<translation id="1235745349614807883">Limpar pesquisas recentes</translation> +<translation id="6807599807928161586">Área Web</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">marcador de lista</translation> +<translation id="8244226242650769279">mapa de imagem</translation> +<translation id="8597182159515967513">cabeçalho</translation> +<translation id="5944544982112848342">2048 (Tamanho grande)</translation> +<translation id="2846343701378493991">1024 (Tamanho médio)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">O plug-in <ph name="PLUGIN"/> não está instalado</translation> +<translation id="4838490908464673667">O plug-in necessário não está instalado</translation> +<translation id="3600343118165084788">Clique aqui para transferir o plug-in</translation> +<translation id="8281246460978372009">Após instalar o plug-in, clique aqui para actualizar</translation> +<translation id="679352192834563463">Nenhum plug-in disponível para apresentar este conteúdo</translation> +<translation id="8662565117025751661">A transferir o plug-in…</translation> +<translation id="3771786644471114952">Obter plug-in</translation> +<translation id="1275511093094545429">Plug-in <ph name="PLUGIN"/> necessário</translation> +<translation id="3825324228893189080">Plug-in adicional necessário</translation> +<translation id="4420062214988137980">A instalação do plug-in falhou</translation> +<translation id="4317653869502688143">Confirme que pretende instalar o plug-in <ph name="PLUGIN"/>. Apenas deverá instalar plug-ins fidedignos.</translation> +<translation id="3926627843712816530">Confirme que pretende instalar este plug-in. Apenas deverá instalar plug-ins fidedignos.</translation> +<translation id="1383141426028388991">Falha ao instalar plug-in de <ph name="URL"/></translation> +<translation id="6845533974506654842">premir</translation> +<translation id="1842960171412779397">seleccionar</translation> +<translation id="6119846243427417423">activar</translation> +<translation id="5476505524087279545">desmarcar</translation> +<translation id="838869780401515933">verificar</translation> +<translation id="4202807286478387388">ir para</translation> +<translation id="795667975304826397">Nenhum ficheiro seleccionado</translation> +<translation id="8964020114565522021">Arraste o ficheiro para aqui</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_ro.xtb b/webkit/glue/resources/webkit_strings_ro.xtb new file mode 100644 index 0000000..04e57ab --- /dev/null +++ b/webkit/glue/resources/webkit_strings_ro.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="ro"> +<translation id="7658239707568436148">Anulaţi</translation> +<translation id="3789841737615482174">Instalaţi</translation> +<translation id="8141602879876242471">Acesta este un index în care se poate căuta. Introduceţi cuvintele cheie pentru căutare:</translation> +<translation id="2653659639078652383">Trimiteţi</translation> +<translation id="5939518447894949180">Resetaţi</translation> +<translation id="7364796246159120393">Alegeţi fişierul</translation> +<translation id="2548326553472216322">Nicio căutare recentă</translation> +<translation id="6663448176199120256">Căutări recente</translation> +<translation id="1235745349614807883">Ştergeţi căutările recente</translation> +<translation id="6807599807928161586">zona Web</translation> +<translation id="3040011195152428237">link</translation> +<translation id="5048533449481078685">marcator listă</translation> +<translation id="8244226242650769279">hartă cu imagini</translation> +<translation id="8597182159515967513">titlu</translation> +<translation id="5944544982112848342">2048 (Grad înalt)</translation> +<translation id="2846343701378493991">1024 (Grad mediu)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Plug-in-ul <ph name="PLUGIN"/> nu este instalat</translation> +<translation id="4838490908464673667">Plug-in-ul necesar nu este instalat</translation> +<translation id="3600343118165084788">Faceţi clic aici pentru a descărca plug-in-ul</translation> +<translation id="8281246460978372009">După instalarea plug-in-ului, faceţi clic aici pentru a reactualiza</translation> +<translation id="679352192834563463">Niciun plug-in disponibil pentru a afişa acest conţinut</translation> +<translation id="8662565117025751661">Descărcare plug-in...</translation> +<translation id="3771786644471114952">Obţineţi plug-in-ul</translation> +<translation id="1275511093094545429">Este necesar plug-in-ul <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Este necesar un plug-in suplimentar</translation> +<translation id="4420062214988137980">Instalarea plug-in-ului a eşuat</translation> +<translation id="4317653869502688143">Confirmaţi că doriţi să instalaţi plug-in-ul <ph name="PLUGIN"/>. Ar trebui să instalaţi numai plug-in-uri în care aveţi încredere.</translation> +<translation id="3926627843712816530">Confirmaţi că doriţi să instalaţi acest plug-in. Ar trebui să instalaţi numai plug-in-uri în care aveţi încredere.</translation> +<translation id="1383141426028388991">Instalarea plug-in-ului de la <ph name="URL"/> a eşuat</translation> +<translation id="6845533974506654842">Apăsaţi</translation> +<translation id="1842960171412779397">Selectaţi</translation> +<translation id="6119846243427417423">Activaţi</translation> +<translation id="5476505524087279545">Debifaţi</translation> +<translation id="838869780401515933">Bifaţi</translation> +<translation id="4202807286478387388">Salt</translation> +<translation id="795667975304826397">Nu s-au ales fişiere</translation> +<translation id="8964020114565522021">Trageţi fişierul aici</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_ru.xtb b/webkit/glue/resources/webkit_strings_ru.xtb new file mode 100644 index 0000000..6e29a72 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_ru.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="ru"> +<translation id="7658239707568436148">Отмена</translation> +<translation id="3789841737615482174">Установить</translation> +<translation id="8141602879876242471">Это индекс с возможностью поиска. Введите ключевые слова для поиска:</translation> +<translation id="2653659639078652383">Отправить</translation> +<translation id="5939518447894949180">Изменить</translation> +<translation id="7364796246159120393">Выберите файл</translation> +<translation id="2548326553472216322">Нет недавних поисков</translation> +<translation id="6663448176199120256">Недавние поиски</translation> +<translation id="1235745349614807883">Очистить недавние поиски</translation> +<translation id="6807599807928161586">область Интернетеа</translation> +<translation id="3040011195152428237">ссылка</translation> +<translation id="5048533449481078685">маркер списка</translation> +<translation id="8244226242650769279">графическая карта</translation> +<translation id="8597182159515967513">заголовок</translation> +<translation id="5944544982112848342">2048 (Крупный размер)</translation> +<translation id="2846343701378493991">1024 (Средний размер)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Плагин <ph name="PLUGIN"/> не установлен</translation> +<translation id="4838490908464673667">Не установлен необходимый плагин.</translation> +<translation id="3600343118165084788">Нажмите здесь, чтобы загрузить плагин</translation> +<translation id="8281246460978372009">После установки подключаемого модуля нажмите здесь для обновления</translation> +<translation id="679352192834563463">Недоступен плагин для отображения этого содержания</translation> +<translation id="8662565117025751661">Загрузка подключаемого модуля...</translation> +<translation id="3771786644471114952">Получить плагин</translation> +<translation id="1275511093094545429">Необходим плагин <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Необходим дополнительный плагин</translation> +<translation id="4420062214988137980">Ошибка при установке плагина</translation> +<translation id="4317653869502688143">Подтвердите, что хотите установить плагин <ph name="PLUGIN"/>. Устанавливать следует модули только тех издателей, которым вы доверяете.</translation> +<translation id="3926627843712816530">Подтвердите, что хотите установить этот плагин. Устанавливать следует модули только тех издателей, которым вы доверяете.</translation> +<translation id="1383141426028388991">Не удалось установить плагин с <ph name="URL"/></translation> +<translation id="6845533974506654842">нажать</translation> +<translation id="1842960171412779397">выбрать</translation> +<translation id="6119846243427417423">активировать</translation> +<translation id="5476505524087279545">снять галочку</translation> +<translation id="838869780401515933">поставить галочку</translation> +<translation id="4202807286478387388">перейти</translation> +<translation id="795667975304826397">Файл не выбран</translation> +<translation id="8964020114565522021">Перетащите файл сюда</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_sk.xtb b/webkit/glue/resources/webkit_strings_sk.xtb new file mode 100644 index 0000000..c429d1a --- /dev/null +++ b/webkit/glue/resources/webkit_strings_sk.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="sk"> +<translation id="7658239707568436148">Zrušiť</translation> +<translation id="3789841737615482174">Inštalovať</translation> +<translation id="8141602879876242471">Tento index sa dá prehľadávať. Zadajte kľúčové slová na vyhľadanie:</translation> +<translation id="2653659639078652383">Odoslať</translation> +<translation id="5939518447894949180">Vynulovať</translation> +<translation id="7364796246159120393">Vybrať súbor</translation> +<translation id="2548326553472216322">Žiadne posledné vyhľadávania</translation> +<translation id="6663448176199120256">Posledné vyhľadávania</translation> +<translation id="1235745349614807883">Vyčistiť posledné vyhľadávania</translation> +<translation id="6807599807928161586">webová oblasť</translation> +<translation id="3040011195152428237">odkaz</translation> +<translation id="5048533449481078685">ukazovateľ v zozname</translation> +<translation id="8244226242650769279">mapa obrázka</translation> +<translation id="8597182159515967513">nadpis</translation> +<translation id="5944544982112848342">2048 (vysoký stupeň)</translation> +<translation id="2846343701378493991">1024 (stredný stupeň)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/> <ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Nie je nainštalovaný doplnkový modul <ph name="PLUGIN"/></translation> +<translation id="4838490908464673667">Nie je nainštalovaný vyžadovaný doplnkový modul.</translation> +<translation id="3600343118165084788">Kliknite sem, ak chcete prevziať doplnkový modul</translation> +<translation id="8281246460978372009">Po inštalácii doplnkového modulu kliknite sem kvôli obnoveniu obsahu</translation> +<translation id="679352192834563463">Na zobrazenie tohto obsahu nie je k dispozícii žiadny doplnkový modul</translation> +<translation id="8662565117025751661">Preberá sa doplnkový modul...</translation> +<translation id="3771786644471114952">Získať doplnkový modul</translation> +<translation id="1275511093094545429">Vyžaduje sa doplnkový modul <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Vyžaduje sa ďalší doplnkový modul</translation> +<translation id="4420062214988137980">Nedal sa nainštalovať doplnkový modul</translation> +<translation id="4317653869502688143">Potvrďte, že naozaj chcete nainštalovať doplnkový modul <ph name="PLUGIN"/>. Mali by ste inštalovať len doplnkové moduly, ktorým dôverujete.</translation> +<translation id="3926627843712816530">Potvrďte, že naozaj chcete nainštalovať tento doplnkový modul. Mali by ste inštalovať len doplnkové moduly, ktorým dôverujete.</translation> +<translation id="1383141426028388991">Nedal sa nainštalovať doplnkový modul z adresy <ph name="URL"/></translation> +<translation id="6845533974506654842">stlačiť</translation> +<translation id="1842960171412779397">vybrať</translation> +<translation id="6119846243427417423">aktivovať</translation> +<translation id="5476505524087279545">zrušiť označenie</translation> +<translation id="838869780401515933">označiť</translation> +<translation id="4202807286478387388">skok</translation> +<translation id="795667975304826397">Nie je vybratý žiadny súbor</translation> +<translation id="8964020114565522021">Súbor presunúť sem</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_sl.xtb b/webkit/glue/resources/webkit_strings_sl.xtb new file mode 100644 index 0000000..dd0e2ed --- /dev/null +++ b/webkit/glue/resources/webkit_strings_sl.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="sl"> +<translation id="7658239707568436148">Prekliči</translation> +<translation id="3789841737615482174">Namesti</translation> +<translation id="8141602879876242471">To je kazalo, ki omogoča iskanje. Vnesite ključne besede za iskanje:</translation> +<translation id="2653659639078652383">Pošlji</translation> +<translation id="5939518447894949180">Ponastavi</translation> +<translation id="7364796246159120393">Izberi datoteko</translation> +<translation id="2548326553472216322">Ni zadnjih iskanj</translation> +<translation id="6663448176199120256">Zadnja iskanja</translation> +<translation id="1235745349614807883">Počisti zadnja iskanja</translation> +<translation id="6807599807928161586">spletno področje</translation> +<translation id="3040011195152428237">povezava</translation> +<translation id="5048533449481078685">označevalnik seznama</translation> +<translation id="8244226242650769279">slikovni zemljevid</translation> +<translation id="8597182159515967513">naslov</translation> +<translation id="5944544982112848342">2048 (visoka stopnja)</translation> +<translation id="2846343701378493991">1024 (srednja stopnja)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Vtičnik <ph name="PLUGIN"/> ni nameščen</translation> +<translation id="4838490908464673667">Potrebni vtičnik ni nameščen</translation> +<translation id="3600343118165084788">Za prenos vtičnika kliknite tukaj</translation> +<translation id="8281246460978372009">Po namestitvi vtičnika za osvežitev kliknite tu</translation> +<translation id="679352192834563463">Za prikaz te vsebine ni na voljo noben vtičnik</translation> +<translation id="8662565117025751661">Prenos vtičnika ...</translation> +<translation id="3771786644471114952">Dobite vtičnik</translation> +<translation id="1275511093094545429">Potreben je vtičnik <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Potreben je dodatni vtičnik</translation> +<translation id="4420062214988137980">Namestitev vtičnika ni bila uspešna</translation> +<translation id="4317653869502688143">Prosimo potrdite, da želite namestiti vtičnik <ph name="PLUGIN"/>. Priporočamo vam, da namestite le vtičnike, ki so vredni zaupanja.</translation> +<translation id="3926627843712816530">Potrdite, da želite namestiti ta vtičnik. Priporočamo vam, da namestite le vtičnike, ki so vredni zaupanja.</translation> +<translation id="1383141426028388991">Namestitev vtičnika z naslova <ph name="URL"/> ni bila uspešna.</translation> +<translation id="6845533974506654842">pritisni</translation> +<translation id="1842960171412779397">izberi</translation> +<translation id="6119846243427417423">aktiviraj</translation> +<translation id="5476505524087279545">počisti izbor</translation> +<translation id="838869780401515933">potrdi</translation> +<translation id="4202807286478387388">skoči</translation> +<translation id="795667975304826397">Nobena datoteka ni izbrana</translation> +<translation id="8964020114565522021">Datoteko povleci sem</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_sr.xtb b/webkit/glue/resources/webkit_strings_sr.xtb new file mode 100644 index 0000000..bdbca13e --- /dev/null +++ b/webkit/glue/resources/webkit_strings_sr.xtb @@ -0,0 +1,41 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="sr"> +<translation id="7658239707568436148">Откажи</translation> +<translation id="3789841737615482174">Инсталирај</translation> +<translation id="8141602879876242471">Ово је индекс који може да се претражује. Унесите кључне речи за претрагу:</translation> +<translation id="2653659639078652383">Пошаљи</translation> +<translation id="5939518447894949180">Ресетуј</translation> +<translation id="7364796246159120393">Одаберите датотеку</translation> +<translation id="2548326553472216322">Нема недавних претрага</translation> +<translation id="6663448176199120256">Недавне претраге</translation> +<translation id="1235745349614807883">Обриши недавне претраге</translation> +<translation id="6807599807928161586">веб област</translation> +<translation id="3040011195152428237">веза</translation> +<translation id="5048533449481078685">означивач листе</translation> +<translation id="8244226242650769279">мапа слике</translation> +<translation id="8597182159515967513">наслов</translation> +<translation id="5944544982112848342">2048 (високи степен)</translation> +<translation id="2846343701378493991">1024 (средњи степен)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Додатна компонента <ph name="PLUGIN"/> није инсталирана</translation> +<translation id="4838490908464673667">Потребна додатна компонента није инсталирана</translation> +<translation id="3600343118165084788">Кликните овде да бисте преузели додатну компоненту</translation> +<translation id="8281246460978372009">Након инсталирања додатне компоненте, кликните овде да бисте освежили</translation> +<translation id="679352192834563463">Нема доступне додатне компоненте за приказивање овог садржаја</translation> +<translation id="8662565117025751661">Преузимање додатне компоненте...</translation> +<translation id="3771786644471114952">Набави додатну компоненту</translation> +<translation id="1275511093094545429">Потребна је додатна компонента <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Потребна је додатна компонента</translation> +<translation id="4420062214988137980">Инсталирање додатне компоненте није успело</translation> +<translation id="4317653869502688143">Потврдите да желите да инсталирате додатну компоненту <ph name="PLUGIN"/>. Требало би да инсталирате само додатне компоненте које сматрате поузданима.</translation> +<translation id="3926627843712816530">Потврдите да желите да инсталирате ову додатну компоненту. Требало би да инсталирате само додатне компоненте које сматрате поузданима.</translation> +<translation id="6845533974506654842">притисни</translation> +<translation id="1842960171412779397">изабери</translation> +<translation id="6119846243427417423">активирај</translation> +<translation id="5476505524087279545">опозови избор</translation> +<translation id="838869780401515933">изабери</translation> +<translation id="4202807286478387388">прескочи</translation> +<translation id="795667975304826397">Није одабрана ниједна датотека</translation> +<translation id="8964020114565522021">Превуците датотеку овде</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_sv.xtb b/webkit/glue/resources/webkit_strings_sv.xtb new file mode 100644 index 0000000..6dc3a1b9 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_sv.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="sv"> +<translation id="7658239707568436148">Avbryt</translation> +<translation id="3789841737615482174">Installera</translation> +<translation id="8141602879876242471">Det här är ett sökbart index. Skriv sökord:</translation> +<translation id="2653659639078652383">Skicka</translation> +<translation id="5939518447894949180">Återställ</translation> +<translation id="7364796246159120393">Välj fil</translation> +<translation id="2548326553472216322">Inga nya sökningar</translation> +<translation id="6663448176199120256">Senaste sökningar</translation> +<translation id="1235745349614807883">Rensa senaste sökningar</translation> +<translation id="6807599807928161586">webbområde</translation> +<translation id="3040011195152428237">länk</translation> +<translation id="5048533449481078685">listmarkör</translation> +<translation id="8244226242650769279">bildkarta</translation> +<translation id="8597182159515967513">rubrik</translation> +<translation id="5944544982112848342">2048 (hög)</translation> +<translation id="2846343701378493991">1024 (medel)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/>-plugin-programmet har inte installerats</translation> +<translation id="4838490908464673667">De begärda plugin-programmen har inte installerats</translation> +<translation id="3600343118165084788">Klicka här för att ladda ned plugin-program</translation> +<translation id="8281246460978372009">Klicka här för att uppdatera när du har installerat plugin-programmet</translation> +<translation id="679352192834563463">Det finns inget plugin-program för att visa det här innehållet</translation> +<translation id="8662565117025751661">Laddar ned plugin-program...</translation> +<translation id="3771786644471114952">Hämta plugin-program</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/>-pluginprogram krävs</translation> +<translation id="3825324228893189080">Ytterligare plugin-program krävs</translation> +<translation id="4420062214988137980">Installationen av plugin-programmet misslyckades</translation> +<translation id="4317653869502688143">Bekräfta att du vill installera <ph name="PLUGIN"/>-plugin-programmet. Installera bara tillförlitliga plugin-program.</translation> +<translation id="3926627843712816530">Bekräfta att du vill installera det här plugin-programmet. Installera bara tillförlitliga plugin-program.</translation> +<translation id="1383141426028388991">Kunde inte installera plugin-program från <ph name="URL"/></translation> +<translation id="6845533974506654842">tryck</translation> +<translation id="1842960171412779397">välj</translation> +<translation id="6119846243427417423">aktivera</translation> +<translation id="5476505524087279545">kryssa av</translation> +<translation id="838869780401515933">kryssa för</translation> +<translation id="4202807286478387388">fortsätta</translation> +<translation id="795667975304826397">Ingen fil har valts</translation> +<translation id="8964020114565522021">Dra filen hit</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_th.xtb b/webkit/glue/resources/webkit_strings_th.xtb new file mode 100644 index 0000000..8f1059f --- /dev/null +++ b/webkit/glue/resources/webkit_strings_th.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="th"> +<translation id="7658239707568436148">ยกเลิก</translation> +<translation id="3789841737615482174">ติดตั้ง</translation> +<translation id="8141602879876242471">นี่คือดัชนีที่สามารถค้นหาได้ ป้อนคำหลักในการค้นหา:</translation> +<translation id="2653659639078652383">ส่ง</translation> +<translation id="5939518447894949180">ตั้งค่าใหม่</translation> +<translation id="7364796246159120393">ปิดไฟล์</translation> +<translation id="2548326553472216322">ไม่พบการค้นหาล่าสุด</translation> +<translation id="6663448176199120256">การค้นหาล่าสุด</translation> +<translation id="1235745349614807883">ลบการค้นหาล่าสุด</translation> +<translation id="6807599807928161586">พื้นที่เว็บ</translation> +<translation id="3040011195152428237">ลิงก์</translation> +<translation id="5048533449481078685">ผู้สร้างรายการ</translation> +<translation id="8244226242650769279">แผนที่รูปภาพ</translation> +<translation id="8597182159515967513">ส่วนหัว</translation> +<translation id="5944544982112848342">2048 (เกรดสูง)</translation> +<translation id="2846343701378493991">1024 (เกรดปานกลาง)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> ไม่ได้ติดตั้งปลั๊กอิน</translation> +<translation id="4838490908464673667">ไม่ได้ติดตั้งปลั๊กอินที่จำเป็น</translation> +<translation id="3600343118165084788">คลิกที่นี่เพื่อดาวน์โหลดปลั๊กอิน</translation> +<translation id="8281246460978372009">หลังจากติดตั้งปลั๊กอิน คลิกที่นี่เพื่อรีเฟรซ</translation> +<translation id="679352192834563463">ไม่มีปลั๊กอินที่จะแสดงเนื้อหานี้</translation> +<translation id="8662565117025751661">กำลังดาวน์โหลดปลั๊กอิน...</translation> +<translation id="3771786644471114952">ติดตั้งปลั๊กอิน</translation> +<translation id="1275511093094545429">ต้องใช้ปลั๊กอิน <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">ต้องการปลั๊กอินเพิ่มเติม</translation> +<translation id="4420062214988137980">การติดตั้งปลั๊กอินล้มเหลว</translation> +<translation id="4317653869502688143">โปรดยืนยันว่าคุณต้องการที่จะติดตั้ง <ph name="PLUGIN"/> ปลั๊กอิน คุณควรติดตั้งปลั๊กอินที่คุณเชื่อถือเท่านั้น</translation> +<translation id="3926627843712816530">โปรดยืนยันว่าคุณต้องการติดตั้งปลั๊กอินนี้ คุณควรติดตั้งปลั๊กอินที่คุณเชื่อถือเท่านั้น</translation> +<translation id="1383141426028388991">การติดตั้งปลั๊กอินจาก <ph name="URL"/> ล้มเหลว</translation> +<translation id="6845533974506654842">กด</translation> +<translation id="1842960171412779397">เลือก</translation> +<translation id="6119846243427417423">เปิดใช้งาน</translation> +<translation id="5476505524087279545">ยกเลิกการทำเครื่องหมาย</translation> +<translation id="838869780401515933">ทำเครื่องหมาย</translation> +<translation id="4202807286478387388">ข้าม</translation> +<translation id="795667975304826397">ไม่ได้เลือกไฟล์ใด</translation> +<translation id="8964020114565522021">ลากไฟล์มาที่นี่</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_tr.xtb b/webkit/glue/resources/webkit_strings_tr.xtb new file mode 100644 index 0000000..f9c8c66 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_tr.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="tr"> +<translation id="7658239707568436148">İptal</translation> +<translation id="3789841737615482174">Yükle</translation> +<translation id="8141602879876242471">Bu dizinde arama yapılabilir. Arama anahtar kelimeleri girin:</translation> +<translation id="2653659639078652383">Gönder</translation> +<translation id="5939518447894949180">Sıfırla</translation> +<translation id="7364796246159120393">Dosya Seç</translation> +<translation id="2548326553472216322">Yeni arama yok</translation> +<translation id="6663448176199120256">Son Aramalar</translation> +<translation id="1235745349614807883">Son Aramaları Temizle</translation> +<translation id="6807599807928161586">web alanı</translation> +<translation id="3040011195152428237">bağlantı</translation> +<translation id="5048533449481078685">liste işaretçisi</translation> +<translation id="8244226242650769279">resim haritası</translation> +<translation id="8597182159515967513">başlık</translation> +<translation id="5944544982112848342">2048 (Yüksek Düzey)</translation> +<translation id="2846343701378493991">1024 (Orta Düzey)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> eklentisi yüklü değil</translation> +<translation id="4838490908464673667">Gereken eklenti yüklü değil</translation> +<translation id="3600343118165084788">Eklentiyi indirmek için burayı tıklayın</translation> +<translation id="8281246460978372009">Eklentiyi yükledikten sonra yenilemek için burayı tıklayın</translation> +<translation id="679352192834563463">Bu içeriği görüntüleyecek eklenti yok</translation> +<translation id="8662565117025751661">Eklenti indiriliyor...</translation> +<translation id="3771786644471114952">Eklenti Al</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> eklentisi gerekiyor</translation> +<translation id="3825324228893189080">Ek eklenti gerekiyor</translation> +<translation id="4420062214988137980">Eklenti kurulumu başarısız oldu</translation> +<translation id="4317653869502688143">Lütfen <ph name="PLUGIN"/> eklentisini yüklemek istediğinizi onaylayın. Yalnızca güvendiğiniz eklentileri yüklemelisiniz.</translation> +<translation id="3926627843712816530">Lütfen bu eklentiyi yüklemek istediğinizi onaylayın. Yalnızca güvendiğiniz eklentileri yüklemelisiniz.</translation> +<translation id="1383141426028388991">Eklenti, <ph name="URL"/> kaynağından kaldırılamadı</translation> +<translation id="6845533974506654842">bas</translation> +<translation id="1842960171412779397">seç</translation> +<translation id="6119846243427417423">etkinleştir</translation> +<translation id="5476505524087279545">işareti kaldır</translation> +<translation id="838869780401515933">işaretle</translation> +<translation id="4202807286478387388">git</translation> +<translation id="795667975304826397">Dosya seçilmedi</translation> +<translation id="8964020114565522021">Dosyayı buraya sürükleyin</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_uk.xtb b/webkit/glue/resources/webkit_strings_uk.xtb new file mode 100644 index 0000000..37c5540 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_uk.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="uk"> +<translation id="7658239707568436148">Скасувати</translation> +<translation id="3789841737615482174">Інсталювати</translation> +<translation id="8141602879876242471">Цей доступний для пошуку індекс. Введіть ключові слова пошуку:</translation> +<translation id="2653659639078652383">Надіслати</translation> +<translation id="5939518447894949180">Скинути</translation> +<translation id="7364796246159120393">Вибрати файл</translation> +<translation id="2548326553472216322">Немає останніх пошуків</translation> +<translation id="6663448176199120256">Останні пошуки</translation> +<translation id="1235745349614807883">Очистити останні пошуки</translation> +<translation id="6807599807928161586">область Інтернету</translation> +<translation id="3040011195152428237">посилання</translation> +<translation id="5048533449481078685">маркер списку</translation> +<translation id="8244226242650769279">мапа зображення</translation> +<translation id="8597182159515967513">заголовок</translation> +<translation id="5944544982112848342">2048 (Високий рівень)</translation> +<translation id="2846343701378493991">1024 (Середній рівень)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436">Модуль <ph name="PLUGIN"/> не інстальовано</translation> +<translation id="4838490908464673667">Потрібний модуль не інстальовано</translation> +<translation id="3600343118165084788">Натисніть тут, щоб завантажити модуль</translation> +<translation id="8281246460978372009">Після інсталяції модуля натисніть тут, щоб оновити</translation> +<translation id="679352192834563463">Немає доступного модуля для відображення цього вмісту</translation> +<translation id="8662565117025751661">Завантаження модуля...</translation> +<translation id="3771786644471114952">Отримати модуль</translation> +<translation id="1275511093094545429">Потрібен модуль <ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">Потрібен додатковий модуль.</translation> +<translation id="4420062214988137980">Не вдалось інсталювати модуль</translation> +<translation id="4317653869502688143">Підтвердьте інсталяцію модуля <ph name="PLUGIN"/>. Слід інсталювати лише модулі, які вважаєте надійними.</translation> +<translation id="3926627843712816530">Підтвердьте інсталяцію цього модуля. Слід інсталювати лише модулі, які вважаєте надійними.</translation> +<translation id="1383141426028388991">Не вдалось інсталювати модуль із <ph name="URL"/></translation> +<translation id="6845533974506654842">натиснути</translation> +<translation id="1842960171412779397">вибрати</translation> +<translation id="6119846243427417423">активувати</translation> +<translation id="5476505524087279545">зняти прапорець</translation> +<translation id="838869780401515933">установити прапорець</translation> +<translation id="4202807286478387388">перейти</translation> +<translation id="795667975304826397">Файл не вибрано</translation> +<translation id="8964020114565522021">Перетягніть файл сюди</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_vi.xtb b/webkit/glue/resources/webkit_strings_vi.xtb new file mode 100644 index 0000000..2d60a93 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_vi.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="vi"> +<translation id="7658239707568436148">Huỷ</translation> +<translation id="3789841737615482174">Cài đặt</translation> +<translation id="8141602879876242471">Đây là chỉ mục có thể tìm kiếm. Nhập từ khoá tìm kiếm vào:</translation> +<translation id="2653659639078652383">Gửi</translation> +<translation id="5939518447894949180">Đặt lại</translation> +<translation id="7364796246159120393">Chọn Tệp tin</translation> +<translation id="2548326553472216322">Không có tìm kiếm nào gần đây</translation> +<translation id="6663448176199120256">Tìm kiếm Gần đây</translation> +<translation id="1235745349614807883">Xoá Tìm kiếm Gần đây</translation> +<translation id="6807599807928161586">khu vực web</translation> +<translation id="3040011195152428237">liên kết</translation> +<translation id="5048533449481078685">đánh dấu danh sách</translation> +<translation id="8244226242650769279">bản đồ hình ảnh</translation> +<translation id="8597182159515967513">đầu đề</translation> +<translation id="5944544982112848342">2048 (Cấp độ cao)</translation> +<translation id="2846343701378493991">1024 (Loại Trung bình)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> plgin không được cài đặt</translation> +<translation id="4838490908464673667">Plugin yêu cầu không được cài đặt</translation> +<translation id="3600343118165084788">Nhấp vào đây để tải plugin xuống</translation> +<translation id="8281246460978372009">Sau khi cài đặt plugin, nhấp vào đây để làm mới</translation> +<translation id="679352192834563463">Không plugin nào có sẵn để hiển thị nội dung này</translation> +<translation id="8662565117025751661">Đang tải xuống plugin...</translation> +<translation id="3771786644471114952">Lấy Plugin</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/> plugin cần</translation> +<translation id="3825324228893189080">Cần plugin bổ sung</translation> +<translation id="4420062214988137980">Không cài đặt plugin được</translation> +<translation id="4317653869502688143">Vui lòng xác nhận rằng bạn muốn cài đặt plugin <ph name="PLUGIN"/>. Bạn chỉ nên cài đặt plugin mà mình tin cậy.</translation> +<translation id="3926627843712816530">Hãy xác nhận rằng bạn muốn cài đặt plugin này. Bạn chỉ nên cài đặt plugin mà mình tin cậy.</translation> +<translation id="1383141426028388991">Lỗi cài đặt plugin từ <ph name="URL"/></translation> +<translation id="6845533974506654842">nhấn</translation> +<translation id="1842960171412779397">chọn</translation> +<translation id="6119846243427417423">kích hoạt</translation> +<translation id="5476505524087279545">bỏ chọn</translation> +<translation id="838869780401515933">chọn</translation> +<translation id="4202807286478387388">chuyển</translation> +<translation id="795667975304826397">Không có tệp tin nào được chọn</translation> +<translation id="8964020114565522021">Kéo tệp tin tại đây</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_zh-CN.xtb b/webkit/glue/resources/webkit_strings_zh-CN.xtb new file mode 100644 index 0000000..e93fb4d --- /dev/null +++ b/webkit/glue/resources/webkit_strings_zh-CN.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="zh-CN"> +<translation id="7658239707568436148">取消</translation> +<translation id="3789841737615482174">安装</translation> +<translation id="8141602879876242471">这是一个可搜索的索引。 输入搜索关键字:</translation> +<translation id="2653659639078652383">提交</translation> +<translation id="5939518447894949180">重置</translation> +<translation id="7364796246159120393">选择文件</translation> +<translation id="2548326553472216322">近期无搜索</translation> +<translation id="6663448176199120256">近期搜索</translation> +<translation id="1235745349614807883">清除最近的搜索</translation> +<translation id="6807599807928161586">网络区域</translation> +<translation id="3040011195152428237">链接</translation> +<translation id="5048533449481078685">列表标记</translation> +<translation id="8244226242650769279">影像地图</translation> +<translation id="8597182159515967513">标题</translation> +<translation id="5944544982112848342">2048(高强度)</translation> +<translation id="2846343701378493991">1024(中等强度)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/></translation> +<translation id="4838490908464673667">未安装所需插件</translation> +<translation id="3600343118165084788">点击此处下载插件。</translation> +<translation id="8281246460978372009">安装插件后,点击此处刷新</translation> +<translation id="679352192834563463">未找到显示此内容的插件</translation> +<translation id="8662565117025751661">正在下载插件...</translation> +<translation id="3771786644471114952">获取插件</translation> +<translation id="1275511093094545429"><ph name="PLUGIN"/></translation> +<translation id="3825324228893189080">需要其他插件</translation> +<translation id="4420062214988137980">插件安装失败</translation> +<translation id="4317653869502688143"><ph name="PLUGIN"/> 只应安装自己信任的插件。</translation> +<translation id="3926627843712816530">请确认要安装此插件。 只应安装自己信任的插件。</translation> +<translation id="1383141426028388991"><ph name="URL"/></translation> +<translation id="6845533974506654842">按下</translation> +<translation id="1842960171412779397">选择</translation> +<translation id="6119846243427417423">启动</translation> +<translation id="5476505524087279545">取消选中</translation> +<translation id="838869780401515933">选中</translation> +<translation id="4202807286478387388">跳转</translation> +<translation id="795667975304826397">没有选择文件</translation> +<translation id="8964020114565522021">将文件拖动到此处</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/webkit_strings_zh-TW.xtb b/webkit/glue/resources/webkit_strings_zh-TW.xtb new file mode 100644 index 0000000..a2955d4 --- /dev/null +++ b/webkit/glue/resources/webkit_strings_zh-TW.xtb @@ -0,0 +1,42 @@ +<?xml version="1.0" ?> +<!DOCTYPE translationbundle> +<translationbundle lang="zh-TW"> +<translation id="7658239707568436148">取消</translation> +<translation id="3789841737615482174">安裝</translation> +<translation id="8141602879876242471">這是可搜尋的索引,輸入搜尋關鍵字:</translation> +<translation id="2653659639078652383">提交</translation> +<translation id="5939518447894949180">重設</translation> +<translation id="7364796246159120393">選擇檔案</translation> +<translation id="2548326553472216322">沒有近期的搜尋</translation> +<translation id="6663448176199120256">最近的搜尋</translation> +<translation id="1235745349614807883">清除最近的搜尋記錄</translation> +<translation id="6807599807928161586">網頁範圍</translation> +<translation id="3040011195152428237">連結</translation> +<translation id="5048533449481078685">清單標記</translation> +<translation id="8244226242650769279">影像地圖</translation> +<translation id="8597182159515967513">標題</translation> +<translation id="5944544982112848342">2048 (高級)</translation> +<translation id="2846343701378493991">1024 (中等)</translation> +<translation id="4611115858363067980"><ph name="FILENAME"/><ph name="WIDTH"/>×<ph name="HEIGHT"/></translation> +<translation id="2965480764085142436"><ph name="PLUGIN"/> 外掛程式尚未安裝</translation> +<translation id="4838490908464673667">未安裝要求的外掛程式</translation> +<translation id="3600343118165084788">按一下這裡以下載掛程式</translation> +<translation id="8281246460978372009">安裝外掛程式後,請按一下這裡更新</translation> +<translation id="679352192834563463">沒有外掛程式可供顯示目前內容</translation> +<translation id="8662565117025751661">正在下載外掛程式...</translation> +<translation id="3771786644471114952">取得外掛程式</translation> +<translation id="1275511093094545429">需要 <ph name="PLUGIN"/> 外掛程式</translation> +<translation id="3825324228893189080">需要額外的外掛程式</translation> +<translation id="4420062214988137980">外掛程式安裝失敗</translation> +<translation id="4317653869502688143">請確定您想要安裝 <ph name="PLUGIN"/> 外掛程式。建議您僅安裝可靠的外掛程式。</translation> +<translation id="3926627843712816530">請確定您想要安裝此外掛程式。建議您僅安裝可靠的外掛程式。</translation> +<translation id="1383141426028388991">無法從 <ph name="URL"/> 安裝外掛程式</translation> +<translation id="6845533974506654842">按下</translation> +<translation id="1842960171412779397">選取</translation> +<translation id="6119846243427417423">啟動</translation> +<translation id="5476505524087279545">取消選取</translation> +<translation id="838869780401515933">選取</translation> +<translation id="4202807286478387388">跳至另一頁</translation> +<translation id="795667975304826397">未選擇檔案</translation> +<translation id="8964020114565522021">拖曳檔案至此</translation> +</translationbundle>
\ No newline at end of file diff --git a/webkit/glue/resources/zoom_in.cur b/webkit/glue/resources/zoom_in.cur Binary files differnew file mode 100644 index 0000000..b594d79 --- /dev/null +++ b/webkit/glue/resources/zoom_in.cur diff --git a/webkit/glue/resources/zoom_out.cur b/webkit/glue/resources/zoom_out.cur Binary files differnew file mode 100644 index 0000000..7e495fb --- /dev/null +++ b/webkit/glue/resources/zoom_out.cur diff --git a/webkit/glue/searchable_form_data.cc b/webkit/glue/searchable_form_data.cc new file mode 100644 index 0000000..3d024e6 --- /dev/null +++ b/webkit/glue/searchable_form_data.cc @@ -0,0 +1,420 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "csshelper.h" +#include "CString.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "FormData.h" +#include "FormDataList.h" +#include "FrameLoader.h" +#include "HTMLFormElement.h" +#include "HTMLOptionElement.h" +#include "HTMLGenericFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLOptionsCollection.h" +#include "HTMLSelectElement.h" +#include "ResourceRequest.h" +#include "String.h" +#include "TextEncoding.h" +#pragma warning(pop) + +#undef LOG + +#include "base/basictypes.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/searchable_form_data.h" +#include "webkit/glue/webframe_impl.h" + +using WebCore::HTMLInputElement; +using WebCore::HTMLOptionElement; + +namespace { + +// TODO (sky): This comes straight out of HTMLFormElement, will work with +// WebKit folks to make public. +WebCore::DeprecatedCString encodeCString(const WebCore::CString& cstr) { + WebCore::DeprecatedCString e = cstr.deprecatedCString(); + + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 + // same safe characters as Netscape for compatibility + static const char *safe = "-._*"; + int elen = e.length(); + WebCore::DeprecatedCString encoded((elen + e.contains('\n')) * 3 + 1); + int enclen = 0; + + for (int pos = 0; pos < elen; pos++) { + unsigned char c = e[pos]; + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safe, c)) + encoded[enclen++] = c; + else if (c == ' ') + encoded[enclen++] = '+'; + else if (c == '\n' || (c == '\r' && e[pos + 1] != '\n')) { + encoded[enclen++] = '%'; + encoded[enclen++] = '0'; + encoded[enclen++] = 'D'; + encoded[enclen++] = '%'; + encoded[enclen++] = '0'; + encoded[enclen++] = 'A'; + } else if (c != '\r') { + encoded[enclen++] = '%'; + unsigned int h = c / 16; + h += (h > 9) ? ('A' - 10) : '0'; + encoded[enclen++] = h; + + unsigned int l = c % 16; + l += (l > 9) ? ('A' - 10) : '0'; + encoded[enclen++] = l; + } + } + encoded[enclen++] = '\0'; + encoded.truncate(enclen); + + return encoded; +} + +// Returns true if the form element has an 'onsubmit' attribute. +bool FormHasOnSubmit(WebCore::HTMLFormElement* form) { + const WebCore::AtomicString& attribute_value = + form->getAttribute(WebCore::HTMLNames::onsubmitAttr); + return (!attribute_value.isNull() && !attribute_value.isEmpty()); +} + +// Returns true if the form element will submit the data using a GET. +bool IsFormMethodGet(WebCore::HTMLFormElement* form) { + const WebCore::AtomicString& attribute_value = + form->getAttribute(WebCore::HTMLNames::methodAttr); + return !equalIgnoringCase(attribute_value, "post"); +} + +// Gets the encoding for the form. +void GetFormEncoding(WebCore::HTMLFormElement* form, + WebCore::TextEncoding* encoding) { + WebCore::String str = + form->getAttribute(WebCore::HTMLNames::accept_charsetAttr); + str.replace(',', ' '); + Vector<WebCore::String> charsets = str.split(' '); + Vector<WebCore::String>::const_iterator end = charsets.end(); + for (Vector<WebCore::String>::const_iterator it = charsets.begin(); it != end; + ++it) { + *encoding = WebCore::TextEncoding(*it); + if (encoding->isValid()) + return; + } + if (!encoding->isValid()) { + WebCore::Frame* frame = form->document()->frame(); + if (frame) + *encoding = WebCore::TextEncoding(frame->loader()->encoding()); + else + *encoding = WebCore::Latin1Encoding(); + } +} + +// Returns true if the submit request results in an HTTP URL. +bool IsHTTPFormSubmit(WebCore::HTMLFormElement* form) { + WebCore::Frame* frame = form->document()->frame(); + WebCore::String action = WebCore::parseURL(form->action()); + WebCore::FrameLoader* loader = frame->loader(); + WebCore::KURL url = loader->completeURL(action.isNull() ? "" : action); + return (url.protocol() == "http"); +} + +// If the form does not have an activated submit button, the first submit +// button is returned. +WebCore::HTMLGenericFormElement* GetButtonToActivate( + WebCore::HTMLFormElement* form) { + WTF::Vector<WebCore::HTMLGenericFormElement*> form_elements = + form->formElements; + WebCore::HTMLGenericFormElement* first_submit_button = NULL; + + for (unsigned i = 0; i < form_elements.size(); ++i) { + WebCore::HTMLGenericFormElement* current = form_elements[i]; + if (current->isActivatedSubmit()) { + // There's a button that is already activated for submit, return NULL. + return NULL; + } else if (first_submit_button == NULL && + current->isSuccessfulSubmitButton()) { + first_submit_button = current; + } + } + return first_submit_button; +} + +// Returns true if the selected state of all the options matches the default +// selected state. +bool IsSelectInDefaultState(WebCore::HTMLSelectElement* select) { + RefPtr<WebCore::HTMLOptionsCollection> options = select->options(); + WebCore::Node* node = options->firstItem(); + + if (!select->multiple() && select->size() <= 1) { + // The select is rendered as a combobox (called menulist in WebKit). At + // least one item is selected, determine which one. + HTMLOptionElement* initial_selected = NULL; + while (node) { + HTMLOptionElement* option_element = + webkit_glue::CastHTMLElement<HTMLOptionElement>( + node, WebCore::HTMLNames::optionTag); + if (option_element) { + if (!initial_selected) + initial_selected = option_element; + if (option_element->defaultSelected()) { + // The page specified the option to select. + initial_selected = option_element; + break; + } + } + node = options->nextItem(); + } + if (initial_selected) + return initial_selected->selected(); + } else { + while (node) { + HTMLOptionElement* option_element = + webkit_glue::CastHTMLElement<HTMLOptionElement>( + node, WebCore::HTMLNames::optionTag); + if (option_element && + option_element->selected() != option_element->defaultSelected()) { + return false; + } + node = options->nextItem(); + } + } + return true; +} + +bool IsCheckBoxOrRadioInDefaultState(HTMLInputElement* element) { + return (element->checked() == element->defaultChecked()); +} + +// Returns true if the form element is in its default state, false otherwise. +// The default state is the state of the form element on initial load of the +// page, and varies depending upon the form element. For example, a checkbox is +// in its default state if the checked state matches the defaultChecked state. +bool IsInDefaultState(WebCore::HTMLGenericFormElement* form_element) { + if (form_element->hasTagName(WebCore::HTMLNames::inputTag)) { + HTMLInputElement* input_element = + static_cast<HTMLInputElement*>(form_element); + if (input_element->inputType() == HTMLInputElement::CHECKBOX || + input_element->inputType() == HTMLInputElement::RADIO) { + return IsCheckBoxOrRadioInDefaultState(input_element); + } + } else if (form_element->hasTagName(WebCore::HTMLNames::selectTag)) { + return IsSelectInDefaultState( + static_cast<WebCore::HTMLSelectElement*>(form_element)); + } + return true; +} + +// If form has only one text input element, it is returned. If a valid input +// element is not found, NULL is returned. Additionally, the form data for all +// elements is added to enc_string and the encoding used is set in +// encoding_name. +WebCore::HTMLInputElement* GetTextElement( + WebCore::HTMLFormElement* form, + WebCore::DeprecatedCString* enc_string, + std::string* encoding_name) { + WebCore::TextEncoding encoding; + GetFormEncoding(form, &encoding); + if (!encoding.isValid()) { + // Need a valid encoding to encode the form elements. + // If the encoding isn't found webkit ends up replacing the params with + // empty strings. So, we don't try to do anything here. + return NULL; + } + *encoding_name = encoding.name(); + WebCore::HTMLInputElement* text_element = NULL; + WTF::Vector<WebCore::HTMLGenericFormElement*> form_elements = + form->formElements; + for (unsigned i = 0; i < form_elements.size(); ++i) { + WebCore::HTMLGenericFormElement* form_element = form_elements[i]; + if (!form_element->disabled() && !form_element->name().isNull()) { + bool is_text_element = false; + if (!IsInDefaultState(form_element)) { + return NULL; + } + if (form_element->hasTagName(WebCore::HTMLNames::inputTag)) { + WebCore::HTMLInputElement* input_element = + static_cast<WebCore::HTMLInputElement*>(form_element); + switch (input_element->inputType()) { + case WebCore::HTMLInputElement::TEXT: + case WebCore::HTMLInputElement::ISINDEX: + is_text_element = true; + break; + case WebCore::HTMLInputElement::PASSWORD: + // Don't store passwords! This is most likely an https anyway. + // Fall through. + case WebCore::HTMLInputElement::FILE: + // Too big, don't try to index this. + return NULL; + break; + default: + // All other input types are indexable. + break; + } + } else if (form_element->hasTagName(WebCore::HTMLNames::textareaTag)) { + // TextArea aren't use for search. + return NULL; + } + WebCore::FormDataList lst(encoding); + if (form_element->appendFormData(lst, false)) { + if (is_text_element && lst.list().size() > 0) { + if (text_element != NULL) { + // The auto-complete bar only knows how to fill in one value. + // This form has multiple fields; don't treat it as searchable. + return NULL; + } + text_element = static_cast<WebCore::HTMLInputElement*>(form_element); + } + for (int j = 0, max = static_cast<int>(lst.list().size()); j < max; ++j) { + const WebCore::FormDataListItem& item = lst.list()[j]; + // handle ISINDEX / <input name=isindex> special + // but only if its the first entry + if (enc_string->isEmpty() && item.m_data == "isindex") { + if (form_element == text_element) + *enc_string += "{searchTerms}"; + else + *enc_string += encodeCString(lst.list()[j + 1].m_data); + ++j; + } else { + if (!enc_string->isEmpty()) + *enc_string += '&'; + *enc_string += encodeCString(item.m_data); + *enc_string += "="; + if (form_element == text_element) + *enc_string += "{searchTerms}"; + else + *enc_string += encodeCString(lst.list()[j + 1].m_data); + ++j; + } + } + } + } + } + return text_element; +} + +} // namespace + +SearchableFormData* SearchableFormData::Create(WebCore::Element* element) { + if (!element->isHTMLElement() || + !static_cast<WebCore::HTMLElement*>(element)->isGenericFormElement()) { + return NULL; + } + + WebCore::Frame* frame = element->document()->frame(); + if (frame == NULL) + return NULL; + + WebCore::HTMLGenericFormElement* input_element = + static_cast<WebCore::HTMLGenericFormElement*>(element); + + WebCore::HTMLFormElement* form = input_element->form(); + if (form == NULL) + return NULL; + + return Create(form); +} + +SearchableFormData* SearchableFormData::Create(WebCore::HTMLFormElement* form) { + WebCore::Frame* frame = form->document()->frame(); + if (frame == NULL) + return NULL; + + // Only consider forms that GET data, do not have script for onsubmit, and + // the action targets an http page. + if (!IsFormMethodGet(form) || FormHasOnSubmit(form) || + !IsHTTPFormSubmit(form)) + return NULL; + + WebCore::DeprecatedCString enc_string = ""; + WebCore::HTMLGenericFormElement* first_submit_button = + GetButtonToActivate(form); + + if (first_submit_button) { + // The form does not have an active submit button, make the first button + // active. We need to do this, otherwise the URL will not contain the + // name of the submit button. + first_submit_button->setActivatedSubmit(true); + } + + std::string encoding; + WebCore::HTMLInputElement* text_element = + GetTextElement(form, &enc_string, &encoding); + + if (first_submit_button) + first_submit_button->setActivatedSubmit(false); + + if (text_element == NULL) { + // Not a searchable form. + return NULL; + } + + // It's a valid form. + // Generate the URL and create a new SearchableFormData. + RefPtr<WebCore::FormData> form_data = new WebCore::FormData; + form_data->appendData(enc_string.data(), enc_string.length()); + WebCore::String action = WebCore::parseURL(form->action()); + WebCore::FrameLoader* loader = frame->loader(); + WebCore::KURL url = loader->completeURL(action.isNull() ? "" : action); + url.setQuery(form_data->flattenToString().deprecatedString()); + std::wstring url_wstring = webkit_glue::StringToStdWString(url.string()); + std::wstring current_value = webkit_glue::StringToStdWString( + static_cast<WebCore::HTMLInputElement*>(text_element)->value()); + std::wstring text_name = + webkit_glue::StringToStdWString(text_element->name()); + return + new SearchableFormData(url_wstring, text_name, current_value, encoding); +} + +// static +bool SearchableFormData::Equals(const SearchableFormData* a, + const SearchableFormData* b) { + return ((a == b) || + (a != NULL && b != NULL && + a->url().spec() == b->url().spec() && + a->element_name() == b->element_name() && + a->element_value() == b->element_value() && + a->encoding() == b->encoding())); +} + +SearchableFormData::SearchableFormData(const std::wstring& url, + const std::wstring& element_name, + const std::wstring& element_value, + const std::string& encoding) + : url_(url), + element_name_(element_name), + element_value_(element_value), + encoding_(encoding) { +} diff --git a/webkit/glue/searchable_form_data.h b/webkit/glue/searchable_form_data.h new file mode 100644 index 0000000..61891e8 --- /dev/null +++ b/webkit/glue/searchable_form_data.h @@ -0,0 +1,84 @@ +// Copyright 2008, 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 WEBKIT_GLUE_SEARCHABLE_FORM_DATA_H__ +#define WEBKIT_GLUE_SEARCHABLE_FORM_DATA_H__ + +#include <string> + +#include "googleurl/src/gurl.h" + +namespace WebCore { +class Element; +class HTMLFormElement; +} + +// SearchableFormData encapsulates a URL and class name of an INPUT field +// that correspond to a searchable form request. +class SearchableFormData { + public: + // If form contains elements that constitutes a valid searchable form + // request, a SearchableFormData is created and returned. + static SearchableFormData* Create(WebCore::HTMLFormElement* form); + + // If the element is contained in a form that constitutes a valid searchable + // form, a SearchableFormData is created and returned. + static SearchableFormData* Create(WebCore::Element* element); + + // Returns true if the two SearchableFormData are equal, false otherwise. + // Either argument may be NULL. If both elements are NULL, true is returned. + static bool Equals(const SearchableFormData* a, const SearchableFormData* b); + + // URL for the searchable form request. + const GURL& url() const { return url_; } + + // Class name of the INPUT element the user input text into. + const std::wstring element_name() const { return element_name_; } + + // Value of the text field in the form. + const std::wstring element_value() const { return element_value_; } + + // Encoding used to encode the form parameters; never empty. + const std::string& encoding() const { return encoding_; } + + private: + SearchableFormData(const std::wstring& url, + const std::wstring& element_name, + const std::wstring& element_value, + const std::string& encoding); + + const GURL url_; + const std::wstring element_name_; + const std::wstring element_value_; + const std::string encoding_; + + DISALLOW_EVIL_CONSTRUCTORS(SearchableFormData); +}; + +#endif // WEBKIT_GLUE_SEARCHABLE_FORM_H__ diff --git a/webkit/glue/simple_clipboard_impl.cc b/webkit/glue/simple_clipboard_impl.cc new file mode 100644 index 0000000..9f26b52 --- /dev/null +++ b/webkit/glue/simple_clipboard_impl.cc @@ -0,0 +1,93 @@ +// Copyright 2008, 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 "webkit/glue/webkit_glue.h" + +#include <string> + +#include "base/clipboard.h" +#include "googleurl/src/gurl.h" + +#include "SkBitmap.h" + +// Clipboard glue +// Basically just proxy the calls off to the clipboard + +namespace webkit_glue { + +Clipboard clipboard; + +void webkit_glue::ClipboardClear() { + clipboard.Clear(); +} + +void webkit_glue::ClipboardWriteText(const std::wstring& text) { + clipboard.WriteText(text); +} + +void webkit_glue::ClipboardWriteHTML(const std::wstring& html, + const GURL& url) { + clipboard.WriteHTML(html, url.spec()); +} + +void webkit_glue::ClipboardWriteBookmark(const std::wstring& title, + const GURL& url) { + clipboard.WriteBookmark(title, url.spec()); +} + +void webkit_glue::ClipboardWriteBitmap(const SkBitmap& bitmap) { + SkAutoLockPixels bitmap_lock(bitmap); + clipboard.WriteBitmap(bitmap.getPixels(), + gfx::Size(bitmap.width(), bitmap.height())); +} + +void webkit_glue::ClipboardWriteWebSmartPaste() { + clipboard.WriteWebSmartPaste(); +} + +bool webkit_glue::ClipboardIsFormatAvailable(unsigned int format) { + return clipboard.IsFormatAvailable(format); +} + +void webkit_glue::ClipboardReadText(std::wstring* result) { + clipboard.ReadText(result); +} + +void webkit_glue::ClipboardReadAsciiText(std::string* result) { + clipboard.ReadAsciiText(result); +} + +void webkit_glue::ClipboardReadHTML(std::wstring* markup, GURL* url) { + std::string url_str; + clipboard.ReadHTML(markup, url ? &url_str : NULL); + if (url) + *url = GURL(url_str); +} + +} // namespace webkit_glue diff --git a/webkit/glue/unittest_test_server.h b/webkit/glue/unittest_test_server.h new file mode 100644 index 0000000..4a8156d --- /dev/null +++ b/webkit/glue/unittest_test_server.h @@ -0,0 +1,72 @@ +// Copyright 2008, 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 WEBKIT_GLUE_UNITTEST_TEST_SERVER_H__ +#define WEBKIT_GLUE_UNITTEST_TEST_SERVER_H__ + +#include "webkit/glue/resource_loader_bridge.h" +#include "net/base/load_flags.h" +#include "net/url_request/url_request_unittest.h" + +using webkit_glue::ResourceLoaderBridge; + +// We need to use ResourceLoaderBridge to communicate with the testserver +// instead of using URLRequest directly because URLRequests need to be run on +// the test_shell's IO thread. +class UnittestTestServer : public TestServer { + public: + UnittestTestServer() : TestServer(TestServer::ManualInit()) { + Init("localhost", 1337, L"webkit/data", std::wstring()); + } + + ~UnittestTestServer() { + Shutdown(); + } + + virtual bool MakeGETRequest(const std::string& page_name) { + GURL url(TestServerPage(page_name)); + scoped_ptr<ResourceLoaderBridge> loader( + ResourceLoaderBridge::Create(NULL, "GET", + url, + url, // policy_url + GURL(), // no referrer + std::string(), // no extra headers + net::LOAD_NORMAL, + 0, + ResourceType::SUB_RESOURCE, + false)); + EXPECT_TRUE(loader.get()); + + ResourceLoaderBridge::SyncLoadResponse resp; + loader->SyncLoad(&resp); + return resp.status.is_success(); + } +}; + +#endif // WEBKIT_GLUE_UNITTEST_TEST_SERVER_H__ diff --git a/webkit/glue/webcursor.cc b/webkit/glue/webcursor.cc new file mode 100644 index 0000000..8bd5744 --- /dev/null +++ b/webkit/glue/webcursor.cc @@ -0,0 +1,168 @@ +// Copyright 2008, 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 "base/gfx/bitmap_header.h" +#include "webkit/glue/webcursor.h" +#include "webkit/glue/webkit_resources.h" + +WebCursor::WebCursor() + : type_(ARROW), + hotspot_x_(0), + hotspot_y_(0) { + memset(&bitmap_, 0, sizeof(bitmap_)); +} + +WebCursor::WebCursor(Type cursor_type) + : type_(cursor_type), + hotspot_x_(0), + hotspot_y_(0) { + memset(&bitmap_, 0, sizeof(bitmap_)); +} + +WebCursor::WebCursor(const SkBitmap* bitmap, int hotspot_x, int hotspot_y) + : type_(CUSTOM) { + hotspot_x_ = hotspot_x; + hotspot_y_ = hotspot_y; + bitmap_ = *bitmap; +} + +WebCursor::~WebCursor() { +} + +WebCursor::WebCursor(const WebCursor& other) { + type_ = other.type_; + hotspot_x_ = other.hotspot_x_; + hotspot_y_ = other.hotspot_y_; + bitmap_ = other.bitmap_; +} + +WebCursor& WebCursor::operator=(const WebCursor& other) { + if (this != &other) { + type_ = other.type_; + hotspot_x_ = other.hotspot_x_; + hotspot_y_ = other.hotspot_y_; + bitmap_ = other.bitmap_; + } + return *this; +} + +HCURSOR WebCursor::GetCursor(HINSTANCE module_handle) const { + if (type_ == CUSTOM) + return NULL; + + static LPCWSTR cursor_resources[] = { + IDC_ARROW, + IDC_IBEAM, + IDC_WAIT, + IDC_CROSS, + IDC_UPARROW, + IDC_SIZE, + IDC_ICON, + IDC_SIZENWSE, + IDC_SIZENESW, + IDC_SIZEWE, + IDC_SIZENS, + IDC_SIZEALL, + IDC_NO, + IDC_HAND, + IDC_APPSTARTING, + IDC_HELP, + // webkit resources + MAKEINTRESOURCE(IDC_ALIAS), + MAKEINTRESOURCE(IDC_CELL), + MAKEINTRESOURCE(IDC_COLRESIZE), + MAKEINTRESOURCE(IDC_COPYCUR), + MAKEINTRESOURCE(IDC_ROWRESIZE), + MAKEINTRESOURCE(IDC_VERTICALTEXT), + MAKEINTRESOURCE(IDC_ZOOMIN), + MAKEINTRESOURCE(IDC_ZOOMOUT) + }; + + HINSTANCE instance_to_use = NULL; + if (type_ > HELP) + instance_to_use = module_handle; + + HCURSOR cursor_handle = LoadCursor(instance_to_use, + cursor_resources[type_]); + return cursor_handle; +} + +HCURSOR WebCursor::GetCustomCursor() const { + if (type_ != CUSTOM) + return NULL; + + BITMAPINFO cursor_bitmap_info = {0}; + gfx::CreateBitmapHeader(bitmap_.width(), bitmap_.height(), + reinterpret_cast<BITMAPINFOHEADER*>(&cursor_bitmap_info)); + HDC dc = ::GetDC(0); + HDC workingDC = CreateCompatibleDC(dc); + HBITMAP bitmap_handle = CreateDIBSection(dc, &cursor_bitmap_info, + DIB_RGB_COLORS, 0, 0, 0); + SkAutoLockPixels bitmap_lock(bitmap_); + SetDIBits(0, bitmap_handle, 0, bitmap_.width(), + bitmap_.getPixels(), &cursor_bitmap_info, DIB_RGB_COLORS); + + HBITMAP old_bitmap = reinterpret_cast<HBITMAP>(SelectObject(workingDC, + bitmap_handle)); + SetBkMode(workingDC, TRANSPARENT); + SelectObject(workingDC, old_bitmap); + + HBITMAP mask = CreateBitmap(bitmap_.width(), bitmap_.height(), + 1, 1, NULL); + ICONINFO ii = {0}; + ii.fIcon = FALSE; + ii.xHotspot = hotspot_x_; + ii.yHotspot = hotspot_y_; + ii.hbmMask = mask; + ii.hbmColor = bitmap_handle; + + HCURSOR cursor_handle = CreateIconIndirect(&ii); + + DeleteObject(mask); + DeleteObject(bitmap_handle); + DeleteDC(workingDC); + ::ReleaseDC(0, dc); + return cursor_handle; +} + +bool WebCursor::IsSameBitmap(const SkBitmap& bitmap) const { + SkAutoLockPixels new_bitmap_lock(bitmap); + SkAutoLockPixels bitmap_lock(bitmap_); + return (memcmp(bitmap_.getPixels(), bitmap.getPixels(), + bitmap_.getSize()) == 0); +} + +bool WebCursor::IsEqual(const WebCursor& other) const { + if (type_ != other.type_) + return false; + + if(type_ == CUSTOM) + return IsSameBitmap(other.bitmap_); + return true; +} diff --git a/webkit/glue/webcursor.h b/webkit/glue/webcursor.h new file mode 100644 index 0000000..fd693f8 --- /dev/null +++ b/webkit/glue/webcursor.h @@ -0,0 +1,124 @@ +// Copyright 2008, 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 WEBCURSOR_H__ +#define WEBCURSOR_H__ + +#include "skia/include/SkBitmap.h" + +// This class provides the functionality of a generic cursor type. The intent +// is to stay away from platform specific code here. We do have win32 specific +// functionality to retreive a HCURSOR from a cursor type. +// TODO(iyengar) : Win32 specific functionality needs to be taken out of this +// object +class WebCursor +{ +public: + // Supported cursor types. + enum Type { + ARROW, + IBEAM, + WAIT, + CROSS, + UPARROW, + SIZE, + ICON, + SIZENWSE, + SIZENESW, + SIZEWE, + SIZENS, + SIZEALL, + NO, + HAND, + APPSTARTING, + HELP, + ALIAS, + CELL, + COLRESIZE, + COPYCUR, + ROWRESIZE, + VERTICALTEXT, + ZOOMIN, + ZOOMOUT, + CUSTOM + }; + + WebCursor(); + WebCursor(Type cursor_type); + WebCursor(const SkBitmap* bitmap, int hotspot_x, int hotspot_y); + ~WebCursor(); + + WebCursor(const WebCursor& other); + WebCursor& operator = (const WebCursor&); + + Type type() const { return type_; }; + int hotspot_x() const { return hotspot_x_; } + int hotspot_y() const { return hotspot_y_; } + const SkBitmap& bitmap() const { return bitmap_; } + + void set_type(Type cursor_type) { + type_ = cursor_type; + } + + void set_bitmap(const SkBitmap& bitmap) { + bitmap_ = bitmap; + } + + void set_hotspot(int hotspot_x, int hotspot_y) { + hotspot_x_ = hotspot_x; + hotspot_y_ = hotspot_x; + } + + // Returns the cursor handle. If the cursor type is a win32 or safari + // cursor, we use LoadCursor to load the cursor. + // Returns NULL on error. + HCURSOR GetCursor(HINSTANCE module_handle) const; + // If the underlying cursor type is a custom cursor, this function converts + // the SkBitmap to a cursor and returns the same. The responsiblity of + // freeing the cursor handle lies with the caller. + // Returns NULL on error. + HCURSOR GetCustomCursor() const; + // Returns true if the passed in SkBitmap is the same as the the + // current SkBitmap. We use memcmp to compare the two bitmaps. + bool IsSameBitmap(const SkBitmap& bitmap) const; + + // Returns true if the current cursor object contains the same + // cursor as the cursor object passed in. If the current cursor + // is a custom cursor, we also compare the bitmaps to verify + // whether they are equal. + bool IsEqual(const WebCursor& other) const; + +protected: + Type type_; + int hotspot_x_; + int hotspot_y_; + SkBitmap bitmap_; +}; + +#endif // WEBCURSOR_H__ diff --git a/webkit/glue/webdatasource.h b/webkit/glue/webdatasource.h new file mode 100644 index 0000000..232e21a --- /dev/null +++ b/webkit/glue/webdatasource.h @@ -0,0 +1,219 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBDATASOURCE_H__ +#define WEBKIT_GLUE_WEBDATASOURCE_H__ + +#include <vector> + +#include "base/basictypes.h" +#include "googleurl/src/gurl.h" + +struct PasswordForm; +class SearchableFormData; +class WebFrame; +class WebRequest; +class WebResponse; + +class WebDataSource { + public: + // + // @method webFrame + // @result Return the frame that represents this data source. + // - (WebFrame *)webFrame; + virtual WebFrame* GetWebFrame() = 0; + + // Returns a reference to the original request data that created the + // datasource. This request will be unmodified by WebKit. + // + // Note that this will be a different physical object than the WebRequest + // that was specified in the load request initiated by the embedder, but the + // data members will be copied. + // + // This call will update the request with the latest information from WebKit, + // so it is important that the caller not cache the result or keep the + // reference across entries into WebKit. + virtual const WebRequest& GetInitialRequest() const = 0; + + // Returns the request that was used to create this datasource. This may + // be modified by WebKit. + // + // Note that this will be a different physical object than the WebRequest + // that was specified in the load request initiated by the embedder. + // + // This call will update the request with the latest information from WebKit, + // so it is important that the caller not cache the result or keep the + // reference across entries into WebKit. + virtual const WebRequest& GetRequest() const = 0; + + // Returns the response associated to this datasource. + virtual const WebResponse& GetResponse() const = 0; + + virtual std::wstring GetResponseMimeType() const = 0; + + // + // @method unreachableURL + // @discussion This will be non-nil only for dataSources created by calls to the + // WebFrame method loadAlternateHTMLString:baseURL:forUnreachableURL:. + // @result returns the unreachableURL for which this dataSource is showing alternate content, or nil + // - (NSURL *)unreachableURL; + virtual GURL GetUnreachableURL() const = 0; + + // Returns true if there is a non-null unreachable URL. + virtual bool HasUnreachableURL() const = 0; + + // Returns all redirects that occurred (both client and server) before at + // last committing the current page. This will contain one entry for each + // intermediate URL, and one entry for the last URL (so if there are no + // redirects, it will contain exactly the current URL, and if there is one + // redirect, it will contain the source and destination URL). + virtual const std::vector<GURL>& GetRedirectChain() const = 0; + + // Returns the SearchableFormData, or NULL if the request wasn't a search + // request. The returned object is owned by the WebDataSource (actually + // the WebDocumentLoader) and shouldn't be freed. + virtual const SearchableFormData* GetSearchableFormData() const = 0; + + // Returns the PasswordFormData, or NULL if the request isn't a form submission + // or doesn't have any password fields. The returned object is owned by the + // WebDataSource (actually the WebDocumentLoader) and shouldn't be freed. + virtual const PasswordForm* GetPasswordFormData() const = 0; + + // Returns true if this request was the result of submitting a form. + // NOTE: this returns false if the user submitted a form, but the form used + // script to do the actuall submission. + virtual bool IsFormSubmit() const = 0; + + /* + These functions are not implemented yet, and we are not yet sure whether or not + we need them, so we have commented them out both here and in the + webdatasource_impl.cc file. + + // + // @method data + // @discussion The data will be incomplete until the datasource has completely loaded. + // @result Returns the raw data associated with the datasource. Returns nil + // if the datasource hasn't loaded any data. + // - (NSData *)data; + virtual void GetData(IStream** data) = 0; + + // + // @method representation + // @discussion A representation holds a type specific representation + // of the datasource's data. The representation class is determined by mapping + // a MIME type to a class. The representation is created once the MIME type + // of the datasource content has been determined. + // @result Returns the representation associated with this datasource. + // Returns nil if the datasource hasn't created it's representation. + // - (id <WebDocumentRepresentation>)representation; + virtual void GetRepresentation(IWebDocumentRepresentation** rep) = 0; + + // + // @method response + // @result returns the WebResourceResponse for the data source. + // - (NSURLResponse *)response; + virtual void GetResponse(IWebURLResponse** response) = 0; + + // + // @method textEncodingName + // @result Returns either the override encoding, as set on the WebView for this + // dataSource or the encoding from the response. + // - (NSString *)textEncodingName; + virtual void GetTextEncodingName(std::wstring* name) = 0; + + // + // @method isLoading + // @discussion Returns YES if there are any pending loads. + // - (BOOL)isLoading; + virtual bool IsLoading() = 0; + + // + // @method pageTitle + // @result Returns nil or the page title. + // - (NSString *)pageTitle; + virtual void GetPageTitle(std::wstring* title) = 0; + + // + // @method webArchive + // @result A WebArchive representing the data source, its subresources and child frames. + // @description This method constructs a WebArchive using the original downloaded data. + // In the case of HTML, if the current state of the document is preferred, webArchive should be + // called on the DOM document instead. + // - (WebArchive *)webArchive; + virtual void GetWebArchive(IWebArchive** archive) = 0; + + // + // @method mainResource + // @result A WebResource representing the data source. + // @description This method constructs a WebResource using the original downloaded data. + // This method can be used to construct a WebArchive in case the archive returned by + // WebDataSource's webArchive isn't sufficient. + // - (WebResource *)mainResource; + virtual void GetMainResource(IWebResource** resource) = 0; + + // + // @method subresources + // @abstract Returns all the subresources associated with the data source. + // @description The returned array only contains subresources that have fully downloaded. + // - (NSArray *)subresources; + virtual void GetSubresources(int* count, IWebResource*** resources); + + // + // method subresourceForURL: + // @abstract Returns a subresource for a given URL. + // @param URL The URL of the subresource. + // @description Returns non-nil if the data source has fully downloaded a subresource with the given URL. + // - (WebResource *)subresourceForURL:(NSURL *)URL; + virtual void GetSubresourceForURL(const std::wstring& url, + IWebResource** resource) = 0; + + // + // @method addSubresource: + // @abstract Adds a subresource to the data source. + // @param subresource The subresource to be added. + // @description addSubresource: adds a subresource to the data source's list of subresources. + // Later, if something causes the data source to load the URL of the subresource, the data source + // will load the data from the subresource instead of from the network. For example, if one wants to add + // an image that is already downloaded to a web page, addSubresource: can be called so that the data source + // uses the downloaded image rather than accessing the network. NOTE: If the data source already has a + // subresource with the same URL, addSubresource: will replace it. + // - (void)addSubresource:(WebResource *)subresource; + virtual void AddSubresource(IWebResource* subresource) = 0; + */ + + WebDataSource() { } + virtual ~WebDataSource() { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebDataSource); +}; + + + +#endif // #ifndef WEBKIT_GLUE_WEBDATASOURCE_H__ diff --git a/webkit/glue/webdatasource_impl.cc b/webkit/glue/webdatasource_impl.cc new file mode 100644 index 0000000..7f018b2 --- /dev/null +++ b/webkit/glue/webdatasource_impl.cc @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2006 Apple Computer, 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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" + +#pragma warning(push, 0) +#include "KURL.h" +#include "FrameLoadRequest.h" +#include "ResourceRequest.h" +#pragma warning(pop) +#undef LOG + +#include "base/string_util.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/password_form.h" +#include "webkit/glue/webdatasource_impl.h" +#include "webkit/glue/webdocumentloader_impl.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/weburlrequest_impl.h" + +// WebDataSource ---------------------------------------------------------------- + +WebDataSourceImpl::WebDataSourceImpl(WebFrameImpl* frame, + WebDocumentLoaderImpl* loader) : + frame_(frame), + loader_(loader), + initial_request_(loader->initialRequest()), + request_(loader->request()) { +} + +WebDataSourceImpl::~WebDataSourceImpl() { +} + +// static +WebDataSourceImpl* WebDataSourceImpl::CreateInstance( + WebFrameImpl* frame, WebDocumentLoaderImpl* loader) { + return new WebDataSourceImpl(frame, loader); +} + +// WebDataSource +WebFrame* WebDataSourceImpl::GetWebFrame() { + return frame_; +} + +const WebRequest& WebDataSourceImpl::GetInitialRequest() const { + // WebKit may change the frame load request as it sees fit, so we must sync + // our request object. + initial_request_.set_frame_load_request( + WebCore::FrameLoadRequest(loader_->initialRequest())); + return initial_request_; +} + +const WebRequest& WebDataSourceImpl::GetRequest() const { + // WebKit may change the frame load request as it sees fit, so we must sync + // our request object. + request_.set_frame_load_request( + WebCore::FrameLoadRequest(loader_->request())); + return request_; +} + +const WebResponse& WebDataSourceImpl::GetResponse() const { + response_.set_resource_response(loader_->response()); + return response_; +} + +void WebDataSourceImpl::SetExtraData(WebRequest::ExtraData* extra) { + initial_request_.SetExtraData(extra); + request_.SetExtraData(extra); +} + +std::wstring WebDataSourceImpl::GetResponseMimeType() const { + return webkit_glue::StringToStdWString(loader_->responseMIMEType()); +} + +GURL WebDataSourceImpl::GetUnreachableURL() const { + const WebCore::KURL& url = loader_->unreachableURL(); + return url.isEmpty() ? GURL() : webkit_glue::KURLToGURL(url); +} + +bool WebDataSourceImpl::HasUnreachableURL() const { + const WebCore::KURL& url = loader_->unreachableURL(); + return !url.isEmpty(); +} + +const std::vector<GURL>& WebDataSourceImpl::GetRedirectChain() const { + return redirect_chain_; +} + +void WebDataSourceImpl::ClearRedirectChain() { + redirect_chain_.clear(); +} + +void WebDataSourceImpl::AppendRedirect(const GURL& url) { + redirect_chain_.push_back(url); +} + +const SearchableFormData* WebDataSourceImpl::GetSearchableFormData() const { + return loader_->searchable_form_data(); +} + +const PasswordForm* WebDataSourceImpl::GetPasswordFormData() const { + return loader_->password_form_data(); +} + +bool WebDataSourceImpl::IsFormSubmit() const { + return loader_->is_form_submit(); +} + +/* +See comment in webdatasource.h + +void WebDataSourceImpl::GetData(IStream** data) { + DebugBreak(); +} + +void WebDataSourceImpl::GetRepresentation(IWebDocumentRepresentation** rep) { + DebugBreak(); +} + +void WebDataSourceImpl::GetResponse(IWebURLResponse** response) { + DebugBreak(); +} + +std::wstring WebDataSourceImpl::GetTextEncodingName() { + DebugBreak(); + return L""; +} + +bool WebDataSourceImpl::IsLoading() { + DebugBreak(); +} + +std::wstring WebDataSourceImpl::GetPageTitle() { + DebugBreak(); + return L""; +} + +void WebDataSourceImpl::GetWebArchive(IWebArchive** archive) { + DebugBreak(); +} + +void WebDataSourceImpl::GetMainResource(IWebResource** resource) { + DebugBreak(); +} + +void WebDataSourceImpl::GetSubresources(int* count, IWebResource*** resources) { + DebugBreak(); +} + +void WebDataSourceImpl::GetSubresourceForURL(const std::wstring& url, + IWebResource** resource) { + DebugBreak(); +} + +void WebDataSourceImpl::AddSubresource(IWebResource* subresource) { + DebugBreak(); +} +*/ diff --git a/webkit/glue/webdatasource_impl.h b/webkit/glue/webdatasource_impl.h new file mode 100644 index 0000000..baa476b --- /dev/null +++ b/webkit/glue/webdatasource_impl.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2006 Apple Computer, 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 WEBKIT_GLUE_WEBDATASOURCE_IMPL_H__ +#define WEBKIT_GLUE_WEBDATASOURCE_IMPL_H__ + +#include "webkit/glue/webdatasource.h" +#include "webkit/glue/webresponse_impl.h" +#include "webkit/glue/weburlrequest_impl.h" +#include "base/basictypes.h" + +struct PasswordForm; +class SearchableFormData; +class WebFrameImpl; +class WebDocumentLoaderImpl; + +class WebDataSourceImpl : public WebDataSource { + public: + static WebDataSourceImpl* CreateInstance(WebFrameImpl* frame, + WebDocumentLoaderImpl* loader); + + protected: + WebDataSourceImpl(WebFrameImpl* frame, WebDocumentLoaderImpl* loader); + ~WebDataSourceImpl(); + + public: + // WebDataSource + virtual WebFrame* GetWebFrame(); + virtual const WebRequest& GetInitialRequest() const; + virtual const WebRequest& GetRequest() const; + virtual const WebResponse& GetResponse() const; + virtual std::wstring GetResponseMimeType() const; + virtual GURL GetUnreachableURL() const; + virtual bool HasUnreachableURL() const; + virtual const std::vector<GURL>& GetRedirectChain() const; + + // WebDataSourceImpl + + // Called after creating a new data source if there is request info + // available. Since we store copies of the WebRequests, the original + // WebRequest that the embedder created was lost, and the exra data would + // go with it. This preserves the request info so retrieving the requests + // later will have the same data. + void SetExtraData(WebRequest::ExtraData* extra); + + void ClearRedirectChain(); + void AppendRedirect(const GURL& url); + + virtual const SearchableFormData* GetSearchableFormData() const; + virtual const PasswordForm* GetPasswordFormData() const; + + virtual bool IsFormSubmit() const; + +private: + WebFrameImpl* frame_; + WebDocumentLoaderImpl* loader_; + + // Mutable because the const getters will magically sync these to the + // latest version from WebKit. + mutable WebRequestImpl initial_request_; + mutable WebRequestImpl request_; + mutable WebResponseImpl response_; + + // Lists all intermediate URLs that have redirected for the current + // provisional load. See WebFrameLoaderClient:: + // dispatchDidReceiveServerRedirectForProvisionalLoad for a description of + // who modifies this when to keep it up to date. + std::vector<GURL> redirect_chain_; + + DISALLOW_EVIL_CONSTRUCTORS(WebDataSourceImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBDATASOURCE_IMPL_H__ diff --git a/webkit/glue/webdocumentloader_impl.cc b/webkit/glue/webdocumentloader_impl.cc new file mode 100644 index 0000000..b33896f --- /dev/null +++ b/webkit/glue/webdocumentloader_impl.cc @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 Apple Computer, 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "webkit/glue/webdocumentloader_impl.h" +#undef LOG + +#include "base/logging.h" + +using WebCore::DocumentLoader; +using WebCore::ResourceRequest; +using WebCore::SubstituteData; + +WebDocumentLoaderImpl::WebDocumentLoaderImpl(const ResourceRequest& request, + const SubstituteData& data) + : DocumentLoader(request, data), + lock_history_(false), + form_submit_(false) { +} + +void WebDocumentLoaderImpl::SetDataSource(WebDataSource* datasource) { + datasource_.reset(datasource); +} + +WebDataSource* WebDocumentLoaderImpl::GetDataSource() const { + return datasource_.get(); +} + +// DocumentLoader +void WebDocumentLoaderImpl::attachToFrame() { + DocumentLoader::attachToFrame(); + if (detached_datasource_.get()) { + DCHECK(datasource_.get()); + datasource_.reset(detached_datasource_.release()); + } +} + +void WebDocumentLoaderImpl::detachFromFrame() { + DocumentLoader::detachFromFrame(); + detached_datasource_.reset(datasource_.release()); +} diff --git a/webkit/glue/webdocumentloader_impl.h b/webkit/glue/webdocumentloader_impl.h new file mode 100644 index 0000000..9ad3ee3 --- /dev/null +++ b/webkit/glue/webdocumentloader_impl.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2006 Apple Computer, 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 WEBKIT_GLUE_WEBDOCUMENTLOADER_IMPL_H__ +#define WEBKIT_GLUE_WEBDOCUMENTLOADER_IMPL_H__ + +#pragma warning(push, 0) +#include "DocumentLoader.h" +#pragma warning(pop) + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/password_form.h" +#include "webkit/glue/searchable_form_data.h" +#include "webkit/glue/webdatasource.h" + +class WebDataSource; + +class WebDocumentLoaderImpl : public WebCore::DocumentLoader +{ + public: + WebDocumentLoaderImpl(const WebCore::ResourceRequest&, const WebCore::SubstituteData&); + + void SetDataSource(WebDataSource*); + WebDataSource* GetDataSource() const; + + void SetLockHistory(bool lock_history) { lock_history_ = lock_history; } + bool GetLockHistory() const { return lock_history_; } + + // DocumentLoader + virtual void attachToFrame(); + virtual void detachFromFrame(); + + // Sets the SearchableFormData for this DocumentLoader. + // WebDocumentLoaderImpl will own the SearchableFormData. + void set_searchable_form_data(SearchableFormData* searchable_form_data) { + searchable_form_data_.reset(searchable_form_data); + } + // Returns the SearchableFormData for this DocumentLoader. + // WebDocumentLoaderImpl owns the returned SearchableFormData. + const SearchableFormData* searchable_form_data() const { + return searchable_form_data_.get(); + } + + // Sets the PasswordFormData for this DocumentLoader. + // WebDocumentLoaderImpl will own the PasswordFormData. + void set_password_form_data(PasswordForm* password_form_data) { + password_form_data_.reset(password_form_data); + } + // Returns the PasswordFormData for this DocumentLoader. + // WebDocumentLoaderImpl owns the returned PasswordFormData. + const PasswordForm* password_form_data() const { + return password_form_data_.get(); + } + + void set_form_submit(bool value) { + form_submit_ = value; + } + bool is_form_submit() const { + return form_submit_; + } + + private: + scoped_ptr<WebDataSource> datasource_; + scoped_ptr<WebDataSource> detached_datasource_; + scoped_ptr<const SearchableFormData> searchable_form_data_; + scoped_ptr<const PasswordForm> password_form_data_; + + bool lock_history_; + + bool form_submit_; + + DISALLOW_EVIL_CONSTRUCTORS(WebDocumentLoaderImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBDOCUMENTLOADER_IMPL_H__ diff --git a/webkit/glue/webdropdata.cc b/webkit/glue/webdropdata.cc new file mode 100644 index 0000000..d8cb4ecf --- /dev/null +++ b/webkit/glue/webdropdata.cc @@ -0,0 +1,55 @@ +// Copyright 2008, 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 "webkit/glue/webdropdata.h" + +#include "base/clipboard_util.h" +#include "googleurl/src/gurl.h" +#include <shellapi.h> +#include <shlobj.h> + +/* static */ +void WebDropData::PopulateWebDropData(IDataObject* data_object, + WebDropData* drop_data) { + std::wstring url_str; + if (ClipboardUtil::GetUrl(data_object, &url_str, &drop_data->url_title)) { + GURL test_url(url_str); + if (test_url.is_valid()) + drop_data->url = test_url; + } + ClipboardUtil::GetFilenames(data_object, &drop_data->filenames); + ClipboardUtil::GetPlainText(data_object, &drop_data->plain_text); + ClipboardUtil::GetCFHtml(data_object, &drop_data->cf_html); + ClipboardUtil::GetTextHtml(data_object, &drop_data->text_html); + ClipboardUtil::GetFileContents(data_object, + &drop_data->file_description_filename, &drop_data->file_contents); + + // data_object used by the test_shell. + drop_data->data_object = data_object; +} diff --git a/webkit/glue/webdropdata.h b/webkit/glue/webdropdata.h new file mode 100644 index 0000000..dbc1f8b --- /dev/null +++ b/webkit/glue/webdropdata.h @@ -0,0 +1,74 @@ +// Copyright 2008, 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. +// +// A struct for managing data being dropped on a webview. This represents a +// union of all the types of data that can be dropped in a platform neutral +// way. + +#ifndef WEBKIT_GLUE_WEBDROPDATA_H__ +#define WEBKIT_GLUE_WEBDROPDATA_H__ + +#include <string> +#include <vector> +#include "googleurl/src/gurl.h" + +struct IDataObject; + +struct WebDropData { + // User is dropping a link on the webview. + GURL url; + std::wstring url_title; // The title associated with |url|. + + // User is dropping one or more files on the webview. + std::vector<std::wstring> filenames; + + // User is dragging plain text into the webview. + std::wstring plain_text; + + // User is dragging MS HTML into the webview (e.g., out of IE). + std::wstring cf_html; + + // User is dragging text/html into the webview (e.g., out of Firefox). + std::wstring text_html; + + // User is dragging data from the webview (e.g., an image). + std::wstring file_description_filename; + std::string file_contents; + + // A reference to the underlying IDataObject. This is a Windows drag and + // drop specific object. This should only be used by the test shell. + IDataObject* data_object; + + // Helper method for converting Window's specific IDataObject to a WebDropData + // object. + static void PopulateWebDropData(IDataObject* data_object, + WebDropData* drop_data); +}; + +#endif // WEBKIT_GLUE_WEBDROPDATA_H__ diff --git a/webkit/glue/weberror.h b/webkit/glue/weberror.h new file mode 100644 index 0000000..4540d8d --- /dev/null +++ b/webkit/glue/weberror.h @@ -0,0 +1,44 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBERROR_H__ +#define WEBKIT_GLUE_WEBERROR_H__ + +class GURL; + +class WebError { + public: + // The network error code. + virtual int GetErrorCode() const = 0; + + // The URL that failed. + virtual const GURL& GetFailedURL() const = 0; +}; + +#endif // WEBKIT_GLUE_WEBERROR_H__ diff --git a/webkit/glue/weberror_impl.cc b/webkit/glue/weberror_impl.cc new file mode 100644 index 0000000..54e2830 --- /dev/null +++ b/webkit/glue/weberror_impl.cc @@ -0,0 +1,55 @@ +// Copyright 2008, 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 "ResourceError.h" + +#undef LOG +#include "webkit/glue/weberror_impl.h" + +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webkit_glue.h" + +WebErrorImpl::WebErrorImpl(const WebCore::ResourceError& e) + : error_code_(e.errorCode()), + failed_url_(webkit_glue::StringToStdWString(e.failingURL())) { +} + +WebErrorImpl::WebErrorImpl(const WebError& e) + : error_code_(e.GetErrorCode()), + failed_url_(e.GetFailedURL()) { +} + +int WebErrorImpl::GetErrorCode() const { + return error_code_; +} + +const GURL& WebErrorImpl::GetFailedURL() const { + return failed_url_; +} diff --git a/webkit/glue/weberror_impl.h b/webkit/glue/weberror_impl.h new file mode 100644 index 0000000..79013b1 --- /dev/null +++ b/webkit/glue/weberror_impl.h @@ -0,0 +1,59 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBERROR_IMPL_H__ +#define WEBKIT_GLUE_WEBERROR_IMPL_H__ + +#include "base/basictypes.h" +#include "webkit/glue/weberror.h" +#include "googleurl/src/gurl.h" + +namespace WebCore { +class ResourceError; +}; + +class WebErrorImpl : public WebError { + public: + explicit WebErrorImpl(const WebCore::ResourceError& error); + explicit WebErrorImpl(const WebError& error); + ~WebErrorImpl() {} + + // WebError implementation: + virtual int GetErrorCode() const; + virtual const GURL& GetFailedURL() const; + + private: + int error_code_; + GURL failed_url_; + + // Disallow assignment operator + void operator=(const WebErrorImpl&); +}; + +#endif // WEBKIT_GLUE_WEBERROR_IMPL_H__ diff --git a/webkit/glue/webframe.h b/webkit/glue/webframe.h new file mode 100644 index 0000000..996d0db --- /dev/null +++ b/webkit/glue/webframe.h @@ -0,0 +1,377 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBFRAME_H__ +#define WEBKIT_GLUE_WEBFRAME_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/gfx/size.h" +#include "webkit/glue/console_message_level.h" +#include "webkit/glue/find_in_page_request.h" + +class GURL; +class PlatformContextSkia; +class WebDataSource; +class WebError; +class WebRequest; +class WebView; +class WebTextInput; +struct NPObject; + +namespace gfx { +class BitmapPlatformDevice; +class Size; +class Rect; +} + +// TODO(darin): use GURL everywhere a URL string appears + +// Every frame in a web page is represented by one WebFrame, including the +// outermost frame. +class WebFrame : public base::RefCounted<WebFrame> { + public: + WebFrame() {} + virtual ~WebFrame() {} + + // Binds a C++ class to a JavaScript property of the window object. This + // should generally be used via CppBoundClass::BindToJavascript() instead of + // calling it directly. + virtual void BindToWindowObject(const std::wstring& name, + NPObject* object) = 0; + + virtual void CallJSGC() = 0; + + // WARNING: DON'T USE THIS METHOD unless you know what it is doing. + // + // Returns a pointer to the underlying implementation WebCore::Frame. + // Currently it is a hack to avoid including "Frame.h". The caller + // casts the return value to WebCore::Frame. + // TODO(fqian): Remove this method when V8 supports NP runtime. + virtual void* GetFrameImplementation() = 0; + + // Loads the given WebRequest. + virtual void LoadRequest(WebRequest* request) = 0; + + // This method is short-hand for calling LoadAlternateHTMLString with a dummy + // request for the given base_url. + virtual void LoadHTMLString(const std::string& html_text, + const GURL& base_url) = 0; + + // Loads alternative HTML text in place of a particular URL. This method is + // designed with error pages in mind, in which case it would typically be + // called in response to WebViewDelegate's didFailProvisionalLoadWithError + // method. + // + // |html_text| is a utf8 string to load in the frame. |display_url| is the + // URL that the content will appear to have been loaded from. The |replace| + // parameter controls how this affects session history. If |replace| is + // true, then the current session history entry is replaced with the given + // HTML text. Otherwise, a new navigation is produced. + // + // In either case, when the corresponding session history entry is revisited, + // it is the given request /w the |display_url| substituted for the request's + // URL, which is repeated. The |html_text| is not stored in session history. + // + virtual void LoadAlternateHTMLString(const WebRequest* request, + const std::string& html_text, + const GURL& display_url, + bool replace) = 0; + + // Asks the WebFrame to try and download the alternate error page. We notify + // the WebViewDelegate of the results so it can decide whether or not to show + // something to the user (e.g., a local error page or the alternate error + // page). + virtual void LoadAlternateHTMLErrorPage(const WebRequest* request, + const WebError& error, + const GURL& error_page_url, + bool replace, + const GURL& fake_url) = 0; + + // Returns a string representing the state of the previous page load for + // later use when loading as well as the uri and title of the page. The + // previous page is the page that was loaded before DidCommitLoadForFrame was + // received. Returns false if there is no state. + virtual bool GetPreviousState(GURL* url, std::wstring* title, + std::string* history_state) const = 0; + + // Returns a string representing the state of the current page load for later + // use when loading as well as the url and title of the page. Returns false + // if there is no state. + virtual bool GetCurrentState(GURL* url, std::wstring* title, + std::string* history_state) const = 0; + + // Returns true if there is a current history item. A newly created WebFrame + // lacks a history item. Otherwise, this will always be true. + virtual bool HasCurrentState() const = 0; + + // Returns the current URL of the frame, or the empty string if there is no + // URL to retrieve (for example, the frame may never have had any content). + virtual GURL GetURL() const = 0; + + // Returns the URL to the favorite icon for the frame. An empty string is + // returned if the frame has not finished loading, or the frame's URL + // protocol is not http or https. + virtual GURL GetFavIconURL() const = 0; + + // Returns the URL to the OpenSearch description document for the frame. If + // the page does not have a valid document, an empty GURL is returned. + virtual GURL GetOSDDURL() const = 0; + + // Returns the committed data source, which is the last data source that has + // successfully started loading. Will return NULL if no provisional data + // has been committed. + virtual WebDataSource* GetDataSource() const = 0; + + // Returns the provisional data source, which is a data source where a + // request has been made, but we are not sure if we will use data from it + // (for example, it may be an invalid URL). When the provisional load is + // "committed," it will become the "real" data source (see GetDataSource + // above) and the provisional data source will be NULL. + virtual WebDataSource* GetProvisionalDataSource() const = 0; + + // + // @method stopLoading + // @discussion Stop any pending loads on the frame's data source, + // and its children. + // - (void)stopLoading; + virtual void StopLoading() = 0; + + // Returns the frame that opened this frame, or NULL if this window has no + // opener. + virtual WebFrame* GetOpener() const = 0; + + // Returns the frame containing this frame, or NULL of this is a top level + // frame with no parent. + virtual WebFrame* GetParent() const = 0; + + // Returns the child frame with the given xpath. + // The document of this frame is used as the context node. + // The xpath may need a recursive traversal if non-trivial + // A non-trivial xpath will contain a combination of xpaths + // (delimited by '\n') leading to an inner subframe. + // + // Example: /html/body/iframe/\n/html/body/div/iframe/\n/frameset/frame[0] + // can be broken into 3 xpaths + // /html/body/iframe evaluates to an iframe within the root frame + // /html/body/div/iframe evaluates to an iframe within the level-1 iframe + // /frameset/frame[0] evaluates to first frame within the level-2 iframe + virtual WebFrame* GetChildFrame(const std::wstring& xpath) const = 0; + + // Returns a pointer to the WebView that contains this WebFrame. This + // pointer is not AddRef'd and is only valid for the lifetime of the WebFrame + // unless it is AddRef'd separately by the caller. + virtual WebView* GetView() const = 0; + + // Fills the contents of this frame into the given string. If the text is + // longer than max_chars, it will be clipped to that length. Warning: this + // function may be slow depending on the number of characters retrieved and + // page complexity. For a typically sized page, expect it to take on the + // order of milliseconds. + // + // If there is room, subframe text will be recursively appended. Each frame + // will be separated by an empty line. + virtual void GetContentAsPlainText(int max_chars, + std::wstring* text) const = 0; + + // Searches a frame for a given string. + // + // If a match is found, this function will select it (scrolling down to make + // it visible if needed) and fill in the IntRect (selection_rect) with the + // location of where the match was found (in screen coordinates). + // + // If no match is found, this function clears all tickmarks and highlighting. + // + // Returns true if the search string was found, false otherwise. + virtual bool Find(const FindInPageRequest& request, + bool wrap_within_frame, + gfx::Rect* selection_rect) = 0; + + // Searches a frame for the next (or previous occurrence of a given string. + // + // This function works similarly to Find (documented above), except that it + // uses an index into the tick-mark vector to figure out what the next + // match is, alleviating the need to call findString on the content again. + // + // Returns true if the search string was found, false otherwise. + virtual bool FindNext(const FindInPageRequest& request, + bool wrap_within_frame) = 0; + + // Notifies the frame that we are no longer interested in searching. This will + // abort any asynchronous scoping effort already under way (see the function + // ScopeStringMatches for details) and erase all tick-marks and highlighting + // from the previous search. + virtual void StopFinding() = 0; + + // Counts how many times a particular string occurs within the frame. It + // also retrieves the location of the string and updates a vector in the frame + // so that tick-marks and highlighting can be drawn. This function does its + // work asynchronously, by running for a certain time-slice and then + // scheduling itself (co-operative multitasking) to be invoked later + // (repeating the process until all matches have been found). This allows + // multiple frames to be searched at the same time and provides a way to + // cancel at any time (see CancelPendingScopingEffort). The parameter Request + // specifies what to look for and Reset signals whether this is a brand new + // request or a continuation of the last scoping effort. + virtual void ScopeStringMatches(FindInPageRequest request, + bool reset) = 0; + + // Cancels any outstanding requests for scoping string matches on a frame. + virtual void CancelPendingScopingEffort() = 0; + + // This function is called on the mainframe during the scoping effort to keep + // a running tally of the accumulated total match-count for all frames. After + // updating the count it will notify the render-view about the new count. + virtual void IncreaseMatchCount(int count, int request_id) = 0; + + // Notifies the webview-delegate about a new selection rect. This will result + // in the browser getting notified. For more information see WebViewDelegate. + virtual void ReportFindInPageSelection(const gfx::Rect& selection_rect, + int active_match_ordinal, + int request_id) = 0; + + // This function is called on the mainframe to reset the total number of + // matches found during the scoping effort. + virtual void ResetMatchCount() = 0; + + // Returns true if the frame is visible (defined as width > 0 and height > 0). + virtual bool Visible() = 0; + + // Selects all the text in the frame. + virtual void SelectAll() = 0; + + // + // - (void)copy:(id)sender; + virtual void Copy() = 0; + + // + // - (void)cut:(id)sender; + virtual void Cut() = 0; + + // + // - (void)paste:(id)sender; + virtual void Paste() = 0; + + // Replace the selection text by a given text. + virtual void Replace(const std::wstring& text) = 0; + + // + // - (void)delete:(id)sender; + // Delete as in similar to Cut, not as in teardown + virtual void Delete() = 0; + + // Undo the last text editing command. + virtual void Undo() = 0; + + // Redo the last undone text editing command. + virtual void Redo() = 0; + + // Clear any text selection in the frame. + virtual void ClearSelection() = 0; + + // Paints the contents of this web view in a bitmapped image. This image + // will not have plugins drawn. Devices are cheap to copy because the data is + // internally refcounted, so we return by value. + // + // Set scroll_to_zero to force all frames to be scrolled to 0,0 before + // being painted into the image. This will not send DOM events because it + // just draws the contents at a different place, but it does mean the + // scrollbars in the resulting image will appear to be wrong (they'll be + // painted as if the content was scrolled). + virtual gfx::BitmapPlatformDevice CaptureImage(bool scroll_to_zero) = 0; + + // This function sets a flag within WebKit to instruct it to render the page + // as View-Source (showing the HTML source for the page). + virtual void SetInViewSourceMode(bool enable) = 0; + + // This function returns whether this frame is in "view-source" mode. + virtual bool GetInViewSourceMode() const = 0; + + // Returns the frame name. + virtual std::wstring GetName() = 0; + + // Returns a pointer to the WebTextInput object associated with the frame. + // The caller does not own the object returned. + virtual WebTextInput* GetTextInput() = 0; + + // Executes a webkit editor command. The supported commands are a + // superset of those accepted by javascript:document.execCommand(). + // This method is exposed in order to implement + // javascript:layoutTestController.execCommand() + virtual bool ExecuteCoreCommandByName(const std::string& name, const std::string& value) = 0; + + // Adds a message to the frame's console. + virtual void AddMessageToConsole(const std::wstring& msg, + ConsoleMessageLevel level) = 0; + + // Tells the current page to close, running the onunload handler. + // TODO(creis): We'd rather use WebView::Close(), but that sets its delegate_ + // to NULL, preventing any JavaScript dialogs in the onunload handler from + // appearing. This lets us shortcut that for now, but we should refactor + // close messages so that this isn't necessary. + virtual void ClosePage() = 0; + + // The current scroll offset from the top of frame in pixels. + virtual gfx::Size ScrollOffset() const = 0; + + // Reformats the web page, i.e. the main frame and its subframes, for printing + // or for screen display, depending on |printing| argument. |page_width_min| + // and |page_width_max| are the minimum and maximum width, in pixels, that the + // layout can try to fit the whole content. |width| is the resulted choosen + // document width in pixel. + // Note: It fails if the main frame failed to load. It will succeed even if a + // child frame failed to load. + virtual bool SetPrintingMode(bool printing, + float page_width_min, + float page_width_max, + int* width) = 0; + + // Layouts the web page on paper. Calculates the rectangle of the web page + // each pages will "see". Then you can retrieve the exact view of a paper page + // with GetPageRect. + // Returns the number of printed pages computed. + virtual int ComputePageRects(const gfx::Size& page_size_px) = 0; + + // Retrieves the paper page's view of the web page. + virtual void GetPageRect(int page, gfx::Rect* page_size) const = 0; + + // Prints one page. |page| is 0-based. + virtual bool SpoolPage(int page, + PlatformContextSkia* context) = 0; + + // Does this frame have an onunload or unbeforeunload event listener? + virtual bool HasUnloadListener() = 0; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebFrame); +}; + +#endif // WEBKIT_GLUE_WEBFRAME_H__ diff --git a/webkit/glue/webframe_impl.cc b/webkit/glue/webframe_impl.cc new file mode 100644 index 0000000..d6288ed --- /dev/null +++ b/webkit/glue/webframe_impl.cc @@ -0,0 +1,1723 @@ +/* + * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2006 Apple Computer, 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// How ownership works +// ------------------- +// +// Big oh represents a refcounted relationship: owner O--- ownee +// +// WebView (for the toplevel frame only) +// O +// | +// O +// WebFrame <-------------------------- FrameLoader +// O (via WebFrameLoaderClient) || +// | || +// +------------------------------+ || +// | || +// FrameView O-------------------------O Frame +// +// FrameLoader and Frame are formerly one object that was split apart because +// it got too big. They basically have the same lifetime, hence the double line. +// +// WebFrame is refcounted and has one ref on behalf of the FrameLoader/Frame +// and, in the case of the toplevel frame, one more for the WebView. This is +// not a normal reference counted pointer because that would require changing +// WebKit code that we don't control. Instead, it is created with this ref +// initially and it is removed when the FrameLoader is getting destroyed. +// +// WebFrames are created in two places, first in WebViewImpl when the root +// frame is created, and second in WebFrame::CreateChildFrame when sub-frames +// are created. WebKit will hook up this object to the FrameLoader/Frame +// and the refcount will be correct. +// +// How frames are destroyed +// ------------------------ +// +// The main frame is never destroyed and is re-used. The FrameLoader is +// re-used and a reference is also kept by the WebView, so the root frame will +// generally have a refcount of 2. +// +// When frame content is replaced, all subframes are destroyed. This happens +// in FrameLoader::detachFromParent for each suframe. Here, we first clear +// the view in the Frame, breaking the circular cycle between Frame and +// FrameView. Then it calls detachedFromParent4 on the FrameLoaderClient. +// +// The FrameLoaderClient is implemented by WebFrameLoaderClient, which is +// an object owned by WebFrame. It calls WebFrame::Closing which causes +// WebFrame to release its references to Frame, generally releasing it. +// +// Frame going away causes the FrameLoader to get deleted. In FrameLoader's +// destructor it notifies its client with frameLoaderDestroyed. This derefs +// WebView and will cause it to be deleted (unless an external someone is also +// holding a reference). + +#include "config.h" + +#include <algorithm> +#include <string> + +#pragma warning(push, 0) +#include "HTMLFormElement.h" // need this before Document.h +#include "Chrome.h" +#include "Document.h" +#include "DocumentFragment.h" // Only needed for ReplaceSelectionCommand.h :( +#include "DocumentLoader.h" +#include "Editor.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "FrameWin.h" +#include "graphics/SkiaUtils.h" +#include "GraphicsContext.h" +#include "HTMLHeadElement.h" +#include "HTMLLinkElement.h" +#include "HTMLNames.h" +#include "HistoryItem.h" +#include "markup.h" +#include "Page.h" +#include "PlatformScrollBar.h" +#include "RenderFrame.h" +#include "RenderWidget.h" +#include "ReplaceSelectionCommand.h" +#include "ResourceHandle.h" +#include "ResourceHandleWin.h" +#include "ResourceRequest.h" +#include "SelectionController.h" +#include "Settings.h" +#include "SubstituteData.h" +#include "TextIterator.h" +#include "TextAffinity.h" +#include "xml/XPathResult.h" + +#pragma warning(pop) + +#undef LOG +#include "base/gfx/bitmap_platform_device.h" +#include "base/gfx/rect.h" +#include "base/gfx/platform_canvas.h" +#include "base/message_loop.h" +#include "base/stats_counters.h" +#include "base/string_util.h" +#include "base/time.h" +#include "net/base/net_errors.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/glue_serialize.h" +#include "webkit/glue/alt_error_page_resource_fetcher.h" +#include "webkit/glue/webdocumentloader_impl.h" +#include "webkit/glue/weberror_impl.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webhistoryitem_impl.h" +#include "webkit/glue/webtextinput_impl.h" +#include "webkit/glue/webview_impl.h" +#include "webkit/port/page/ChromeClientWin.h" +#include "webkit/port/platform/WidgetClientWin.h" + +using WebCore::ChromeClientWin; +using WebCore::Color; +using WebCore::DeprecatedString; +using WebCore::Document; +using WebCore::DocumentLoader; +using WebCore::ExceptionCode; +using WebCore::GraphicsContext; +using WebCore::HTMLFrameOwnerElement; +using WebCore::Frame; +using WebCore::FrameLoader; +using WebCore::FrameLoadRequest; +using WebCore::FrameLoadType; +using WebCore::FrameTree; +using WebCore::FrameView; +using WebCore::HistoryItem; +using WebCore::HTMLFrameElementBase; +using WebCore::IntRect; +using WebCore::KURL; +using WebCore::Node; +using WebCore::PlatformScrollbar; +using WebCore::Range; +using WebCore::ReloadIgnoringCacheData; +using WebCore::RenderObject; +using WebCore::ResourceError; +using WebCore::ResourceHandle; +using WebCore::ResourceRequest; +using WebCore::Selection; +using WebCore::SharedBuffer; +using WebCore::String; +using WebCore::SubstituteData; +using WebCore::TextIterator; +using WebCore::VisiblePosition; +using WebCore::WidgetClientWin; +using WebCore::XPathResult; + +static const wchar_t* const kWebFrameActiveCount = L"WebFrameActiveCount"; + +static const char* const kOSDType = "application/opensearchdescription+xml"; +static const char* const kOSDRel = "search"; + +// The separator between frames when the frames are converted to plain text. +static const wchar_t kFrameSeparator[] = L"\n\n"; +static const int kFrameSeparatorLen = arraysize(kFrameSeparator) - 1; + +// Backend for GetContentAsPlainText, this is a recursive function that gets +// the text for the current frame and all of its subframes. It will append +// the text of each frame in turn to the |output| up to |max_chars| length. +// +// The |frame| must be non-NULL. +static void FrameContentAsPlainText(int max_chars, Frame* frame, + std::wstring* output) { + Document* doc = frame->document(); + if (!doc) + return; + + // Select the document body. + RefPtr<Range> range(doc->createRange()); + ExceptionCode exception = 0; + range->selectNodeContents(doc->body(), exception); + + if (exception == 0) { + // The text iterator will walk nodes giving us text. This is similar to + // the plainText() function in TextIterator.h, but we implement the maximum + // size and also copy the results directly into a wstring, avoiding the + // string conversion. + for (TextIterator it(range.get()); !it.atEnd(); it.advance()) { + const wchar_t* chars = reinterpret_cast<const wchar_t*>(it.characters()); + if (chars) { + int to_append = std::min(it.length(), + max_chars - static_cast<int>(output->size())); + output->append(chars, to_append); + if (output->size() >= static_cast<size_t>(max_chars)) + return; // Filled up the buffer. + } + } + } + + // Recursively walk the children. + FrameTree* frame_tree = frame->tree(); + Frame* cur_child = frame_tree->firstChild(); + for (Frame* cur_child = frame_tree->firstChild(); cur_child; + cur_child = cur_child->tree()->nextSibling()) { + // Make sure the frame separator won't fill up the buffer, and give up if + // it will. The danger is if the separator will make the buffer longer than + // max_chars. This will cause the computation above: + // max_chars - output->size() + // to be a negative number which will crash when the subframe is added. + if (static_cast<int>(output->size()) >= max_chars - kFrameSeparatorLen) + return; + + output->append(kFrameSeparator, kFrameSeparatorLen); + FrameContentAsPlainText(max_chars, cur_child, output); + if (output->size() >= static_cast<size_t>(max_chars)) + return; // Filled up the buffer. + } +} + +// WebFrameImpl ---------------------------------------------------------------- + +int WebFrameImpl::live_object_count_ = 0; + +WebFrameImpl::WebFrameImpl() +// Don't complain about using "this" in initializer list. +#pragma warning(disable: 4355) + : frame_loader_client_(this), + scope_matches_factory_(this), +#pragma warning(default: 4355) + currently_loading_request_(NULL), + plugin_delegate_(NULL), + allows_scrolling_(true), + margin_width_(-1), + margin_height_(-1), + last_match_count_(-1), + total_matchcount_(-1), + inspected_node_(NULL), + active_tickmark_frame_(NULL), + active_tickmark_(WidgetClientWin::kNoTickmark), + last_active_range_(NULL), + frames_scoping_count_(-1), + scoping_complete_(false), + next_invalidate_after_(0), + printing_(false) { + StatsCounter(kWebFrameActiveCount).Increment(); + live_object_count_++; +} + +WebFrameImpl::~WebFrameImpl() { + StatsCounter(kWebFrameActiveCount).Decrement(); + live_object_count_--; + + CancelPendingScopingEffort(); +} + +// WebFrame ------------------------------------------------------------------- + +void WebFrameImpl::InitMainFrame(WebViewImpl* webview_impl) { + webview_impl_ = webview_impl; // owning ref + + Frame* frame = new Frame(webview_impl_->page(), 0, &frame_loader_client_); + + // Add reference on behalf of FrameLoader. See comments in + // WebFrameLoaderClient::frameLoaderDestroyed for more info. + AddRef(); + + frame_ = frame; + + // We must call init() after frame_ is assigned because it is referenced + // during init(). + frame_->init(); +} + +void WebFrameImpl::LoadRequest(WebRequest* request) { + SubstituteData data; + InternalLoadRequest(request, data, false); +} + +void WebFrameImpl::InternalLoadRequest(const WebRequest* request, + const SubstituteData& data, + bool replace) { + const WebRequestImpl* request_impl = + static_cast<const WebRequestImpl*>(request); + + const ResourceRequest& resource_request = + request_impl->frame_load_request().resourceRequest(); + + // Special-case javascript URLs. Do not interrupt the existing load when + // asked to load a javascript URL unless the script generates a result. + // We can't just use FrameLoader::executeIfJavaScriptURL because it doesn't + // handle redirects properly. + const KURL& kurl = resource_request.url(); + if (!data.isValid() && kurl.protocol() == "javascript") { + // Don't attempt to reload javascript URLs. + if (resource_request.cachePolicy() == ReloadIgnoringCacheData) + return; + + // We can't load a javascript: URL if there is no Document! + if (!frame_->document()) + return; + + // TODO(darin): Is this the best API to use here? It works and seems good, + // but will it change out from under us? + DeprecatedString script = + KURL::decode_string(kurl.deprecatedString().mid(sizeof("javascript:")-1)); + bool succ = false; + WebCore::String value = + frame_->loader()->executeScript(script, &succ, true); + if (succ && !frame_->loader()->isScheduledLocationChangePending()) { + // TODO(darin): We need to figure out how to represent this in session + // history. Hint: don't re-eval script when the user or script navigates + // back-n-forth (instead store the script result somewhere). + LoadDocumentData(kurl, value, String("text/html"), String()); + } + return; + } + + StopLoading(); // make sure existing activity stops + + // Keep track of the request temporarily. This is effectively a way of + // passing the request to callbacks that may need it. See + // WebFrameLoaderClient::createDocumentLoader. + currently_loading_request_ = request; + + // If we have a current datasource, save the request info on it immediately. + // This is because WebCore may not actually initiate a load on the toplevel + // frame for some subframe navigations, so we want to update its request. + WebDataSourceImpl* datasource = GetDataSourceImpl(); + if (datasource) + CacheCurrentRequestInfo(datasource); + + if (data.isValid()) { + frame_->loader()->load(resource_request, data); + if (replace) { + // Do this to force WebKit to treat the load as replacing the currently + // loaded page. + frame_->loader()->setReplacing(); + } + } else if (request_impl->history_item()) { + // Use the history item if we have one, otherwise fall back to standard + // load. + RefPtr<HistoryItem> current_item = frame_->loader()->currentHistoryItem(); + + // If there is no current_item, which happens when we are navigating in + // session history after a crash, we need to manufacture one otherwise + // WebKit hoarks. This is probably the wrong thing to do, but it seems to + // work. + if (!current_item) { + current_item = new HistoryItem(KURL("about:blank"), ""); + frame_->loader()->setCurrentHistoryItem(current_item); + frame_->page()->backForwardList()->setCurrentItem(current_item.get()); + + // Mark the item as fake, so that we don't attempt to save its state and + // end up with about:blank in the navigation history. + frame_->page()->backForwardList()->setCurrentItemFake(true); + } + + frame_->loader()->goToItem(request_impl->history_item().get(), + WebCore::FrameLoadTypeIndexedBackForward); + } else if (resource_request.cachePolicy() == ReloadIgnoringCacheData) { + frame_->loader()->reload(); + } else { + frame_->loader()->load(resource_request); + } + + currently_loading_request_ = NULL; +} + +void WebFrameImpl::LoadHTMLString(const std::string& html_text, + const GURL& base_url) { + WebRequestImpl request(base_url); + LoadAlternateHTMLString(&request, html_text, GURL(), false); +} + +void WebFrameImpl::LoadAlternateHTMLString(const WebRequest* request, + const std::string& html_text, + const GURL& display_url, + bool replace) { + int len = static_cast<int>(html_text.size()); + RefPtr<SharedBuffer> buf(new SharedBuffer(html_text.data(), len)); + + SubstituteData subst_data( + buf, String("text/html"), String("UTF-8"), + webkit_glue::GURLToKURL(display_url)); + DCHECK(subst_data.isValid()); + + InternalLoadRequest(request, subst_data, replace); +} + +GURL WebFrameImpl::GetURL() const { + const WebDataSource* ds = GetDataSource(); + if (!ds) + return GURL(); + return ds->GetRequest().GetURL(); +} + +GURL WebFrameImpl::GetFavIconURL() const { + WebCore::FrameLoader* frame_loader = frame_->loader(); + // The URL to the favicon may be in the header. As such, only + // ask the loader for the favicon if it's finished loading. + if (frame_loader->state() == WebCore::FrameStateComplete) { + const KURL& url = frame_loader->iconURL(); + if (!url.isEmpty()) { + return webkit_glue::KURLToGURL(url); + } + } + return GURL(); +} + +GURL WebFrameImpl::GetOSDDURL() const { + WebCore::FrameLoader* frame_loader = frame_->loader(); + if (frame_loader->state() == WebCore::FrameStateComplete && + frame_->document() && frame_->document()->head() && + !frame_->tree()->parent()) { + WebCore::HTMLHeadElement* head = frame_->document()->head(); + if (head) { + RefPtr<WebCore::HTMLCollection> children = head->children(); + for (Node* child = children->firstItem(); child != NULL; + child = children->nextItem()) { + WebCore::HTMLLinkElement* link_element = + webkit_glue::CastHTMLElement<WebCore::HTMLLinkElement>( + child, WebCore::HTMLNames::linkTag); + if (link_element && link_element->type() == kOSDType && + link_element->rel() == kOSDRel && !link_element->href().isEmpty()) { + return GURL(link_element->href().charactersWithNullTermination()); + } + } + } + } + return GURL(); +} + +bool WebFrameImpl::GetPreviousState(GURL* url, std::wstring* title, + std::string* history_state) const { + // We use the previous item here because documentState (filled-out forms) + // only get saved to history when it becomes the previous item. The caller + // is expected to query the history state after a navigation occurs, after + // the desired history item has become the previous entry. + if (frame_->page()->backForwardList()->isPreviousItemFake()) + return false; + + RefPtr<HistoryItem> item = frame_->page()->backForwardList()->previousItem(); + if (!item) + return false; + + static StatsCounterTimer history_timer(L"GetHistoryTimer"); + StatsScope<StatsCounterTimer> history_scope(history_timer); + + webkit_glue::HistoryItemToString(item, history_state); + *url = webkit_glue::KURLToGURL(item->url()); + *title = webkit_glue::StringToStdWString(item->title()); + + return true; +} + +bool WebFrameImpl::GetCurrentState(GURL* url, std::wstring* title, + std::string* state) const { + if (frame_->loader()) + frame_->loader()->saveDocumentAndScrollState(); + RefPtr<HistoryItem> item = frame_->page()->backForwardList()->currentItem(); + if (!item) + return false; + + webkit_glue::HistoryItemToString(item, state); + *url = webkit_glue::KURLToGURL(item->url()); + *title = webkit_glue::StringToStdWString(item->title()); + + return true; +} + +bool WebFrameImpl::HasCurrentState() const { + return frame_->page()->backForwardList()->currentItem() != NULL; +} + +void WebFrameImpl::LoadDocumentData(const KURL& base_url, + const String& data, + const String& mime_type, + const String& charset) { + // TODO(darin): This is wrong. We need to re-cast this in terms of a call to + // one of the FrameLoader::load(...) methods. Else, WebCore will be angry!! + + // Requiring a base_url here seems like a good idea for security reasons. + ASSERT(!base_url.isEmpty()); + ASSERT(!mime_type.isEmpty()); + + StopLoading(); + + // Reset any pre-existing scroll offset + frameview()->setContentsPos(0, 0); + + // Make sure the correct document type is constructed. + frame_->loader()->setResponseMIMEType(mime_type); + + // TODO(darin): Inform the FrameLoader of the charset somehow. + + frame_->loader()->begin(base_url); + frame_->loader()->write(data); + frame_->loader()->end(); +} + +void WebFrameImpl::set_currently_loading_history_item( + WebHistoryItemImpl* item) { + currently_loading_history_item_ = item; +} + + +static WebDataSource* DataSourceForDocLoader(DocumentLoader* loader) { + return (loader ? + static_cast<WebDocumentLoaderImpl*>(loader)->GetDataSource() : NULL); +} + +WebDataSource* WebFrameImpl::GetDataSource() const { + if (!frame_->loader()) + return NULL; + return DataSourceForDocLoader(frame_->loader()->documentLoader()); +} + +WebDataSourceImpl* WebFrameImpl::GetDataSourceImpl() const { + return static_cast<WebDataSourceImpl*>(GetDataSource()); +} + +WebDataSource* WebFrameImpl::GetProvisionalDataSource() const { + FrameLoader* frame_loader = frame_->loader(); + if (!frame_loader) + return NULL; + + // We regard the policy document loader as still provisional. + DocumentLoader* doc_loader = frame_loader->provisionalDocumentLoader(); + if (!doc_loader) + doc_loader = frame_loader->policyDocumentLoader(); + + return DataSourceForDocLoader(doc_loader); +} + +WebDataSourceImpl* WebFrameImpl::GetProvisionalDataSourceImpl() const { + return static_cast<WebDataSourceImpl*>(GetProvisionalDataSource()); +} + +void WebFrameImpl::CacheCurrentRequestInfo(WebDataSourceImpl* datasource) { + // Cache our current request info on the data source. It contains its + // own requests, so the extra data needs to be transferred. + scoped_refptr<WebRequest::ExtraData> extra; + + // Our extra data may come from a request issued via LoadRequest, or a + // history navigation from WebCore. + if (currently_loading_request_) { + extra = currently_loading_request_->GetExtraData(); + } else if (currently_loading_history_item_) { + extra = currently_loading_history_item_->GetExtraData(); + currently_loading_history_item_ = 0; + } + + // We must only update this if it is valid, or the valid state will be lost. + if (extra) + datasource->SetExtraData(extra); +} + +void WebFrameImpl::StopLoading() { + if (!frame_) + return; + + // TODO(darin): Figure out what we should really do here. It seems like a + // bug that FrameLoader::stopLoading doesn't call stopAllLoaders. + frame_->loader()->stopAllLoaders(); + frame_->loader()->stopLoading(false); +} + +WebFrame* WebFrameImpl::GetOpener() const { + if (frame_) { + Frame* opener = frame_->loader()->opener(); + if (opener) + return FromFrame(opener); + } + return NULL; +} + +WebFrame* WebFrameImpl::GetParent() const { + if (frame_) { + Frame *parent = frame_->tree()->parent(); + if (parent) + return FromFrame(parent); + } + return NULL; +} + +WebFrame* WebFrameImpl::GetChildFrame(const std::wstring& xpath) const { + // xpath string can represent a frame deep down the tree (across multiple + // frame DOMs). + // Example, /html/body/table/tbody/tr/td/iframe\n/frameset/frame[0] + // should break into 2 xpaths + // /html/body/table/tbody/tr/td/iframe & /frameset/frame[0] + + if (xpath.empty()) + return NULL; + + std::wstring secondary; + String xpath_str; + + std::wstring::size_type delim_pos = xpath.find_first_of(L'\n'); + if (delim_pos != std::wstring::npos) { + std::wstring primary = xpath.substr(0, delim_pos); + secondary = xpath.substr(delim_pos + 1); + xpath_str = webkit_glue::StdWStringToString(primary); + } else { + xpath_str = webkit_glue::StdWStringToString(xpath); + } + + Document* document = frame_->document(); + + ExceptionCode ec = 0; + PassRefPtr<XPathResult> xpath_result = + document->evaluate(xpath_str, + document, + NULL, /* namespace */ + XPathResult::ORDERED_NODE_ITERATOR_TYPE, + NULL, /* XPathResult object */ + ec); + + if (!xpath_result.get()) + return NULL; + + Node* node = xpath_result->iterateNext(ec); + + if (!node || !node->isFrameOwnerElement()) + return NULL; + HTMLFrameOwnerElement* frame_element = + static_cast<HTMLFrameOwnerElement*>(node); + WebFrame* web_frame = FromFrame(frame_element->contentFrame()); + + if (secondary.empty()) + return web_frame; + else + return web_frame->GetChildFrame(secondary); +} + +void WebFrameImpl::SetInViewSourceMode(bool enable) { + if (frame_) + frame_->setInViewSourceMode(enable); +} + +bool WebFrameImpl::GetInViewSourceMode() const { + if (frame_) + return frame_->inViewSourceMode(); + + return false; +} + +WebView* WebFrameImpl::GetView() const { + return webview_impl_; +} + +void WebFrameImpl::BindToWindowObject(const std::wstring& name, + NPObject* object) { + assert(frame_); + if (!frame_ || !frame_->scriptBridge()->isEnabled()) + return; + + String key = webkit_glue::StdWStringToString(name); + frame_->scriptBridge()->BindToWindowObject(frame_.get(), key, object); +} + + +// Call JavaScript garbage collection. +void WebFrameImpl::CallJSGC() { + if (!frame_) return; + if (!frame_->settings()->isJavaScriptEnabled()) return; + frame_->scriptBridge()->CollectGarbage(); +} + + +void WebFrameImpl::GetContentAsPlainText(int max_chars, + std::wstring* text) const { + text->clear(); + if (!frame_) + return; + + FrameContentAsPlainText(max_chars, frame_.get(), text); +} + +void WebFrameImpl::InvalidateArea(AreaToInvalidate area) { + ASSERT(frame() && frame()->view()); + FrameView* view = frame()->view(); + + if ((area & INVALIDATE_ALL) == INVALIDATE_ALL) { + view->addToDirtyRegion(view->frameGeometry()); + } else { + if ((area & INVALIDATE_CONTENT_AREA) == INVALIDATE_CONTENT_AREA) { + IntRect content_area(view->x(), + view->y(), + view->visibleWidth(), + view->visibleHeight()); + view->addToDirtyRegion(content_area); + } + + if ((area & INVALIDATE_SCROLLBAR) == INVALIDATE_SCROLLBAR) { + // Invalidate the vertical scroll bar region for the view. + IntRect scroll_bar_vert(view->x() + view->visibleWidth(), + view->y(), + PlatformScrollbar::verticalScrollbarWidth(), + view->visibleHeight()); + view->addToDirtyRegion(scroll_bar_vert); + } + } +} + +void WebFrameImpl::InvalidateTickmark(RefPtr<WebCore::Range> tickmark) { + ASSERT(frame() && frame()->view()); + FrameView* view = frame()->view(); + + IntRect pos = tickmark->boundingBox(); + pos.move(-view->contentsX(), -view->contentsY()); + view->addToDirtyRegion(pos); +} + +void WebFrameImpl::IncreaseMatchCount(int count, int request_id) { + total_matchcount_ += count; + + // Update the UI with the latest findings. + WebViewDelegate* webview_delegate = GetView()->GetDelegate(); + DCHECK(webview_delegate); + if (webview_delegate) + webview_delegate->ReportFindInPageMatchCount(total_matchcount_, request_id, + frames_scoping_count_ == 0); +} + +void WebFrameImpl::ReportFindInPageSelection(const gfx::Rect& selection_rect, + int active_match_ordinal, + int request_id) { + // Update the UI with the latest selection rect. + WebViewDelegate* webview_delegate = GetView()->GetDelegate(); + DCHECK(webview_delegate); + if (webview_delegate) { + webview_delegate->ReportFindInPageSelection( + request_id, + OrdinalOfFirstMatchForFrame(this) + active_match_ordinal, + selection_rect); + } +} + +void WebFrameImpl::ResetMatchCount() { + total_matchcount_ = 0; + frames_scoping_count_ = 0; +} + +bool WebFrameImpl::Find(const FindInPageRequest& request, + bool wrap_within_frame, + gfx::Rect* selection_rect) { + WebCore::String webcore_string = + webkit_glue::StdWStringToString(request.search_string); + + // Starts the search from the current selection. + bool start_in_selection = true; // Policy. Can it be made configurable? + + // If the user has selected something since the last Find operation we want + // to start from there. Otherwise, we start searching from where the last Find + // operation left off (either a Find or a FindNext operation). + Selection selection(frame()->selectionController()->selection()); + if (selection.isNone() && last_active_range_) { + selection = Selection(last_active_range_.get()); + frame()->selectionController()->setSelection(selection); + } + + DCHECK(frame() && frame()->view()); + bool found = frame()->findString(webcore_string, request.forward, + request.match_case, wrap_within_frame, + start_in_selection); + if (found) { + // We found something, so we can now query the selection for its position. + Selection new_selection(frame()->selectionController()->selection()); + + // If we thought we found something, but it couldn't be selected (perhaps + // because it was marked -webkit-user-select: none), call it not-found so + // we don't crash. See http://b/1169294. This matches Safari's behavior, + // including some oddities when selectable and un-selectable text are mixed + // on a page: see http://b/1180007. + if (new_selection.isNone()) { + // Fall through to clean up selection and tickmarks. + found = false; + } else { + last_active_range_ = new_selection.toRange(); + active_selection_rect_ = new_selection.toRange()->boundingBox(); + ClearSelection(); // We'll draw our own highlight for the active item. + + if (selection_rect) { + gfx::Rect rect( + frame()->view()->convertToContainingWindow(active_selection_rect_)); + rect.Offset(-frameview()->scrollOffset().width(), + -frameview()->scrollOffset().height()); + *selection_rect = rect; + } + } + } + + if (!found) { + active_selection_rect_ = IntRect(); + last_active_range_ = NULL; + + if (!tickmarks_.isEmpty()) { + // Let the frame know that we found no matches. + tickmarks_.clear(); + // Erase all previous tickmarks and highlighting. + InvalidateArea(INVALIDATE_ALL); + } + } + + return found; +} + +bool WebFrameImpl::FindNext(const FindInPageRequest& request, + bool wrap_within_frame) { + if (tickmarks_.isEmpty()) + return false; + + // Save the old tickmark (if any). We will use this to invalidate the area + // of the tickmark that becomes unselected. + WebFrameImpl* const main_frame_impl = + static_cast<WebFrameImpl*>(GetView()->GetMainFrame()); + WebFrameImpl* const active_frame = main_frame_impl->active_tickmark_frame_; + RefPtr<WebCore::Range> old_tickmark = NULL; + if (active_frame && + (active_frame->active_tickmark_ != WidgetClientWin::kNoTickmark)) { + // When we get a reference to |old_tickmark| we can be in a state where + // the |active_tickmark_| points outside the tickmark vector, possibly + // during teardown of the frame. This doesn't reproduce normally, so if you + // hit this during debugging, update issue http://b/1277569 with + // reproduction steps - or contact the assignee. In release, we can ignore + // this and continue on (and let |old_tickmark| be null). + if (active_frame->active_tickmark_ >= active_frame->tickmarks_.size()) + NOTREACHED() << L"Active tickmark points outside the tickmark vector!"; + else + old_tickmark = active_frame->tickmarks_[active_frame->active_tickmark_]; + } + + // See if we have another match to select, and select it. + if (request.forward) { + const bool at_end = (active_tickmark_ == (tickmarks_.size() - 1)); + if ((active_tickmark_ == WidgetClientWin::kNoTickmark) || + (at_end && wrap_within_frame)) { + // Wrapping within a frame is only done for single frame pages. So when we + // reach the end we go back to the beginning (or back to the end if + // searching backwards). + active_tickmark_ = 0; + } else if (at_end) { + return false; + } else { + ++active_tickmark_; + DCHECK(active_tickmark_ < tickmarks_.size()); + } + } else { + const bool at_end = (active_tickmark_ == 0); + if ((active_tickmark_ == WidgetClientWin::kNoTickmark) || + (at_end && wrap_within_frame)) { + // Wrapping within a frame is not done for multi-frame pages, but if no + // tickmark is active we still need to set the index to the end so that + // we don't skip the frame during FindNext when searching backwards. + active_tickmark_ = tickmarks_.size() - 1; + } else if (at_end) { + return false; + } else { + --active_tickmark_; + DCHECK(active_tickmark_ < tickmarks_.size()); + } + } + + if (active_frame != this) { + // If we are jumping between frames, reset the active tickmark in the old + // frame and invalidate the area. + active_frame->active_tickmark_ = WidgetClientWin::kNoTickmark; + active_frame->InvalidateArea(INVALIDATE_CONTENT_AREA); + main_frame_impl->active_tickmark_frame_ = this; + } else { + // Invalidate the old tickmark. + if (old_tickmark) + active_frame->InvalidateTickmark(old_tickmark); + } + + Selection selection(tickmarks_[active_tickmark_].get()); + frame()->selectionController()->setSelection(selection); + frame()->revealSelection(); // Scroll the selection into view if necessary. + // Make sure we save where the selection was after the operation so that + // we can set the selection to it for the next Find operation (if needed). + last_active_range_ = tickmarks_[active_tickmark_]; + ClearSelection(); // We will draw our own highlighting. + + // Notify browser of new location for the selected rectangle. + IntRect pos = tickmarks_[active_tickmark_]->boundingBox(); + pos.move(-frameview()->scrollOffset().width(), + -frameview()->scrollOffset().height()); + ReportFindInPageSelection( + gfx::Rect(frame()->view()->convertToContainingWindow(pos)), + active_tickmark_ + 1, + request.request_id); + + return true; // Found a match. +} + +int WebFrameImpl::OrdinalOfFirstMatchForFrame(WebFrameImpl* frame) const { + int ordinal = 0; + WebFrameImpl* const main_frame_impl = + static_cast<WebFrameImpl*>(GetView()->GetMainFrame()); + // Iterate from the main frame up to (but not including) this frame and + // add up the number of tickmarks. + for (WebFrameImpl* frame = main_frame_impl; + frame != this; + frame = static_cast<WebFrameImpl*>( + webview_impl_->GetNextFrameAfter(frame, true))) { + ordinal += frame->tickmarks().size(); + } + + return ordinal; +} + +bool WebFrameImpl::ShouldScopeMatches(FindInPageRequest request) { + // Don't scope if we can't find a frame or if the frame is not visible. + // The user may have closed the tab/application, so abort. + if (!frame() || !Visible()) + return false; + + DCHECK(frame()->document() && frame()->view()); + + // If the frame completed the scoping operation and found 0 matches the last + // time it was searched, then we don't have to search it again if the user is + // just adding to the search string or sending the same search string again. + if (scoping_complete_ && + last_search_string_ != std::wstring(L"") && last_match_count_ == 0) { + // Check to see if the search string prefixes match. + std::wstring previous_search_prefix = + request.search_string.substr(0, last_search_string_.length()); + + if (previous_search_prefix == last_search_string_) { + return false; // Don't search this frame, it will be fruitless. + } + } + + return true; +} + +void WebFrameImpl::InvalidateIfNecessary() { + if (last_match_count_ > next_invalidate_after_) { + // TODO(finnur): (http://b/1088165) Optimize the drawing of the + // tickmarks and remove this. This calculation sets a milestone for when + // next to invalidate the scrollbar and the content area. We do this so that + // we don't spend too much time drawing the scrollbar over and over again. + // Basically, up until the first 500 matches there is no throttle. After the + // first 500 matches, we set set the milestone further and further out (750, + // 1125, 1688, 2K, 3K). + static const int start_slowing_down_after = 500; + static const int slowdown = 750; + int i = (last_match_count_ / start_slowing_down_after); + next_invalidate_after_ += i * slowdown; + + // Invalidating content area draws both highlighting and in-page + // tickmarks, but not the scrollbar. + // TODO(finnur): (http://b/1088165) invalidate content area only if + // match found on-screen. + InvalidateArea(INVALIDATE_CONTENT_AREA); + InvalidateArea(INVALIDATE_SCROLLBAR); + } +} + +// static +bool WebFrameImpl::RangeShouldBeHighlighted(Range* range) { + ExceptionCode exception = 0; + Node* common_ancestor_container = range->commonAncestorContainer(exception); + + if (exception) + return false; + + RenderObject* renderer = common_ancestor_container->renderer(); + + if (!renderer) + return false; + + IntRect overflow_clip_rect = renderer->absoluteClippedOverflowRect(); + return range->boundingBox().intersects(overflow_clip_rect); +} + +void WebFrameImpl::selectNodeFromInspector(WebCore::Node* node) { + inspected_node_ = node; +} + +void WebFrameImpl::ScopeStringMatches(FindInPageRequest request, + bool reset) { + if (!ShouldScopeMatches(request)) + return; + + WebFrameImpl* main_frame_impl = + static_cast<WebFrameImpl*>(GetView()->GetMainFrame()); + + if (reset) { + // This is a brand new search, so we need to reset everything. + // Scoping is just about to begin. + scoping_complete_ = false; + // First of all, all previous tickmarks need to be erased. + tickmarks_.clear(); + // Clear the counters from last operation. + last_match_count_ = 0; + next_invalidate_after_ = 0; + + main_frame_impl->frames_scoping_count_++; + + // Now, defer scoping until later to allow find operation to finish quickly. + MessageLoop::current()->PostTask(FROM_HERE, + scope_matches_factory_.NewRunnableMethod( + &WebFrameImpl::ScopeStringMatches, + request, + false)); // false=we just reset, so don't do it again. + return; + } + + WebCore::String webcore_string = + webkit_glue::StdWStringToString(request.search_string); + + RefPtr<Range> searchRange(rangeOfContents(frame()->document())); + + ExceptionCode ec = 0, ec2 = 0; + if (!reset && !tickmarks_.isEmpty()) { + // This is a continuation of a scoping operation that timed out and didn't + // complete last time around, so we should start from where we left off. + RefPtr<Range> start_range = tickmarks_.last(); + searchRange->setStart(start_range->startNode(), + start_range->startOffset(ec2) + 1, ec); + if (ec != 0 || ec2 != 0) { + NOTREACHED(); + return; + } + } + + // This timeout controls how long we scope (in ms) before releasing control. + // This value does not prevent us from running for longer than this, but it + // is periodically checked to see if we have exceeded our allocated time. + static const int kTimeout = 100; // ms + + int matchCount = 0; + bool timeout = false; + Time start_time = Time::Now(); + do { + // Find next occurrence of the search string. + // TODO(finnur): (http://b/1088245) This WebKit operation may run + // for longer than the timeout value, and is not interruptible as it is + // currently written. We may need to rewrite it with interruptibility in + // mind, or find an alternative. + RefPtr<Range> resultRange(findPlainText(searchRange.get(), + webcore_string, + true, + request.match_case)); + if (resultRange->collapsed(ec)) + break; // no further matches. + + // A non-collapsed result range can in some funky whitespace cases still not + // advance the range's start position (4509328). Break to avoid infinite + // loop. (This function is based on the implementation of Frame::FindString, + // which is where this safeguard comes from). + VisiblePosition newStart = + endVisiblePosition(resultRange.get(), WebCore::DOWNSTREAM); + if (newStart == + startVisiblePosition(searchRange.get(), WebCore::DOWNSTREAM)) + break; + + ++matchCount; + + // Add the location we just found to the tickmarks collection. + tickmarks_.append(resultRange); + + setStart(searchRange.get(), newStart); + + // If the Find function found a match it will have stored where the + // match was found in active_selection_rect_ on the current frame. If we + // find this rect during scoping it means we have found the active + // tickmark. + if (!active_selection_rect_.isEmpty() && + active_selection_rect_ == resultRange->boundingBox()) { + // We have found the active tickmark frame. + main_frame_impl->active_tickmark_frame_ = this; + // We also know which tickmark is active now. + active_tickmark_ = tickmarks_.size() - 1; + // To stop looking for the active tickmark, we clear this rectangle. + active_selection_rect_ = IntRect(); + + // Notify browser of new location for the selected rectangle. + IntRect pos = tickmarks_[active_tickmark_]->boundingBox(); + pos.move(-frameview()->scrollOffset().width(), + -frameview()->scrollOffset().height()); + ReportFindInPageSelection( + gfx::Rect(frame()->view()->convertToContainingWindow(pos)), + active_tickmark_ + 1, + request.request_id); + } + + timeout = (Time::Now() - start_time).InMilliseconds() >= kTimeout; + } while (!timeout); + + // Remember what we search for last time, so we can skip searching if more + // letters are added to the search string (and last outcome was 0). + last_search_string_ = request.search_string; + + if (matchCount > 0) { + last_match_count_ += matchCount; + + // Let the mainframe know how much we found during this pass. + main_frame_impl->IncreaseMatchCount(matchCount, request.request_id); + } + + if (timeout) { + // If we found anything during this pass, we should redraw. However, we + // don't want to spam too much if the page is extremely long, so if we + // reach a certain point we start throttling the redraw requests. + if (matchCount > 0) + InvalidateIfNecessary(); + + // Scoping effort ran out of time, lets ask for another time-slice. + MessageLoop::current()->PostTask(FROM_HERE, + scope_matches_factory_.NewRunnableMethod( + &WebFrameImpl::ScopeStringMatches, + request, + false)); // don't reset. + + return; // Done for now, resume work later. + } + + // This frame has no further scoping left, so it is done. Other frames might, + // of course, continue to scope matches. + scoping_complete_ = true; + main_frame_impl->frames_scoping_count_--; + + // If this is the last frame to finish scoping we need to trigger the final + // update to be sent. + if (main_frame_impl->frames_scoping_count_ == 0) + main_frame_impl->IncreaseMatchCount(0, request.request_id); + + // This frame is done, so show any tickmark/highlight we haven't drawn yet. + InvalidateArea(INVALIDATE_ALL); + + return; +} + +void WebFrameImpl::CancelPendingScopingEffort() { + scope_matches_factory_.RevokeAll(); + active_tickmark_ = WidgetClientWin::kNoTickmark; +} + +void WebFrameImpl::StopFinding() { + CancelPendingScopingEffort(); + + // Let the frame know that we don't want tickmarks or highlighting anymore. + tickmarks_.clear(); + InvalidateArea(INVALIDATE_ALL); +} + +void WebFrameImpl::SelectAll() { + frame()->selectionController()->selectAll(); + + WebViewDelegate* d = GetView()->GetDelegate(); + if (d) + d->UserMetricsRecordAction(L"SelectAll"); +} + +void WebFrameImpl::Copy() { + frame()->editor()->copy(); + + WebViewDelegate* d = GetView()->GetDelegate(); + if (d) + d->UserMetricsRecordAction(L"Copy"); +} + +void WebFrameImpl::Cut() { + frame()->editor()->cut(); + + WebViewDelegate* d = GetView()->GetDelegate(); + if (d) + d->UserMetricsRecordAction(L"Cut"); +} + +// Returns a copy of data from a data handle retrieved from the clipboard. The +// data is decoded according to the format that it is in. The caller is +// responsible for freeing the data. +static wchar_t* GetDataFromHandle(HGLOBAL data_handle, + unsigned int clipboard_format) { + switch (clipboard_format) { + case CF_TEXT: { + char* string_data = static_cast<char*>(::GlobalLock(data_handle)); + int n_chars = ::MultiByteToWideChar(CP_ACP, 0, string_data, -1, NULL, 0); + wchar_t* wcs_data = + static_cast<wchar_t*>(malloc((n_chars * sizeof(wchar_t)) + + sizeof(wchar_t))); + if (!wcs_data) { + ::GlobalUnlock(data_handle); + return NULL; + } + + ::MultiByteToWideChar(CP_ACP, 0, string_data, -1, wcs_data, n_chars); + ::GlobalUnlock(data_handle); + wcs_data[n_chars] = '\0'; + return wcs_data; + } + case CF_UNICODETEXT: { + wchar_t* string_data = static_cast<wchar_t*>(::GlobalLock(data_handle)); + size_t data_size_in_bytes = ::GlobalSize(data_handle); + wchar_t* wcs_data = + static_cast<wchar_t*>(malloc(data_size_in_bytes + sizeof(wchar_t))); + if (!wcs_data) { + ::GlobalUnlock(data_handle); + return NULL; + } + + size_t n_chars = static_cast<int>(data_size_in_bytes / sizeof(wchar_t)); + wmemcpy_s(wcs_data, n_chars, string_data, n_chars); + ::GlobalUnlock(data_handle); + wcs_data[n_chars] = '\0'; + return wcs_data; + } + } + return NULL; +} + +void WebFrameImpl::Paste() { + frame()->editor()->paste(); + + WebViewDelegate* d = GetView()->GetDelegate(); + if (d) + d->UserMetricsRecordAction(L"Paste"); +} + +void WebFrameImpl::Replace(const std::wstring& wtext) { + String text = webkit_glue::StdWStringToString(wtext); + frame()->editor()->replaceSelectionWithText(text, false, true); +} + +void WebFrameImpl::Delete() { + frame()->editor()->command("Delete").execute(); + + WebViewDelegate* d = GetView()->GetDelegate(); + if (d) + d->UserMetricsRecordAction(L"DeleteSelection"); +} + +void WebFrameImpl::Undo() { + frame()->editor()->undo(); + + WebViewDelegate* d = GetView()->GetDelegate(); + if (d) + d->UserMetricsRecordAction(L"Undo"); +} + +void WebFrameImpl::Redo() { + frame()->editor()->redo(); + + WebViewDelegate* d = GetView()->GetDelegate(); + if (d) + d->UserMetricsRecordAction(L"Redo"); +} + +void WebFrameImpl::ClearSelection() { + frame()->selectionController()->clear(); +} + +void WebFrameImpl::CreateFrameView() { + ASSERT(frame_); // If frame_ doesn't exist, we probably didn't init properly. + + WebCore::Page* page = frame_->page(); + DCHECK(page); + + DCHECK(page->mainFrame() != NULL); + + // Detach the current view. This ensures that UI widgets like plugins, + // etc are detached(hidden) + if (frame_->view()) + frame_->view()->detachFromWindow(); + + frame_->setView(0); + + WebCore::FrameView* view = new FrameView(frame_.get()); + + frame_->setView(view); + + // Attaching the view ensures that UI widgets like plugins, display/hide + // correctly. + frame_->view()->attachToWindow(); + + if (margin_width_ >= 0) + view->setMarginWidth(margin_width_); + if (margin_height_ >= 0) + view->setMarginHeight(margin_height_); + if (!allows_scrolling_) + view->setScrollbarsMode(WebCore::ScrollbarAlwaysOff); + + // TODO(darin): The Mac code has a comment about this possibly being + // unnecessary. See installInFrame in WebCoreFrameBridge.mm + if (frame_->ownerRenderer()) + frame_->ownerRenderer()->setWidget(view); + + view->initScrollbars(); + + // FrameViews are created with a refcount of 1 so it needs releasing after we + // assign it to a RefPtr. + view->deref(); + + WebFrameImpl* parent = static_cast<WebFrameImpl*>(GetParent()); + if (parent) { + parent->frameview()->addChild(view); + } else { + view->setClient(webview_impl_); + + IntRect geom(0, 0, webview_impl_->size().width(), + webview_impl_->size().height()); + view->setFrameGeometry(geom); + } +} + +// static +WebFrameImpl* WebFrameImpl::FromFrame(WebCore::Frame* frame) { + return static_cast<WebFrameLoaderClient*>( + frame->loader()->client())->webframe(); +} + +// WebFrame -------------------------------------------------------------------- + +void WebFrameImpl::Layout() { + // layout this frame + if (frame_->document()) + frame_->document()->updateLayout(); + // layout child frames + Frame* child = frame_->tree()->firstChild(); + for (; child; child = child->tree()->nextSibling()) + FromFrame(child)->Layout(); +} + +void WebFrameImpl::Paint(gfx::PlatformCanvas* canvas, const gfx::Rect& rect) { + static StatsRate rendering(L"WebFramePaintTime"); + StatsScope<StatsRate> rendering_scope(rendering); + + if (!rect.IsEmpty()) { + PlatformContextSkia context(canvas); + + // PlatformGraphicsContext is actually a pointer to PlatformContextSkia + GraphicsContext gc(reinterpret_cast<PlatformGraphicsContext*>(&context)); + IntRect dirty_rect(rect.x(), rect.y(), rect.width(), rect.height()); + + if (frame_->document() && frameview()) { + frameview()->paint(&gc, dirty_rect); + } else { + gc.fillRect(dirty_rect, Color::white); + } + } +} + +gfx::BitmapPlatformDevice WebFrameImpl::CaptureImage(bool scroll_to_zero) { + // Must layout before painting. + Layout(); + + gfx::PlatformCanvas canvas(frameview()->width(), frameview()->height(), true); + PlatformContextSkia context(&canvas); + + GraphicsContext gc(reinterpret_cast<PlatformGraphicsContext*>(&context)); + frameview()->paint(&gc, IntRect(0, 0, frameview()->width(), + frameview()->height())); + + gfx::BitmapPlatformDevice& device = + static_cast<gfx::BitmapPlatformDevice&>(canvas.getTopPlatformDevice()); + device.fixupAlphaBeforeCompositing(); + return device; +} + +bool WebFrameImpl::IsLoading() { + // I'm assuming this does what we want. + return frame_->loader()->isLoading(); +} + +void WebFrameImpl::Closing() { + // let go of our references, this breaks reference cycles and will + // usually eventually lead to us being destroyed. + if (frameview()) + frameview()->clear(); + if (frame_) { + StopLoading(); + frame_ = NULL; + } + alt_error_page_fetcher_.reset(); + webview_impl_ = NULL; +} + +void WebFrameImpl::DidReceiveData(DocumentLoader* loader, + const char* data, int length) { + // Set the text encoding. This calls begin() for us. It is safe to call + // this multiple times (Mac does: page/mac/WebCoreFrameBridge.mm). + bool user_chosen = true; + String encoding = frame_->loader()->documentLoader()->overrideEncoding(); + if (encoding.isNull()) { + user_chosen = false; + encoding = loader->response().textEncodingName(); + } + frame_->loader()->setEncoding(encoding, user_chosen); + + // NOTE: mac only does this if there is a document + frame_->loader()->addData(data, length); + + // It's possible that we get a DNS failure followed by a second load that + // succeeds before we hear back from the alternate error page server. In + // that case, cancel the alt error page download. + alt_error_page_fetcher_.reset(); +} + +void WebFrameImpl::DidFail(const ResourceError& error, bool was_provisional) { + // Make sure we never show errors in view source mode. + SetInViewSourceMode(false); + + WebViewDelegate* delegate = webview_impl_->delegate(); + if (delegate) { + WebErrorImpl web_error(error); + if (was_provisional) { + delegate->DidFailProvisionalLoadWithError(webview_impl_, web_error, + this); + } else { + delegate->DidFailLoadWithError(webview_impl_, web_error, this); + } + } +} + +void WebFrameImpl::LoadAlternateHTMLErrorPage(const WebRequest* request, + const WebError& error, + const GURL& error_page_url, + bool replace, + const GURL& fake_url) { + // Load alternate HTML in place of the previous request. We create a copy of + // the original request so we can replace its URL with a dummy URL. That + // prevents other web content from the same origin as the failed URL to + // script the error page. + scoped_ptr<WebRequest> failed_request(request->Clone()); + failed_request->SetURL(fake_url); + + LoadAlternateHTMLString(failed_request.get(), std::string(), + error.GetFailedURL(), replace); + + WebErrorImpl weberror_impl(error); + alt_error_page_fetcher_.reset( + new AltErrorPageResourceFetcher(webview_impl_, weberror_impl, this, + error_page_url)); +} + +std::wstring WebFrameImpl::GetName() { + return webkit_glue::StringToStdWString(frame_->tree()->name()); +} + +WebTextInput* WebFrameImpl::GetTextInput() { + if (!webtextinput_impl_.get()) { + webtextinput_impl_.reset(new WebTextInputImpl(this)); + } + return webtextinput_impl_.get(); +} + +void WebFrameImpl::SetPrinting(bool printing, + float page_width_min, + float page_width_max) { + frame_->setPrinting(printing, + page_width_min, + page_width_max, + true); +} + +bool WebFrameImpl::Visible() { + return frame()->view()->visibleWidth() > 0 && + frame()->view()->visibleHeight() > 0; +} + +void WebFrameImpl::CreateChildFrame(const FrameLoadRequest& r, + HTMLFrameOwnerElement* owner_element, + bool allows_scrolling, + int margin_height, + int margin_width, + Frame*& result) { + // TODO(darin): share code with initWithName() + + scoped_refptr<WebFrameImpl> webframe = new WebFrameImpl(); + + // Add an extra ref on behalf of the Frame/FrameLoader, which references the + // WebFrame via the FrameLoaderClient interface. See the comment at the top + // of this file for more info. + webframe->AddRef(); + + webframe->allows_scrolling_ = allows_scrolling; + webframe->margin_width_ = margin_width; + webframe->margin_height_ = margin_height; + + webframe->frame_ = + new Frame(frame_->page(), owner_element, &webframe->frame_loader_client_); + webframe->frame_->tree()->setName(r.frameName()); + + webframe->webview_impl_ = webview_impl_; // owning ref + + + // Note that Frames already start out with a refcount of 1. + // We wait until loader()->load() returns before deref-ing the Frame. + // Otherwise the danger is that the onload handler can cause + // the Frame to be dealloc-ed, and subsequently trash memory. + // (b:1055700) + WTF::RefPtr<Frame> protector(WTF::adoptRef(webframe->frame_.get())); + + frame_->tree()->appendChild(webframe->frame_); + + // Frame::init() can trigger onload event in the parent frame, + // which may detach this frame and trigger a null-pointer access + // in FrameTree::removeChild. Move init() after appendChild call + // so that webframe->frame_ is in the tree before triggering + // onload event handler. + // Because the event handler may set webframe->frame_ to null, + // it is necessary to check the value after calling init() and + // return without loading URL. + // (b:791612) + webframe->frame_->init(); // create an empty document + if (!webframe->frame_.get()) + return; + + // The following code was pulled from WebFrame.mm:_loadURL, with minor + // modifications. The purpose is to ensure we load the right HistoryItem for + // this child frame. + HistoryItem* parentItem = frame_->loader()->currentHistoryItem(); + FrameLoadType loadType = frame_->loader()->loadType(); + FrameLoadType childLoadType = WebCore::FrameLoadTypeRedirectWithLockedHistory; + KURL new_url = r.resourceRequest().url(); + + // If we're moving in the backforward list, we might want to replace the + // content of this child frame with whatever was there at that point. + // Reload will maintain the frame contents, LoadSame will not. + if (parentItem && parentItem->children().size() != 0 && + (isBackForwardLoadType(loadType) || + loadType == WebCore::FrameLoadTypeReload || + loadType == WebCore::FrameLoadTypeReloadAllowingStaleData)) { + HistoryItem* childItem = parentItem->childItemWithName(r.frameName()); + if (childItem) { + // Use the original URL to ensure we get all the side-effects, such as + // onLoad handlers, of any redirects that happened. An example of where + // this is needed is Radar 3213556. + new_url = KURL(KURL(""), + childItem->originalURLString().deprecatedString()); + + // These behaviors implied by these loadTypes should apply to the child + // frames + childLoadType = loadType; + + if (isBackForwardLoadType(loadType)) { + // For back/forward, remember this item so we can traverse any child + // items as child frames load. + webframe->frame_->loader()->setProvisionalHistoryItem(childItem); + } else { + // For reload, just reinstall the current item, since a new child frame + // was created but we won't be creating a new BF item + webframe->frame_->loader()->setCurrentHistoryItem(childItem); + } + } + } + + webframe->frame_->loader()->load(new_url, + r.resourceRequest().httpReferrer(), + childLoadType, + String(), NULL, NULL); + + // A synchronous navigation (about:blank) would have already processed + // onload, so it is possible for the frame to have already been destroyed by + // script in the page. + result = webframe->frame_.get(); +} + +bool WebFrameImpl::ExecuteCoreCommandByName(const std::string& name, + const std::string& value) { + ASSERT(frame()); + return frame()->editor()->command(webkit_glue::StdStringToString(name)) + .execute(webkit_glue::StdStringToString(value)); +} + +void WebFrameImpl::AddMessageToConsole(const std::wstring& msg, + ConsoleMessageLevel level) { + ASSERT(frame()); + + WebCore::MessageLevel webcore_message_level; + switch (level) { + case MESSAGE_LEVEL_TIP: + webcore_message_level = WebCore::TipMessageLevel; + break; + case MESSAGE_LEVEL_LOG: + webcore_message_level = WebCore::LogMessageLevel; + break; + case MESSAGE_LEVEL_WARNING: + webcore_message_level = WebCore::WarningMessageLevel; + break; + case MESSAGE_LEVEL_ERROR: + webcore_message_level = WebCore::ErrorMessageLevel; + break; + default: + NOTREACHED(); + return; + } + + frame()->page()->chrome()->addMessageToConsole( + WebCore::OtherMessageSource, webcore_message_level, + webkit_glue::StdWStringToString(msg), 1, String()); +} + +void WebFrameImpl::ClosePage() { + // TODO(creis): Find a way to use WebView::Close() instead. (See comments in + // webframe.h and RenderView::OnClosePage.) + frame_->loader()->closeURL(); +} + +gfx::Size WebFrameImpl::ScrollOffset() const { + WebCore::FrameView* view = frameview(); + if (view) { + WebCore::IntSize s = view->scrollOffset(); + return gfx::Size(s.width(), s.height()); + } + + return gfx::Size(); +} + +void WebFrameImpl::SetAllowsScrolling(bool flag) { + allows_scrolling_ = flag; + frame_->view()->setAllowsScrolling(flag); +} + +bool WebFrameImpl::SetPrintingMode(bool printing, + float page_width_min, + float page_width_max, + int* width) { + // Make sure main frame is loaded. + WebCore::FrameView* view = frameview(); + if (!view) { + NOTREACHED(); + return false; + } + printing_ = printing; + if (printing) { + view->setScrollbarsMode(WebCore::ScrollbarAlwaysOff); + } else { + view->setScrollbarsMode(WebCore::ScrollbarAuto); + } + DCHECK_EQ(frame()->isFrameSet(), false); + + SetPrinting(printing, page_width_min, page_width_max); + if (!printing) + pages_.clear(); + + // The document width is well hidden. + if (width) + *width = frame()->document()->renderer()->width(); + return true; +} + +int WebFrameImpl::ComputePageRects(const gfx::Size& page_size_px) { + if (!printing_ || + !frame() || + !frame()->document()) { + NOTREACHED(); + return 0; + } + // In Safari, they are using: + // (0,0) + soft margins top/left + // (phys width, phys height) - hard margins - + // soft margins top/left - soft margins right/bottom + // TODO(maruel): Weird. We don't do that. + // Everything is in pixels :( + // pages_ and page_height are actually output parameters. + int page_height; + WebCore::IntRect rect(0, 0, page_size_px.width(), page_size_px.height()); + computePageRectsForFrame(frame(), rect, 0, 0, 1.0, pages_, page_height); + return pages_.size(); +} + +void WebFrameImpl::GetPageRect(int page, gfx::Rect* page_size) const { + if (page < 0 || page >= static_cast<int>(pages_.size())) { + NOTREACHED(); + return; + } + *page_size = pages_[page]; +} + +bool WebFrameImpl::SpoolPage(int page, + PlatformContextSkia* context) { + // Ensure correct state. + if (!context || + !printing_ || + page < 0 || + page >= static_cast<int>(pages_.size())) { + NOTREACHED(); + return false; + } + + if (!frame() || !frame()->document()) { + NOTREACHED(); + return false; + } + + GraphicsContext spool(reinterpret_cast<PlatformGraphicsContext*>(context)); + DCHECK(pages_[page].x() == 0); + // Offset to get the right square. + spool.translate(0, -static_cast<float>(pages_[page].y())); + frame()->paint(&spool, pages_[page]); + return true; +} + +bool WebFrameImpl::HasUnloadListener() { + if (frame() && frame()->document()) { + Document* doc = frame()->document(); + return doc->hasUnloadEventListener(); + } + return false; +} diff --git a/webkit/glue/webframe_impl.h b/webkit/glue/webframe_impl.h new file mode 100644 index 0000000..2b974b7 --- /dev/null +++ b/webkit/glue/webframe_impl.h @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2006 Apple Computer, 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 WEBKIT_GLUE_WEBFRAME_IMPL_H__ +#define WEBKIT_GLUE_WEBFRAME_IMPL_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "webkit/glue/webdatasource_impl.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webframeloaderclient_impl.h" +#include "webkit/glue/webplugin_delegate.h" +#include "webkit/glue/webview_delegate.h" + +#pragma warning(push, 0) +#include "ResourceHandleClient.h" +#include "Frame.h" +#include "PlatformString.h" +#pragma warning(pop) + +class AltErrorPageResourceFetcher; +class WebErrorImpl; +class WebHistoryItemImpl; +class WebRequest; +class WebView; +class WebViewImpl; +class WebTextInput; +class WebTextInputImpl; + +namespace WebCore { +class Frame; +class FrameView; +class HistoryItem; +class Node; +class Range; +class SubstituteData; +struct WindowFeatures; +} + +namespace gfx { +class PlatformCanvas; +class BitmapPlatformDevice; +} + +// Implementation of WebFrame, note that this is a reference counted object. +class WebFrameImpl : public WebFrame { + public: + WebFrameImpl(); + ~WebFrameImpl(); + + static int live_object_count() { + return live_object_count_; + } + + // Called by the WebViewImpl to initialize its main frame: + void InitMainFrame(WebViewImpl* webview_impl); + + // WebFrame + virtual void LoadRequest(WebRequest* request); + virtual void LoadHTMLString(const std::string& html_text, + const GURL& base_url); + virtual void LoadAlternateHTMLString(const WebRequest* request, + const std::string& html_text, + const GURL& display_url, + bool replace); + virtual void LoadAlternateHTMLErrorPage(const WebRequest* request, + const WebError& error, + const GURL& error_page_url, + bool replace, + const GURL& fake_url); + virtual bool GetPreviousState(GURL* url, std::wstring* title, + std::string* history_state) const; + virtual bool GetCurrentState(GURL* url, std::wstring* title, + std::string* history_state) const; + virtual bool HasCurrentState() const; + virtual GURL GetURL() const; + virtual GURL GetFavIconURL() const; + virtual GURL GetOSDDURL() const; + virtual WebDataSource* GetDataSource() const; + virtual WebDataSource* GetProvisionalDataSource() const; + virtual void StopLoading(); + virtual WebFrame* GetOpener() const; + virtual WebFrame* GetParent() const; + virtual WebFrame* GetChildFrame(const std::wstring& xpath) const; + virtual WebView* GetView() const; + virtual gfx::BitmapPlatformDevice CaptureImage(bool scroll_to_zero); + + // This method calls createRuntimeObject (in KJS::Bindings::Instance), which + // increments the refcount of the NPObject passed in. + virtual void BindToWindowObject(const std::wstring& name, NPObject* object); + virtual void CallJSGC(); + + virtual void* GetFrameImplementation() { return frame(); } + + virtual void GetContentAsPlainText(int max_chars, std::wstring* text) const; + virtual bool Find(const FindInPageRequest& request, + bool wrap_within_frame, + gfx::Rect* selection_rect); + virtual bool FindNext(const FindInPageRequest& request, + bool wrap_within_frame); + virtual void StopFinding(); + virtual void ScopeStringMatches(FindInPageRequest request, bool reset); + virtual void CancelPendingScopingEffort(); + virtual void ResetMatchCount(); + virtual bool Visible(); + virtual void SelectAll(); + virtual void Copy(); + virtual void Cut(); + virtual void Paste(); + virtual void Replace(const std::wstring& text); + virtual void Delete(); + virtual void Undo(); + virtual void Redo(); + virtual void ClearSelection(); + + virtual void SetInViewSourceMode(bool); + + virtual bool GetInViewSourceMode() const; + + virtual void DidReceiveData(WebCore::DocumentLoader* loader, + const char* data, + int length); + virtual void DidFail(const WebCore::ResourceError&, bool was_provisional); + + virtual std::wstring GetName(); + + virtual WebTextInput* GetTextInput(); + + virtual bool ExecuteCoreCommandByName(const std::string& name, const std::string& value); + + virtual void AddMessageToConsole(const std::wstring& msg, + ConsoleMessageLevel level); + + virtual void ClosePage(); + + virtual gfx::Size ScrollOffset() const; + + virtual bool SetPrintingMode(bool printing, + float page_width_min, + float page_width_max, + int* width); + virtual int ComputePageRects(const gfx::Size& page_size_px); + virtual void GetPageRect(int page, gfx::Rect* page_size) const; + virtual bool SpoolPage(int page, + PlatformContextSkia* context); + + // Reformats this frame for printing or for screen display, depending on + // |printing| flag. Acts recursively on inner frames. + // Note: It fails if the main frame failed to load. It will succeed even if a + // child frame failed to load. + void SetPrinting(bool printing, float page_width_min, float page_width_max); + + void CreateChildFrame(const WebCore::FrameLoadRequest&, + WebCore::HTMLFrameOwnerElement* owner_element, + bool allows_scrolling, int margin_width, + int margin_height, WebCore::Frame*& new_frame); + + // WebFrameImpl + void Layout(); + void Paint(gfx::PlatformCanvas* canvas, const gfx::Rect& rect); + + bool IsLoading(); + + void CreateFrameView(); + + // The plugin delegate is used to get notifications when downloads complete. + // This is used by the NPAPI method getURLNotify. plugin_delegate() may + // return NULL. TODO(darin): how come there is only one per frame?!? + WebPluginDelegate* plugin_delegate() const { + return plugin_delegate_; + } + void set_plugin_delegate(WebPluginDelegate* plugin_delegate) { + plugin_delegate_ = plugin_delegate; + } + + WebCore::Frame* frame() { + return frame_.get(); + } + + static WebFrameImpl* FromFrame(WebCore::Frame* frame); + + WebViewImpl* webview_impl() const { + return webview_impl_; + } + + WebCore::FrameView* frameview() const { + return frame_ ? frame_->view() : NULL; + } + + // Update the given datasource with currently_loading_request's info. + // If currently_loading_request is NULL, does nothing. + void CacheCurrentRequestInfo(WebDataSourceImpl* datasource); + + void set_currently_loading_history_item(WebHistoryItemImpl* item); + + // Getters for the impls corresponding to Get(Provisional)DataSource. They + // may return NULL if there is no corresponding data source. + WebDataSourceImpl* GetDataSourceImpl() const; + WebDataSourceImpl* GetProvisionalDataSourceImpl() const; + + // Gets the tickmarks for drawing on the scrollbars of a particular frame. + const Vector<RefPtr<WebCore::Range> >& tickmarks() const { + return tickmarks_; + } + + // Returns whether a range representing a tickmark should be highlighted. + // We use this to avoid highlighting ranges that are currently hidden. + static bool RangeShouldBeHighlighted(WebCore::Range* range); + + const WebCore::Node* inspected_node() const { + return inspected_node_; + } + + void WebFrameImpl::selectNodeFromInspector(WebCore::Node* node); + + // Returns which frame has an active tickmark. This function should only be + // called on the main frame, as it is the only frame keeping track. Returned + // value can be NULL if no frame has an active tickmark. + const WebFrameImpl* active_tickmark_frame() const { + return active_tickmark_frame_; + } + + // Returns the index of the active tickmark for this frame. + size_t active_tickmark_index() const { + return active_tickmark_; + } + + // Sets whether the WebFrameImpl allows its document to be scrolled. + // If the parameter is true, allow the document to be scrolled. + // Otherwise, disallow scrolling + void SetAllowsScrolling(bool flag); + + // Returns true if the frame CSS is in "printing" mode. + bool printing() const { return printing_; } + + virtual bool HasUnloadListener(); + + protected: + friend class WebFrameLoaderClient; + + // Informs the WebFrame that the Frame is being closed, called by the + // WebFrameLoaderClient + void Closing(); + + // A helper function for loading some document, given all of its data, into + // this frame. The charset may be empty if unknown, but a mime type must be + // specified. TODO(darin): Add option for storing this in session history. + void LoadDocumentData(const WebCore::KURL& base_url, + const WebCore::String& data, + const WebCore::String& mime_type, + const WebCore::String& charset); + + // See WebFrame.h for details. + virtual void IncreaseMatchCount(int count, int request_id); + virtual void ReportFindInPageSelection(const gfx::Rect& selection_rect, + int active_match_ordinal, + int request_id); + + // Resource fetcher for downloading an alternate DNS error page. + scoped_ptr<AltErrorPageResourceFetcher> alt_error_page_fetcher_; + + // Used to check for leaks of this object. + static int live_object_count_; + + WebFrameLoaderClient frame_loader_client_; + + // Holding a reference back to the WebViewImpl is necessary to ensure that + // its HWND is not destroyed before all of the WebCore::Widgets, which refer + // to the WebViewImpl's HWND as their containingWindow. However, this ref + // creates a cycle between the WebViewImpl and the top-most WebFrameImpl. We + // break this cycle in our Closing method. + scoped_refptr<WebViewImpl> webview_impl_; + + // The WebCore frame associated with this frame. MAY BE NULL if the frame + // has been detached from the DOM. + WTF::RefPtr<WebCore::Frame> frame_; + + // This holds the request passed to LoadRequest, for access by the + // WebFrameLoaderClient. Unfortunately we have no other way to pass this + // information to him. Only non-NULL during a call to LoadRequest. + const WebRequest* currently_loading_request_; + + // Similar to currently_loading_request_, except this will be set when + // WebCore initiates a history navigation (probably via javascript). + scoped_refptr<WebHistoryItemImpl> currently_loading_history_item_; + + // Plugins sometimes need to be notified when loads are complete so we keep + // a pointer back to the appropriate plugin. + WebPluginDelegate* plugin_delegate_; + + // Frame construction parameters + bool allows_scrolling_; + int margin_width_; + int margin_height_; + + // Handling requests from TextInputController on this frame. + scoped_ptr<WebTextInputImpl> webtextinput_impl_; + + // This vector maintains a list of Ranges representing locations for search + // string matches that were found in the frame during a FindInPage operation. + Vector<RefPtr<WebCore::Range> > tickmarks_; + + // The node selected in the web inspector. Used for highlighting it on the page. + WebCore::Node* inspected_node_; + + // The index of the active tickmark for the current frame. + size_t active_tickmark_; + + // This rectangle is used during the scoping effort to figure out what rect + // got selected during the Find operation. In other words, first the Find + // operation iterates to the next match and then scoping will happen for all + // matches. When we encounter this rectangle during scoping we mark that + // tickmark as active (see active_tickmark_). This avoids having to iterate + // through a potentially very large tickmark vector to see which hit is + // active. Once we find the active rectangle we clear this rectangle to + // indicate that we are done determining what the active match is. + WebCore::IntRect active_selection_rect_; + + // This range represents the range that got selected during the Find or + // FindNext operation. We will set this range as the selection (unless the + // user selects something between Find/FindNext operations) so that we can + // continue from where we left off. + RefPtr<WebCore::Range> last_active_range_; + + // Keeps track of the last string this frame searched for. This is used for + // short-circuiting searches in the following scenarios: When a frame has + // been searched and returned 0 results, we don't need to search that frame + // again if the user is just adding to the search (making it more specific). + std::wstring last_search_string_; + + // Keeps track of how many matches this frame has found so far, so that we + // don't loose count between scoping efforts, and is also used (in conjunction + // with last_search_string_ and scoping_complete_) to figure out if we need to + // search the frame again. + int last_match_count_; + + // This variable keeps a cumulative total of matches found so far for ALL the + // frames on the page, and is only incremented by calling IncreaseMatchCount + // (on the main frame only). It should be -1 for all other frames. + int total_matchcount_; + + // A way for the main frame to keep track of which frame has an active + // tickmark. Should be NULL for all other frames. + WebFrameImpl* active_tickmark_frame_; + + // This variable keeps a cumulative total of how many frames are currently + // scoping, and is incremented/decremented on the main frame only. + // It should be -1 for all other frames. + int frames_scoping_count_; + + // Keeps track of whether the scoping effort was completed (the user may + // interrupt it before it completes by submitting a new search). + bool scoping_complete_; + + // Keeps track of when the scoping effort should next invalidate the scrollbar + // and the frame area. + int next_invalidate_after_; + + // This is a factory for creating cancelable tasks for this frame that run + // asynchronously in order to scope string matches during a find operation. + ScopedRunnableMethodFactory<WebFrameImpl> scope_matches_factory_; + + private: + // A bit mask specifying area of the frame to invalidate. + typedef enum AreaToInvalidate { + INVALIDATE_NOTHING = 0, + INVALIDATE_CONTENT_AREA = 1, + INVALIDATE_SCROLLBAR = 2, // vertical scrollbar only. + INVALIDATE_ALL = 3 // both content area and the scrollbar. + }; + + // Invalidates a certain area within the frame. + void InvalidateArea(AreaToInvalidate area); + + // Invalidates the tickmark area represented by the range passed in. + void InvalidateTickmark(RefPtr<WebCore::Range> tickmark); + + // Returns the ordinal of the first match in the frame specified. This + // function enumerates the frames, starting with the main frame and up to (but + // not including) the frame passed in as a parameter and counts how many + // tickmarks there are. + int OrdinalOfFirstMatchForFrame(WebFrameImpl* frame) const; + + // Determines whether the scoping effort is required for a particular frame. + // It is not necessary if the frame is invisible, for example, or if this + // is a repeat search that already returned nothing last time the same prefix + // was searched. + bool ShouldScopeMatches(FindInPageRequest request); + + // Determines whether to invalidate the content area and scrollbar. + void InvalidateIfNecessary(); + + void InternalLoadRequest(const WebRequest* request, + const WebCore::SubstituteData& data, + bool replace); + + // In "printing" mode. Used as a state check. + bool printing_; + + // For each printed page, the view of the document in pixels. + Vector<WebCore::IntRect> pages_; + + DISALLOW_EVIL_CONSTRUCTORS(WebFrameImpl); +}; + +#endif // WEBKIT_GLUE_WEBFRAME_IMPL_H__ diff --git a/webkit/glue/webframeloaderclient_impl.cc b/webkit/glue/webframeloaderclient_impl.cc new file mode 100644 index 0000000..b1056d8 --- /dev/null +++ b/webkit/glue/webframeloaderclient_impl.cc @@ -0,0 +1,1502 @@ +// Copyright 2008, 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 <string> +#include <vector> + +#pragma warning(push, 0) +#include "Chrome.h" +#include "CString.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Element.h" +#include "HistoryItem.h" +#include "HTMLFormElement.h" // needed by FormState.h +#include "FormState.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameView.h" +#include "MIMETypeRegistry.h" +#include "MouseEvent.h" +#include "Page.h" +#include "PlatformString.h" +#include "PluginInfoStore.h" +#include "RefPtr.h" +#include "WindowFeatures.h" +#pragma warning(pop) + +#undef LOG +#include "base/command_line.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "net/base/mime_util.h" +#include "net/base/net_errors.h" +#include "webkit/activex_shim/activex_shared.h" +#include "webkit/glue/webframeloaderclient_impl.h" +#include "webkit/glue/alt_404_page_resource_fetcher.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/password_form_dom_manager.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/searchable_form_data.h" +#include "webkit/glue/webdatasource_impl.h" +#include "webkit/glue/webdocumentloader_impl.h" +#include "webkit/glue/weberror_impl.h" +#include "webkit/glue/webhistoryitem_impl.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webplugin_impl.h" +#include "webkit/glue/webresponse_impl.h" +#include "webkit/glue/webview_delegate.h" +#include "webkit/glue/webview_impl.h" +#include "webkit/glue/weburlrequest.h" + +using namespace WebCore; + +// Domain for internal error codes. +static const char kInternalErrorDomain[] = "webkit_glue"; + +// An internal error code. Used to note a policy change error resulting from +// dispatchDecidePolicyForMIMEType not passing the PolicyUse option. +enum { + ERR_POLICY_CHANGE = -10000, +}; + +WebFrameLoaderClient::WebFrameLoaderClient(WebFrameImpl* frame) : + webframe_(frame), + postpone_loading_data_(false), + has_representation_(false), + plugin_widget_(NULL), + sent_initial_response_to_plugin_(false), + next_window_open_disposition_(IGNORE_ACTION) { +} + +WebFrameLoaderClient::~WebFrameLoaderClient() { +} + +void WebFrameLoaderClient::frameLoaderDestroyed() { + // When the WebFrame was created, it had an extra ref() given to it on behalf + // of the FrameWin, which accesses it via the FrameWinClient interface. + // Since the WebFrame owns us, this extra ref also serves to keep us alive + // until the FrameLoader is done with us. The FrameLoader calls this method + // when it's going away. Therefore, we balance out that extra ref. + // + // May delete 'this' + webframe_->Release(); +} + +void WebFrameLoaderClient::windowObjectCleared() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->WindowObjectCleared(webframe_); +} + +void WebFrameLoaderClient::didPerformFirstNavigation() const { +} + +void WebFrameLoaderClient::registerForIconNotification(bool listen){ +} + +void WebFrameLoaderClient::unloadListenerChanged() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->OnUnloadListenerChanged(webview, webframe_); +} + +bool WebFrameLoaderClient::hasWebView() const { + return webframe_->webview_impl() != NULL; +} + +bool WebFrameLoaderClient::hasFrameView() const { + // The Mac port has this notion of a WebFrameView, which seems to be + // some wrapper around an NSView. Since our equivalent is HWND, I guess + // we have a "frameview" whenever we have the toplevel HWND. + return webframe_->webview_impl() != NULL; +} + +bool WebFrameLoaderClient::privateBrowsingEnabled() const { + // FIXME + return false; +} + +void WebFrameLoaderClient::makeDocumentView() { + webframe_->CreateFrameView(); +} + +void WebFrameLoaderClient::makeRepresentation(DocumentLoader*) { + has_representation_ = true; +} + +void WebFrameLoaderClient::setDocumentViewFromCachedPage(CachedPage*) { + // FIXME +} +void WebFrameLoaderClient::forceLayout() { + // FIXME +} +void WebFrameLoaderClient::forceLayoutForNonHTML() { + // FIXME +} + +void WebFrameLoaderClient::setCopiesOnScroll() { + // FIXME +} + +void WebFrameLoaderClient::detachedFromParent2() { + // FIXME +} +void WebFrameLoaderClient::detachedFromParent3() { + // FIXME +} + +void WebFrameLoaderClient::detachedFromParent4() { + // Called during the last part of frame detaching, to indicate that we should + // destroy various objects (including the FrameWin). + webframe_->Closing(); +} + +void WebFrameLoaderClient::loadedFromCachedPage() { + // FIXME +} + +// This function is responsible for associating the |identifier| with a given +// subresource load. The following functions that accept an |identifier| are +// called for each subresource, so they should not be dispatched to the +// WebFrame. +void WebFrameLoaderClient::assignIdentifierToInitialRequest( + unsigned long identifier, DocumentLoader* loader, + const ResourceRequest& request) { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + WebRequestImpl webreq(request); + d->AssignIdentifierToRequest(webview, identifier, webreq); + } +} + +// Determines whether the request being loaded by |loader| is a frame +// or a subresource. A subresource in this context is anything other +// than a frame -- this includes images and xmlhttp requests. +// It is important to note that a subresource is NOT limited to stuff +// loaded through the frame's subresource loader. Synchronous xmlhttp +// requests for example, do not go through the subresource loader, +// but we still label them as SUB_RESOURCE. +// +// The important edge cases to consider when modifying this function are +// how synchronous resource loads are treated during load/unload threshold. +static ResourceType::Type DetermineResourceTypeFromLoader(DocumentLoader* loader) { + if (loader == loader->frameLoader()->provisionalDocumentLoader()) { + if (loader->frameLoader()->isLoadingMainFrame()) { + return ResourceType::MAIN_FRAME; + } else { + return ResourceType::SUB_FRAME; + } + } + return ResourceType::SUB_RESOURCE; +} + +void WebFrameLoaderClient::dispatchWillSendRequest( + DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, + const ResourceResponse& redirectResponse) { + // We set the Frame on the ResourceRequest to provide load context to the + // ResourceHandle implementation. + request.setFrame(webframe_->frame()); + + // We want to distinguish between a request for a document to be loaded into + // the main frame, a sub-frame, or the sub-objects in that document. + request.setResourceType(DetermineResourceTypeFromLoader(loader)); + + // FrameLoader::loadEmptyDocumentSynchronously() creates an empty document + // with no URL. We don't like that, so we'll rename it to about:blank. + if (request.url().isEmpty()) + request.setURL(KURL("about:blank")); + if (request.mainDocumentURL().isEmpty()) + request.setMainDocumentURL(KURL("about:blank")); + + // Give the delegate a crack at the request. + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + WebRequestImpl webreq(request); + d->WillSendRequest(webview, identifier, &webreq); + request = webreq.frame_load_request().resourceRequest(); + } +} + +void WebFrameLoaderClient::dispatchDidReceiveAuthenticationChallenge( + DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { + // FIXME +} + +void WebFrameLoaderClient::dispatchDidCancelAuthenticationChallenge( + DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { + // FIXME +} + +void WebFrameLoaderClient::dispatchDidReceiveResponse(DocumentLoader* loader, + unsigned long identifier, + const ResourceResponse& response) { + + // True if the request was for the page's main frame, or a subframe. + bool is_frame = ResourceType::IsFrame(DetermineResourceTypeFromLoader(loader)); + + /* TODO(evanm): reenable this once we properly sniff XHTML from text/xml documents. + if (is_frame && + response.httpStatusCode() == 200 && + mime_util::IsViewSourceMimeType( + webkit_glue::CStringToStdString(response.mimeType().latin1()).c_str())) { + loader->frame()->setInViewSourceMode(); + }*/ + + // When the frame request first 404's, chrome may replace it with the alternate + // 404 page's contents. It does this using substitute data in the document + // loader, so the original response and url of the request can be preserved. + // We need to avoid replacing the current page, if it has already been + // replaced (otherwise could loop on setting alt-404 page!) + bool is_substitute_data = loader->substituteData().isValid(); + + // If it's a 404 page, we wait until we get 512 bytes of data before trying + // to load the document. This allows us to put up an alternate 404 page if + // there's short text. + postpone_loading_data_ = is_frame && + !is_substitute_data && + response.httpStatusCode() == 404 && + GetAlt404PageUrl(loader).is_valid(); + if (postpone_loading_data_) + postponed_data_.clear(); + + // Cancel any pending loads. + alt_404_page_fetcher_.reset(NULL); +} + +void WebFrameLoaderClient::dispatchDidReceiveContentLength(DocumentLoader* loader, + unsigned long identifier, + int lengthReceived) { + // FIXME +} + +// Called when a particular resource load completes +void WebFrameLoaderClient::dispatchDidFinishLoading(DocumentLoader* loader, + unsigned long identifier) { + if (postpone_loading_data_) { + // The server returned a 404 and the content was < 512 bytes (which we + // suppressed). Go ahead and fetch the alternate page content. + const GURL& url = GetAlt404PageUrl(loader); + DCHECK(url.is_valid()) << + "URL changed? It was valid in dispatchDidReceiveResponse."; + alt_404_page_fetcher_.reset(new Alt404PageResourceFetcher(this, + webframe_->frame(), loader, url)); + } + + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->DidFinishLoading(webview, identifier); +} + +GURL WebFrameLoaderClient::GetAlt404PageUrl(DocumentLoader* loader) { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (!d) + return GURL(); + + const GURL& failedURL = webkit_glue::KURLToGURL(loader->url()); + + // If trying to view source on a 404 page, just show the original page + // content. + if (webframe_->frame()->inViewSourceMode()) + return GURL(); + + // Construct the URL to fetch from the alt error page server. "html404" + // is understood by the link doctor server. + return d->GetAlternateErrorPageURL(failedURL, WebViewDelegate::HTTP_404); +} + +void WebFrameLoaderClient::Alt404PageFinished(DocumentLoader* loader, + const std::string& html) { + if (html.length() > 0) { + // TODO(tc): Handle backoff on so we don't hammer the alt error page + // servers. + webframe_->LoadHTMLString(html, webkit_glue::KURLToGURL(loader->url())); + } else { + // Fall back on original text + webframe_->LoadHTMLString(postponed_data_, + webkit_glue::KURLToGURL(loader->url())); + } +} + +void WebFrameLoaderClient::dispatchDidFailLoading(DocumentLoader* loader, + unsigned long identifier, + const ResourceError& error) { + WebViewImpl* webview = webframe_->webview_impl(); + if (webview && webview->delegate()) { + webview->delegate()->DidFailLoadingWithError(webview, identifier, + WebErrorImpl(error)); + } +} + +void WebFrameLoaderClient::dispatchDidFinishDocumentLoad() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + // The document has now been fully loaded. + // Scan for password forms to be sent to the browser + PassRefPtr<WebCore::HTMLCollection> forms = + webframe_->frame()->document()->forms(); + + std::vector<PasswordForm> actions; + unsigned int form_count = forms->length(); + for (unsigned int i = 0; i < form_count; ++i) { + // Strange but true, sometimes item can be NULL. + WebCore::Node* item = forms->item(i); + if (item) { + WebCore::HTMLFormElement* form = + static_cast<WebCore::HTMLFormElement*>(item); + + // Honour autocomplete=off. + if (!form->autoComplete()) + continue; + + scoped_ptr<PasswordForm> data( + PasswordFormDomManager::CreatePasswordForm(form)); + if (data.get()) + actions.push_back(*data); + } + } + if (d && (actions.size() > 0)) + d->OnPasswordFormsSeen(webview, actions); + if (d) + d->DidFinishDocumentLoadForFrame(webview, webframe_); +} + +bool WebFrameLoaderClient::dispatchDidLoadResourceFromMemoryCache( + DocumentLoader* loader, + const ResourceRequest& request, + const ResourceResponse& response, + int length) { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + WebRequestImpl webreq(request); + WebResponseImpl webresp(response); + return d->DidLoadResourceFromMemoryCache(webview, webreq, webresp, + webframe_); + } + + return false; +} + +void WebFrameLoaderClient::dispatchDidHandleOnloadEvents() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + + if (d) + d->DidHandleOnloadEventsForFrame(webview, webframe_); +} + +// Redirect Tracking +// ================= +// We want to keep track of the chain of redirects that occur during page +// loading. There are two types of redirects, server redirects which are HTTP +// response codes, and client redirects which are document.location= and meta +// refreshes. +// +// This outlines the callbacks that we get in different redirect situations, +// and how each call modifies the redirect chain. +// +// Normal page load +// ---------------- +// dispatchDidStartProvisionalLoad() -> adds URL to the redirect list +// dispatchDidCommitLoad() -> DISPATCHES & clears list +// +// Server redirect (success) +// ------------------------- +// dispatchDidStartProvisionalLoad() -> adds source URL +// dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL +// dispatchDidCommitLoad() -> DISPATCHES +// +// Client redirect (success) +// ------------------------- +// (on page) +// dispatchWillPerformClientRedirect() -> saves expected redirect +// dispatchDidStartProvisionalLoad() -> appends redirect source (since +// it matches the expected redirect) +// and the current page as the dest) +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidCommitLoad() -> DISPATCHES +// +// Client redirect (cancelled) +// (e.g meta-refresh trumped by manual doc.location change, or just cancelled +// because a link was clicked that requires the meta refresh to be rescheduled +// (the SOURCE URL may have changed). +// --------------------------- +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidStartProvisionalLoad() -> adds only URL to redirect list +// dispatchDidCommitLoad() -> DISPATCHES & clears list +// rescheduled ? dispatchWillPerformClientRedirect() -> saves expected redirect +// : nothing + +// Client redirect (failure) +// ------------------------- +// (on page) +// dispatchWillPerformClientRedirect() -> saves expected redirect +// dispatchDidStartProvisionalLoad() -> appends redirect source (since +// it matches the expected redirect) +// and the current page as the dest) +// dispatchDidCancelClientRedirect() +// dispatchDidFailProvisionalLoad() +// +// Load 1 -> Server redirect to 2 -> client redirect to 3 -> server redirect to 4 +// ------------------------------------------------------------------------------ +// dispatchDidStartProvisionalLoad() -> adds source URL 1 +// dispatchDidReceiveServerRedirectForProvisionalLoad() -> adds dest URL 2 +// dispatchDidCommitLoad() -> DISPATCHES 1+2 +// -- begin client redirect and NEW DATA SOURCE +// dispatchWillPerformClientRedirect() -> saves expected redirect +// dispatchDidStartProvisionalLoad() -> appends URL 2 and URL 3 +// dispatchDidReceiveServerRedirectForProvisionalLoad() -> appends destination URL 4 +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidCommitLoad() -> DISPATCHES +// +// Interesting case with multiple location changes involving anchors. +// Load page 1 containing future client-redirect (back to 1, e.g meta refresh) > Click +// on a link back to the same page (i.e an anchor href) > +// client-redirect finally fires (with new source, set to 1#anchor) +// ----------------------------------------------------------------------------- +// dispatchWillPerformClientRedirect(non-zero 'interval' param) -> saves expected redirect +// -- click on anchor href +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidStartProvisionalLoad() -> adds 1#anchor source +// dispatchDidCommitLoad() -> DISPATCHES 1#anchor +// dispatchWillPerformClientRedirect() -> saves exp. source (1#anchor) +// -- redirect timer fires +// dispatchDidStartProvisionalLoad() -> appends 1#anchor (src) and 1 (dest) +// dispatchDidCancelClientRedirect() -> clears expected redirect +// dispatchDidCommitLoad() -> DISPATCHES 1#anchor + 1 +// +void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad() { + WebDataSourceImpl* ds = webframe_->GetProvisionalDataSourceImpl(); + if (!ds) { + NOTREACHED() << "Got a server redirect when there is no provisional DS"; + return; + } + + // A provisional load should have started already, which should have put an + // entry in our redirect chain. + DCHECK(!ds->GetRedirectChain().empty()); + + // The URL of the destination is on the provisional data source. We also need + // to update the redirect chain to account for this addition (we do this + // before the callback so the callback can look at the redirect chain to see + // what happened). + ds->AppendRedirect(ds->GetRequest().GetURL()); + + // Dispatch callback + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->DidReceiveProvisionalLoadServerRedirect(webview, webframe_); +} + +// Called on both success and failure of a client redirect. +void WebFrameLoaderClient::dispatchDidCancelClientRedirect() { + // No longer expecting a client redirect. + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview ? webview->delegate() : NULL; + if (d) { + expected_client_redirect_src_ = GURL(); + expected_client_redirect_dest_ = GURL(); + + d->DidCancelClientRedirect(webview, webframe_); + } + + // No need to clear the redirect chain, since that data source has already + // been deleted by the time this function is called. +} + +void WebFrameLoaderClient::dispatchWillPerformClientRedirect(const KURL& url, + double interval, + double fire_date) { + // Tells dispatchDidStartProvisionalLoad that if it sees this item it is a + // redirect and the source item should be added as the start of the chain. + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview ? webview->delegate() : NULL; + if (d) { + expected_client_redirect_src_ = webframe_->GetURL(); + expected_client_redirect_dest_ = webkit_glue::KURLToGURL(url); + + // TODO(timsteele): bug 1135512. Webkit does not properly notify us of + // cancelling http > file client redirects. Since the FrameLoader's policy + // is to never carry out such a navigation anyway, the best thing we can do + // for now to not get confused is ignore this notification. + if (expected_client_redirect_dest_.SchemeIsFile() && + (expected_client_redirect_src_.SchemeIs("http") || + expected_client_redirect_src_.SchemeIsSecure())) { + expected_client_redirect_src_ = GURL(); + expected_client_redirect_dest_ = GURL(); + return; + } + + d->WillPerformClientRedirect(webview, + webframe_, + expected_client_redirect_src_, + expected_client_redirect_dest_, + static_cast<unsigned int>(interval), + static_cast<unsigned int>(fire_date)); + } +} + +void WebFrameLoaderClient::dispatchDidChangeLocationWithinPage() { + // Anchor fragment navigations are not normal loads, so we need to synthesize + // some events for our delegate. + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->DidStartLoading(webview); + + WebDataSourceImpl* ds = webframe_->GetDataSourceImpl(); + DCHECK(ds) << "DataSource NULL when navigating to reference fragment"; + if (ds) { + GURL url = ds->GetRequest().GetURL(); + GURL chain_end = ds->GetRedirectChain().back(); + ds->ClearRedirectChain(); + + // Figure out if this location change is because of a JS-initiated client + // redirect (e.g onload/setTimeout document.location.href=). + // TODO(timsteele): (bugs 1085325, 1046841) We don't get proper redirect + // performed/cancelled notifications across anchor navigations, so the + // other redirect-tracking code in this class (see dispatch*ClientRedirect() + // and dispatchDidStartProvisionalLoad) is insufficient to catch and + // properly flag these transitions. Once a proper fix for this bug is + // identified and applied the following block may no longer be required. + bool was_client_redirect = + ((url == expected_client_redirect_dest_) && + (chain_end == expected_client_redirect_src_)) || + (NavigationGestureForLastLoad() == NavigationGestureAuto); + + if (was_client_redirect) { + if (d) + d->DidCompleteClientRedirect(webview, webframe_, chain_end); + ds->AppendRedirect(chain_end); + // Make sure we clear the expected redirect since we just effectively + // completed it. + expected_client_redirect_src_ = GURL(); + expected_client_redirect_dest_ = GURL(); + } + + // Regardless of how we got here, we are navigating to a URL so we need to + // add it to the redirect chain. + ds->AppendRedirect(url); + + // WebKit will re-use requests for in-page navigations, but we want to + // think of it as a new request that has a page ID in session history. + // This will set the proper page ID, etc. on the request so that the + // browser will treat it properly. + webframe_->CacheCurrentRequestInfo(ds); + } + + bool is_new_navigation; + webview->DidCommitLoad(&is_new_navigation); + if (d) { + d->DidChangeLocationWithinPageForFrame(webview, webframe_, + is_new_navigation); + } + + if (d) + d->DidStopLoading(webview); +} + +void WebFrameLoaderClient::dispatchWillClose() { + WebViewImpl* webview = webframe_->webview_impl(); + // Make sure WebViewImpl releases the references it uses to restore focus. + // If we didn't do this, WebViewImpl might try to restore focus to an invalid + // element. + webview->ReleaseFocusReferences(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->WillCloseFrame(webview, webframe_); +} + +void WebFrameLoaderClient::dispatchDidReceiveIcon() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->DidReceiveIconForFrame(webview, webframe_); +} + +void WebFrameLoaderClient::dispatchDidStartProvisionalLoad() { + // In case a redirect occurs, we need this to be set so that the redirect + // handling code can tell where the redirect came from. Server redirects + // will occur on the provisional load, so we need to keep track of the most + // recent provisional load URL. + // See dispatchDidReceiveServerRedirectForProvisionalLoad. + WebDataSourceImpl* ds = webframe_->GetProvisionalDataSourceImpl(); + if (!ds) { + NOTREACHED() << "Attempting to provisional load but there isn't one"; + return; + } + const GURL& url = ds->GetRequest().GetURL(); + + // Since the provisional load just started, we should have not gotten + // any redirects yet. + DCHECK(ds->GetRedirectChain().empty()); + + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + // If this load is what we expected from a client redirect, treat it as a + // redirect from that original page. The expected redirect urls will be + // cleared by DidCancelClientRedirect. + if (expected_client_redirect_src_.is_valid()) { + // expected_client_redirect_dest_ could be something like + // "javascript:history.go(-1)" thus we need to exclude url starts with + // "javascript:". See bug: 1080873 + DCHECK(expected_client_redirect_dest_.SchemeIs("javascript") || + expected_client_redirect_dest_ == url); + ds->AppendRedirect(expected_client_redirect_src_); + if (d) + d->DidCompleteClientRedirect(webview, webframe_, + expected_client_redirect_src_); + } + ds->AppendRedirect(url); + + if (d) + d->DidStartProvisionalLoadForFrame(webview, webframe_, + NavigationGestureForLastLoad()); + + // Cancel any pending loads. + if (alt_404_page_fetcher_.get()) + alt_404_page_fetcher_->Cancel(); +} + +NavigationGesture WebFrameLoaderClient::NavigationGestureForLastLoad() { + // TODO(timsteele): userGestureHint returns too many false positives + // (see bug 1051891) to trust it and assign NavigationGestureUser, so + // for now we assign Unknown in those cases and Auto otherwise. + // (Issue 874811 known false negative as well). + return webframe_->frame()->loader()->userGestureHint() ? + NavigationGestureUnknown : + NavigationGestureAuto; +} + +void WebFrameLoaderClient::dispatchDidReceiveTitle(const String& title) { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) { + d->DidReceiveTitle(webview, webkit_glue::StringToStdWString(title), + webframe_); + } +} + +void WebFrameLoaderClient::dispatchDidCommitLoad() { + WebViewImpl* webview = webframe_->webview_impl(); + + bool is_new_navigation; + webview->DidCommitLoad(&is_new_navigation); + WebViewDelegate* d = webview->delegate(); + if (d) + d->DidCommitLoadForFrame(webview, webframe_, is_new_navigation); +} + +void WebFrameLoaderClient::dispatchDidFailProvisionalLoad( + const ResourceError& error) { + // If a policy change occured, then we do not want to inform the plugin + // delegate. See bug 907789 for details. + if (error.domain() == kInternalErrorDomain && + error.errorCode() == ERR_POLICY_CHANGE) { + webframe_->DidFail(cancelledError(error.failingURL()), true); + } else { + webframe_->DidFail(error, true); + WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); + if (plg_delegate) + plg_delegate->DidFinishLoadWithReason(NPRES_NETWORK_ERR); + } +} + +void WebFrameLoaderClient::dispatchDidFailLoad(const ResourceError& error) { + webframe_->DidFail(error, false); + + WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); + if (plg_delegate) + plg_delegate->DidFinishLoadWithReason(NPRES_NETWORK_ERR); + + // Don't clear the redirect chain, this will happen in the middle of client + // redirects, and we need the context. The chain will be cleared when the + // provisional load succeeds or fails, not the "real" one. +} + +void WebFrameLoaderClient::dispatchDidFinishLoad() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->DidFinishLoadForFrame(webview, webframe_); + WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); + if (plg_delegate) + plg_delegate->DidFinishLoadWithReason(NPRES_DONE); + + // Don't clear the redirect chain, this will happen in the middle of client + // redirects, and we need the context. The chain will be cleared when the + // provisional load succeeds or fails, not the "real" one. +} + +void WebFrameLoaderClient::dispatchDidFirstLayout() { + // FIXME: called when webkit finished layout of page. + // All resources have not necessarily finished loading. +} + +Frame* WebFrameLoaderClient::dispatchCreatePage() { + struct WebCore::WindowFeatures features; + Page* new_page = webframe_->frame()->page()->chrome()->createWindow( + webframe_->frame(), FrameLoadRequest(), features); + + // Make sure that we have a valid disposition. This should have been set in + // the preceeding call to dispatchDecidePolicyForNewWindowAction. + DCHECK(next_window_open_disposition_ != IGNORE_ACTION); + WindowOpenDisposition disp = next_window_open_disposition_; + next_window_open_disposition_ = IGNORE_ACTION; + + // createWindow can return NULL (e.g., popup blocker denies the window). + if (!new_page) + return NULL; + + WebViewImpl::FromPage(new_page)->set_window_open_disposition(disp); + return new_page->mainFrame(); +} + +void WebFrameLoaderClient::dispatchShow() { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (d) + d->Show(webview, webview->window_open_disposition()); +} + +static bool TreatAsAttachment(const ResourceResponse& response) { + const String& content_disposition = + response.httpHeaderField("Content-Disposition"); + if (content_disposition.isEmpty()) + return false; + + // Some broken sites just send + // Content-Disposition: ; filename="file" + // screen those out here. + if (content_disposition.startsWith(";")) + return false; + + if (content_disposition.startsWith("inline", false)) + return false; + + // Some broken sites just send + // Content-Disposition: filename="file" + // without a disposition token... screen those out. + if (content_disposition.startsWith("filename", false)) + return false; + + // Also in use is Content-Disposition: name="file" + if (content_disposition.startsWith("name", false)) + return false; + + // We have a content-disposition of "attachment" or unknown. + // RFC 2183, section 2.8 says that an unknown disposition + // value should be treated as "attachment" + return true; +} + +void WebFrameLoaderClient::dispatchDecidePolicyForMIMEType( + FramePolicyFunction function, + const String& mime_type, + const ResourceRequest&) { + const ResourceResponse& response = + webframe_->frame()->loader()->activeDocumentLoader()->response(); + + PolicyAction action; + + int status_code = response.httpStatusCode(); + if (status_code == 204 || status_code == 205) { + // The server does not want us to replace the page contents. + action = PolicyIgnore; + } else if (TreatAsAttachment(response)) { + // The server wants us to download instead of replacing the page contents. + // Downloading is handled by the embedder, but we still get the initial + // response so that we can ignore it and clean up properly. + action = PolicyIgnore; + } else if (!canShowMIMEType(mime_type)) { + // Make sure that we can actually handle this type internally. + action = PolicyIgnore; + } else { + // OK, we will render this page. + action = PolicyUse; + } + + // NOTE: ERR_POLICY_CHANGE will be generated when action is not PolicyUse. + (webframe_->frame()->loader()->*function)(action); +} + +void WebFrameLoaderClient::dispatchDecidePolicyForNewWindowAction( + WebCore::FramePolicyFunction function, + const WebCore::NavigationAction& action, + const WebCore::ResourceRequest& request, + const WebCore::String& frame_name) { + WindowOpenDisposition disposition; + if (!ActionSpecifiesDisposition(action, &disposition)) + disposition = NEW_FOREGROUND_TAB; + + PolicyAction policy_action; + if (disposition == SAVE_TO_DISK) { + policy_action = PolicyDownload; + } else { + policy_action = PolicyUse; + + // Remember the disposition for when dispatchCreatePage is called. It is + // unfortunate that WebCore does not provide us with any context when + // creating or showing the new window that would allow us to avoid having + // to keep this state. + next_window_open_disposition_ = disposition; + } + (webframe_->frame()->loader()->*function)(policy_action); +} + +// Conversion. +static WebNavigationType NavigationTypeToWebNavigationType( + WebCore::NavigationType t) { + switch (t) { + case WebCore::NavigationTypeLinkClicked: + return WebNavigationTypeLinkClicked; + case WebCore::NavigationTypeFormSubmitted: + return WebNavigationTypeFormSubmitted; + case WebCore::NavigationTypeBackForward: + return WebNavigationTypeBackForward; + case WebCore::NavigationTypeReload: + return WebNavigationTypeReload; + case WebCore::NavigationTypeFormResubmitted: + return WebNavigationTypeFormResubmitted; + default: + case WebCore::NavigationTypeOther: + return WebNavigationTypeOther; + } +} + +void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction( + WebCore::FramePolicyFunction function, + const WebCore::NavigationAction& action, + const WebCore::ResourceRequest& request) { + PolicyAction policy_action = PolicyUse; + + WebViewImpl* wv = webframe_->webview_impl(); + WebViewDelegate* d = wv->delegate(); + // It is valid for this function to be invoked in code paths where the + // the webview is closed. + if (d) { + WindowOpenDisposition disposition = CURRENT_TAB; + ActionSpecifiesDisposition(action, &disposition); + + // Give the delegate a chance to change the disposition. When we do not + // have a provisional data source here, it means that we are scrolling to + // an anchor in the page. We don't need to ask the WebViewDelegate about + // such navigations. + const WebDataSourceImpl* ds = webframe_->GetProvisionalDataSourceImpl(); + if (ds) { + bool is_redirect = !ds->GetRedirectChain().empty(); + + WebNavigationType webnav_type = + NavigationTypeToWebNavigationType(action.type()); + + disposition = d->DispositionForNavigationAction( + wv, webframe_, &ds->GetRequest(), webnav_type, disposition, is_redirect); + + if (disposition != IGNORE_ACTION) { + if (disposition == CURRENT_TAB) { + policy_action = PolicyUse; + } else if (disposition == SAVE_TO_DISK) { + policy_action = PolicyDownload; + } else { + d->OpenURL(webframe_->webview_impl(), + webkit_glue::KURLToGURL(request.url()), + disposition); + policy_action = PolicyIgnore; + } + } else { + policy_action = PolicyIgnore; + } + } + } else { + policy_action = PolicyIgnore; + } + + (webframe_->frame()->loader()->*function)(policy_action); +} + +void WebFrameLoaderClient::cancelPolicyCheck() { + // FIXME +} + +void WebFrameLoaderClient::dispatchUnableToImplementPolicy(const ResourceError&) { + // FIXME +} + +void WebFrameLoaderClient::dispatchWillSubmitForm(FramePolicyFunction function, + PassRefPtr<FormState> form_ref) { + SearchableFormData* form_data = SearchableFormData::Create(form_ref->form()); + WebDocumentLoaderImpl* loader = static_cast<WebDocumentLoaderImpl*>( + webframe_->frame()->loader()->provisionalDocumentLoader()); + // Don't free the SearchableFormData, the loader will do that. + loader->set_searchable_form_data(form_data); + + PasswordForm* pass_data = + PasswordFormDomManager::CreatePasswordForm(form_ref->form()); + // Don't free the PasswordFormData, the loader will do that. + loader->set_password_form_data(pass_data); + + loader->set_form_submit(true); + + (webframe_->frame()->loader()->*function)(PolicyUse); +} + +void WebFrameLoaderClient::dispatchDidLoadMainResource(DocumentLoader*) { + // FIXME +} + +void WebFrameLoaderClient::revertToProvisionalState(DocumentLoader*) { + has_representation_ = true; +} + +void WebFrameLoaderClient::setMainDocumentError(DocumentLoader*, + const ResourceError& error) { + if (plugin_widget_) { + if (sent_initial_response_to_plugin_) { + plugin_widget_->didFail(error); + sent_initial_response_to_plugin_ = false; + } + plugin_widget_ = NULL; + } +} + +void WebFrameLoaderClient::clearUnarchivingState(DocumentLoader*) { + // FIXME +} + +void WebFrameLoaderClient::postProgressStartedNotification() { + if (hasWebView()) { + WebViewDelegate* d = webframe_->webview_impl()->delegate(); + if (d) + d->DidStartLoading(webframe_->webview_impl()); + } +} + +void WebFrameLoaderClient::postProgressEstimateChangedNotification() { + // FIXME +} + +void WebFrameLoaderClient::postProgressFinishedNotification() { + // TODO(ericroman): why might webframe_->webview_impl be null? + // http://b/1234461 + if (hasWebView()) { + WebViewDelegate* d = webframe_->webview_impl()->delegate(); + + if (d) + d->DidStopLoading(webframe_->webview_impl()); + } +} + +void WebFrameLoaderClient::setMainFrameDocumentReady(bool ready) { + // FIXME +} + +// Creates a new connection and begins downloading from that (contrast this +// with |download|). +void WebFrameLoaderClient::startDownload(const ResourceRequest& request) { + WebViewDelegate* d = webframe_->webview_impl()->delegate(); + if (d) { + const GURL url(webkit_glue::KURLToGURL(request.url())); + const GURL referrer(webkit_glue::StringToStdWString(request.httpReferrer())); + d->DownloadUrl(url, referrer); + } +} + +void WebFrameLoaderClient::willChangeTitle(DocumentLoader*) { + // FIXME +} +void WebFrameLoaderClient::didChangeTitle(DocumentLoader*) { + // FIXME +} + +// Called whenever data is received. +void WebFrameLoaderClient::committedLoad(DocumentLoader* loader, const char* data, int length) { + if (!plugin_widget_) { + if (postpone_loading_data_) { + postponed_data_.append(data, length); + if (postponed_data_.length() >= 512) { + postpone_loading_data_ = false; + webframe_->DidReceiveData(loader, postponed_data_.c_str(), + static_cast<int>(postponed_data_.length())); + } + return; + } + webframe_->DidReceiveData(loader, data, length); + } + + // The plugin widget could have been created in the webframe_->DidReceiveData + // function. + if (plugin_widget_) { + if (!sent_initial_response_to_plugin_) { + sent_initial_response_to_plugin_ = true; + plugin_widget_->didReceiveResponse( + webframe_->frame()->loader()->activeDocumentLoader()->response()); + } + plugin_widget_->didReceiveData(data, length); + } +} + +void WebFrameLoaderClient::finishedLoading(DocumentLoader* dl) { + if (plugin_widget_) { + plugin_widget_->didFinishLoading(); + plugin_widget_ = NULL; + sent_initial_response_to_plugin_ = false; + } else { + // This is necessary to create an empty document. See bug 634004. + // However, we only want to do this if makeRepresentation has been called, to + // match the behavior on the Mac. + if (has_representation_) + dl->frameLoader()->setEncoding("", false); + } +} + +void WebFrameLoaderClient::finalSetupForReplace(DocumentLoader*) { + // FIXME +} + +void WebFrameLoaderClient::updateGlobalHistoryForStandardLoad(const KURL& kurl) { +} + +void WebFrameLoaderClient::updateGlobalHistoryForReload(const KURL&) { + // FIXME: this is for updating the visit time. +} + +bool WebFrameLoaderClient::shouldGoToHistoryItem(HistoryItem*) const { + // FIXME + return true; +} + +ResourceError WebFrameLoaderClient::blockedError(const WebCore::ResourceRequest&) { + // FIXME + return ResourceError(); +} + +ResourceError WebFrameLoaderClient::cancelledError( + const ResourceRequest& request) { + return ResourceError(net::kErrorDomain, net::ERR_ABORTED, + request.url().string(), String()); +} +ResourceError WebFrameLoaderClient::cannotShowURLError(const ResourceRequest&) { + // FIXME + return ResourceError(); +} +ResourceError WebFrameLoaderClient::interruptForPolicyChangeError( + const ResourceRequest& request) { + return ResourceError(kInternalErrorDomain, ERR_POLICY_CHANGE, + request.url().string(), String()); +} + +ResourceError WebFrameLoaderClient::cannotShowMIMETypeError(const ResourceResponse&) { + // FIXME + return ResourceError(); +} +ResourceError WebFrameLoaderClient::fileDoesNotExistError(const ResourceResponse&) { + // FIXME + return ResourceError(); +} + +bool WebFrameLoaderClient::shouldFallBack(const ResourceError& error) { + // This method is called when we fail to load the URL for an <object> tag + // that has fallback content (child elements) and is being loaded as a frame. + // The error parameter indicates the reason for the load failure. + // We should let the fallback content load only if this wasn't a cancelled + // request. + // Note: The mac version also has a case for "WebKitErrorPluginWillHandleLoad" + return error.errorCode() != net::ERR_ABORTED; +} + +void WebFrameLoaderClient::setDefersLoading(bool) { + // FIXME +} + +bool WebFrameLoaderClient::willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL& originalURL) const { + // FIXME + return false; +} +bool WebFrameLoaderClient::isArchiveLoadPending(ResourceLoader*) const { + // FIXME + return false; +} +void WebFrameLoaderClient::cancelPendingArchiveLoad(ResourceLoader*) { + // FIXME +} +void WebFrameLoaderClient::clearArchivedResources() { + // FIXME +} + +bool WebFrameLoaderClient::canHandleRequest(const ResourceRequest&) const { + // FIXME: this appears to be used only by the context menu code to determine + // if "open" should be displayed in the menu when clicking on a link. + return true; +} + +bool WebFrameLoaderClient::canShowMIMEType(const String& mime_type) const { + // This method is called to determine if the media type can be shown + // "internally" (i.e. inside the browser) regardless of whether or not the + // browser or a plugin is doing the rendering. + + if (mime_util::IsSupportedMimeType( + WideToASCII(webkit_glue::StringToStdWString(mime_type)))) + return true; + + // See if the type is handled by an installed plugin, if so, we can show it. + // TODO(beng): (http://b/1085524) This is the place to stick a preference to + // disable full page plugins (optionally for certain types!) + return PluginInfoStore::supportsMIMEType(mime_type); +} + +bool WebFrameLoaderClient::representationExistsForURLScheme(const String& URLScheme) const { + // FIXME + return false; +} + +String WebFrameLoaderClient::generatedMIMETypeForURLScheme(const String& URLScheme) const { + // This appears to generate MIME types for protocol handlers that are handled + // internally. The only place I can find in the WebKit code that uses this + // function is WebView::registerViewClass, where it is used as part of the + // process by which custom view classes for certain document representations + // are registered. + String mimetype(L"x-apple-web-kit/"); + mimetype.append(URLScheme.lower()); + return mimetype; +} + +void WebFrameLoaderClient::frameLoadCompleted() { + // FIXME: the mac port also conditionally calls setDrawsBackground:YES on + // it's ScrollView here. + + // This comment from the Mac port: + // Note: Can be called multiple times. + // Even if already complete, we might have set a previous item on a frame that + // didn't do any data loading on the past transaction. Make sure to clear these out. + webframe_->frame()->loader()->setPreviousHistoryItem(0); +} + +void WebFrameLoaderClient::saveViewStateToItem(HistoryItem*) { + // FIXME +} + + +void WebFrameLoaderClient::restoreViewState() { + // FIXME: probably scrolls to last position when you go back or forward +} + +void WebFrameLoaderClient::provisionalLoadStarted() { + // FIXME: On mac, this does various caching stuff +} + +void WebFrameLoaderClient::didFinishLoad() { + WebPluginDelegate* plg_delegate = webframe_->plugin_delegate(); + if (plg_delegate) + plg_delegate->DidFinishLoadWithReason(NPRES_DONE); +} +void WebFrameLoaderClient::prepareForDataSourceReplacement() { + // FIXME +} + +PassRefPtr<DocumentLoader> WebFrameLoaderClient::createDocumentLoader( + const ResourceRequest& request, + const SubstituteData& data) { + WebDocumentLoaderImpl* loader = new WebDocumentLoaderImpl(request, data); + + // Attach a datasource to the loader as a way of accessing requests. + WebDataSourceImpl* datasource = + WebDataSourceImpl::CreateInstance(webframe_, loader); + loader->SetDataSource(datasource); + + webframe_->CacheCurrentRequestInfo(datasource); + + return loader; +} + +void WebFrameLoaderClient::setTitle(const String& title, const KURL& url) { + // FIXME: monitor for changes in WebFrameLoaderClient.mm + // FIXME: Set the title of the current history item. HistoryItemImpl's setter + // will notify its clients (e.g. the history database) that the title + // has changed. + // + // e.g.: + // WebHistoryItem* item = + // webframe_->webview_impl()->GetBackForwardList()->GetCurrentItem(); + // WebHistoryItemImpl* item_impl = static_cast<WebHistoryItemImpl*>(item); + // + // item_impl->SetTitle(webkit_glue::StringToStdWString(title)); +} + +String WebFrameLoaderClient::userAgent(const KURL& url) { + return webkit_glue::StdStringToString( + webframe_->webview_impl()->GetPreferences().user_agent); +} + +void WebFrameLoaderClient::savePlatformDataToCachedPage(WebCore::CachedPage*) { + NOTREACHED() << "Page cache should be disabled"; +} + +void WebFrameLoaderClient::transitionToCommittedFromCachedPage(WebCore::CachedPage*) { + ASSERT_NOT_REACHED(); +} + +void WebFrameLoaderClient::transitionToCommittedForNewPage() { + makeDocumentView(); +} + +bool WebFrameLoaderClient::canCachePage() const { + NOTREACHED() << "Page cache should be disabled"; + return false; +} + +// Downloading is handled in the browser process, not WebKit. If we get to this +// point, our download detection code in the ResourceDispatcherHost is broken! +void WebFrameLoaderClient::download(ResourceHandle* handle, + const ResourceRequest& request, + const ResourceRequest& initialRequest, + const ResourceResponse& response) { + NOTREACHED(); +} + +PassRefPtr<Frame> WebFrameLoaderClient::createFrame( + const KURL& url, + const String& name, + HTMLFrameOwnerElement* owner_element, + const String& referrer, + bool allows_scrolling, + int margin_width, + int margin_height) { + FrameLoadRequest frame_request(ResourceRequest(url, referrer), name); + + Frame* new_frame = NULL; + if (webframe_) + webframe_->CreateChildFrame(frame_request, owner_element, allows_scrolling, + margin_width, margin_height, new_frame); + return new_frame; +} + +// Utility function to convert a vector to an array of char*'s. +// Caller is responsible to free memory with DeleteToArray(). +static char** ToArray(const Vector<WebCore::String> &vector) { + char **rv = new char *[vector.size()+1]; + unsigned int index = 0; + for (index = 0; index < vector.size(); ++index) { + WebCore::CString src = vector[index].utf8(); + rv[index] = new char[src.length() + 1]; + strncpy_s(rv[index], src.length() + 1, src.data(), _TRUNCATE); + rv[index][src.length()] = '\0'; + } + rv[index] = 0; + return rv; +} + +static void DeleteToArray(char** arr) { + char **ptr = arr; + while (*ptr != 0) { + delete [] *ptr; + ++ptr; + } + delete [] arr; +} + +Widget* WebFrameLoaderClient::createPlugin(const IntSize& size, // TODO(erikkay): how do we use this? + Element *element, const KURL &url, + const Vector<String> ¶m_names, + const Vector<String> ¶m_values, + const String &mime_type, + bool load_manually) { + WebViewImpl* webview = webframe_->webview_impl(); + WebViewDelegate* d = webview->delegate(); + if (!d) + return NULL; + + GURL gurl = webkit_glue::KURLToGURL(url); + std::string my_mime_type = + webkit_glue::CStringToStdString(mime_type.latin1()); + StringToLowerASCII(&my_mime_type); + + // Get the classid and version from attributes of the object. + std::string clsid, version, combined_clsid; + if (activex_shim::IsMimeTypeActiveX(my_mime_type)) { + GURL url = webframe_->GetURL(); + for (unsigned int i = 0; i < param_names.size(); i++) { + String lowercase_param_name = param_names[i].lower(); + if (lowercase_param_name == "classid") { + activex_shim::GetClsidFromClassidAttribute( + webkit_glue::CStringToStdString(param_values[i].latin1()), &clsid); + } else if (lowercase_param_name == "codebase") { + version = activex_shim::GetVersionFromCodebaseAttribute( + webkit_glue::CStringToStdString(param_values[i].latin1())); + } + } + // We only allowed specific ActiveX controls to run from certain websites. + if (!activex_shim::IsActiveXAllowed(clsid, url)) + return NULL; + // We need to pass the combined clsid + version to PluginsList, so that it + // would detect if the requested version is installed. If not, it needs + // to use the default plugin to update the control. + if (!version.empty()) + combined_clsid = clsid + "#" + version; + else + combined_clsid = clsid; + } + + std::string actual_mime_type; + WebPluginDelegate* plugin_delegate = + d->CreatePluginDelegate(webframe_->webview_impl(), gurl, my_mime_type, + combined_clsid, &actual_mime_type); + if (!plugin_delegate) + return NULL; + + if (!actual_mime_type.empty()) + my_mime_type = actual_mime_type; + + DCHECK(param_names.size() == param_values.size()); + + char **argn = NULL; + char **argv = NULL; + int argc = 0; + // There is a bug in Webkit which occurs when a plugin instance is defined + // with an OBJECT tag containing the "DATA" attribute". Please refer to the + // webkit issue http://bugs.webkit.org/show_bug.cgi?id=15457 for more info. + // The code below is a patch which should be taken out when a fix is + // available in webkit. The logic is to add the "src" attribute to the list + // of params if the "data" attribute exists. + // TODO(iyengar) : remove this when a fix is available in webkit. + int data_attr_index = -1; + int src_attr_index = -1; + for (unsigned int i = 0; i < param_names.size(); i++) { + String param_name = param_names[i].lower(); + if (param_name == "data") + data_attr_index = i; + else if (param_name == "src") + src_attr_index = i; + } + if ((data_attr_index != -1) && (src_attr_index == -1)) { + Vector<String> updated_param_names = param_names; + Vector<String> updated_param_values = param_values; + updated_param_names.append("src"); + updated_param_values.append(param_values[data_attr_index]); + + argn = ToArray(updated_param_names); + argv = ToArray(updated_param_values); + argc = static_cast<int>(updated_param_names.size()); + } else { + argn = ToArray(param_names); + argv = ToArray(param_values); + argc = static_cast<int>(param_names.size()); + } + + Widget* result = WebPluginImpl::Create(gurl, argn, argv, argc, element, + webframe_, plugin_delegate, + load_manually); + + DeleteToArray(argn); + DeleteToArray(argv); + + return result; +} + +// This method gets called when a plugin is put in place of html content +// (e.g., acrobat reader). +void WebFrameLoaderClient::redirectDataToPlugin(Widget* pluginWidget) { + plugin_widget_ = static_cast<WebPluginContainer*>(pluginWidget); + DCHECK(plugin_widget_ != NULL); +} + +Widget* WebFrameLoaderClient::createJavaAppletWidget( + const IntSize& size, + Element *element, const KURL &url, + const Vector<String> ¶m_names, + const Vector<String> ¶m_values) { + return createPlugin(size, element, url, param_names, param_values, + "application/x-java-applet", false); +} + +ObjectContentType WebFrameLoaderClient::objectContentType( + const KURL& url, + const String& explicit_mime_type) { + // This code is based on Apple's implementation from + // WebCoreSupport/WebFrameBridge.mm. + + String mime_type = explicit_mime_type; + if (mime_type.isEmpty()) { + // Try to guess the MIME type based off the extension. + String filename = url.lastPathComponent(); + int extension_pos = filename.reverseFind('.'); + if (extension_pos >= 0) + mime_type = MIMETypeRegistry::getMIMETypeForPath(url.path()); + + if (mime_type.isEmpty()) + return ObjectContentFrame; + } + + if (MIMETypeRegistry::isSupportedImageMIMEType(mime_type)) + return ObjectContentImage; + + if (PluginInfoStore::supportsMIMEType(mime_type)) + return ObjectContentNetscapePlugin; + + if (MIMETypeRegistry::isSupportedNonImageMIMEType(mime_type)) + return ObjectContentFrame; + + return ObjectContentNone; +} + +String WebFrameLoaderClient::overrideMediaType() const { + // FIXME + String rv; + return rv; +} + +bool WebFrameLoaderClient::ActionSpecifiesDisposition( + const WebCore::NavigationAction& action, + WindowOpenDisposition* disposition) { + if ((action.type() != NavigationTypeLinkClicked) || + !action.event()->isMouseEvent()) + return false; + + const MouseEvent* event = static_cast<const MouseEvent*>(action.event()); + const bool middle_or_ctrl = (event->button() == 1) || event->ctrlKey(); + const bool shift = event->shiftKey(); + const bool alt = event->altKey(); + if (!middle_or_ctrl && !shift && !alt) + return false; + + DCHECK(disposition); + if (middle_or_ctrl) + *disposition = shift ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; + else + *disposition = shift ? NEW_WINDOW : SAVE_TO_DISK; + return true; +} diff --git a/webkit/glue/webframeloaderclient_impl.h b/webkit/glue/webframeloaderclient_impl.h new file mode 100644 index 0000000..2c2eb78 --- /dev/null +++ b/webkit/glue/webframeloaderclient_impl.h @@ -0,0 +1,287 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBFRAMELOADERCLIENT_IMPL_H__ +#define WEBKIT_GLUE_WEBFRAMELOADERCLIENT_IMPL_H__ + +#pragma warning(push, 0) +#include "FrameLoaderClient.h" +#pragma warning(pop) + +#include "googleurl/src/gurl.h" +#include "webkit/glue/window_open_disposition.h" + +namespace WebCore { +class Frame; +class Widget; +} + +enum NavigationGesture; +class Alt404PageResourceFetcher; +class WebFrameImpl; +class WebPluginContainer; + +class WebFrameLoaderClient : public WebCore::FrameLoaderClient { + public: + WebFrameLoaderClient(WebFrameImpl* webframe); + virtual ~WebFrameLoaderClient(); + + WebFrameImpl* webframe() const { return webframe_; } + + // WebCore::FrameLoaderClient ---------------------------------------------- + + virtual void frameLoaderDestroyed(); + + // Notifies the WebView delegate that the JS window object has been cleared, + // giving it a chance to bind native objects to the window before script + // parsing begins. + virtual void windowObjectCleared(); + + virtual bool hasWebView() const; // mainly for assertions + virtual bool hasFrameView() const; // ditto + + virtual bool privateBrowsingEnabled() const; + + virtual void makeDocumentView(); + virtual void makeRepresentation(WebCore::DocumentLoader*); + virtual void setDocumentViewFromCachedPage(WebCore::CachedPage*); + virtual void forceLayout(); + virtual void forceLayoutForNonHTML(); + + virtual void setCopiesOnScroll(); + + virtual void detachedFromParent2(); + virtual void detachedFromParent3(); + virtual void detachedFromParent4(); + + virtual void loadedFromCachedPage(); + + virtual void assignIdentifierToInitialRequest(unsigned long identifier, WebCore::DocumentLoader*, const WebCore::ResourceRequest&); + + virtual void dispatchWillSendRequest(WebCore::DocumentLoader*, unsigned long identifier, WebCore::ResourceRequest&, const WebCore::ResourceResponse& redirectResponse); + virtual void dispatchDidReceiveAuthenticationChallenge(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::AuthenticationChallenge&); + virtual void dispatchDidCancelAuthenticationChallenge(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::AuthenticationChallenge&); + virtual void dispatchDidReceiveResponse(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::ResourceResponse&); + virtual void dispatchDidReceiveContentLength(WebCore::DocumentLoader*, unsigned long identifier, int lengthReceived); + virtual void dispatchDidFinishLoading(WebCore::DocumentLoader*, unsigned long identifier); + virtual void dispatchDidFailLoading(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::ResourceError&); + virtual bool dispatchDidLoadResourceFromMemoryCache(WebCore::DocumentLoader*, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&, int length); + + virtual void dispatchDidHandleOnloadEvents(); + virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(); + virtual void dispatchDidCancelClientRedirect(); + virtual void dispatchWillPerformClientRedirect(const WebCore::KURL&, double interval, double fireDate); + virtual void dispatchDidChangeLocationWithinPage(); + virtual void dispatchWillClose(); + virtual void dispatchDidReceiveIcon(); + virtual void dispatchDidStartProvisionalLoad(); + virtual void dispatchDidReceiveTitle(const WebCore::String& title); + virtual void dispatchDidCommitLoad(); + virtual void dispatchDidFailProvisionalLoad(const WebCore::ResourceError&); + virtual void dispatchDidFailLoad(const WebCore::ResourceError&); + virtual void dispatchDidFinishDocumentLoad(); + virtual void dispatchDidFinishLoad(); + virtual void dispatchDidFirstLayout(); + + virtual WebCore::Frame* dispatchCreatePage(); + virtual void dispatchShow(); + + virtual void dispatchDecidePolicyForMIMEType(WebCore::FramePolicyFunction function, const WebCore::String& mime_type, const WebCore::ResourceRequest&); + virtual void dispatchDecidePolicyForNewWindowAction(WebCore::FramePolicyFunction function, const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request, const WebCore::String& frame_name); + virtual void dispatchDecidePolicyForNavigationAction(WebCore::FramePolicyFunction function, const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request); + virtual void cancelPolicyCheck(); + + virtual void dispatchUnableToImplementPolicy(const WebCore::ResourceError&); + + virtual void dispatchWillSubmitForm(WebCore::FramePolicyFunction, PassRefPtr<WebCore::FormState>); + + virtual void dispatchDidLoadMainResource(WebCore::DocumentLoader*); + virtual void revertToProvisionalState(WebCore::DocumentLoader*); + virtual void setMainDocumentError(WebCore::DocumentLoader*, const WebCore::ResourceError&); + virtual void clearUnarchivingState(WebCore::DocumentLoader*); + + // Maybe these should go into a ProgressTrackerClient some day + virtual void willChangeEstimatedProgress() { } + virtual void didChangeEstimatedProgress() { } + virtual void postProgressStartedNotification(); + virtual void postProgressEstimateChangedNotification(); + virtual void postProgressFinishedNotification(); + + virtual void setMainFrameDocumentReady(bool); + + virtual void startDownload(const WebCore::ResourceRequest&); + + virtual void willChangeTitle(WebCore::DocumentLoader*); + virtual void didChangeTitle(WebCore::DocumentLoader*); + + virtual void committedLoad(WebCore::DocumentLoader*, const char*, int); + virtual void finishedLoading(WebCore::DocumentLoader*); + virtual void finalSetupForReplace(WebCore::DocumentLoader*); + + virtual void updateGlobalHistoryForStandardLoad(const WebCore::KURL&); + virtual void updateGlobalHistoryForReload(const WebCore::KURL&); + virtual bool shouldGoToHistoryItem(WebCore::HistoryItem*) const; + + virtual WebCore::ResourceError blockedError(const WebCore::ResourceRequest&); + virtual WebCore::ResourceError cancelledError(const WebCore::ResourceRequest&); + virtual WebCore::ResourceError cannotShowURLError(const WebCore::ResourceRequest&); + virtual WebCore::ResourceError interruptForPolicyChangeError(const WebCore::ResourceRequest&); + + virtual WebCore::ResourceError cannotShowMIMETypeError(const WebCore::ResourceResponse&); + virtual WebCore::ResourceError fileDoesNotExistError(const WebCore::ResourceResponse&); + + virtual bool shouldFallBack(const WebCore::ResourceError&); + + virtual void setDefersLoading(bool); + + virtual bool willUseArchive(WebCore::ResourceLoader*, const WebCore::ResourceRequest&, const WebCore::KURL& originalURL) const; + virtual bool isArchiveLoadPending(WebCore::ResourceLoader*) const; + virtual void cancelPendingArchiveLoad(WebCore::ResourceLoader*); + virtual void clearArchivedResources(); + + virtual bool canHandleRequest(const WebCore::ResourceRequest&) const; + virtual bool canShowMIMEType(const WebCore::String& MIMEType) const; + virtual bool representationExistsForURLScheme(const WebCore::String& URLScheme) const; + virtual WebCore::String generatedMIMETypeForURLScheme(const WebCore::String& URLScheme) const; + + virtual void frameLoadCompleted(); + virtual void saveViewStateToItem(WebCore::HistoryItem*); + virtual void restoreViewState(); + virtual void provisionalLoadStarted(); + virtual void didFinishLoad(); + virtual void prepareForDataSourceReplacement(); + + virtual PassRefPtr<WebCore::DocumentLoader> createDocumentLoader( + const WebCore::ResourceRequest&, + const WebCore::SubstituteData&); + virtual void setTitle(const WebCore::String& title, const WebCore::KURL&); + + virtual WebCore::String userAgent(const WebCore::KURL&); + + virtual void savePlatformDataToCachedPage(WebCore::CachedPage*); + virtual void transitionToCommittedFromCachedPage(WebCore::CachedPage*); + virtual void transitionToCommittedForNewPage(); + + virtual bool canCachePage() const; + virtual void download(WebCore::ResourceHandle* handle, + const WebCore::ResourceRequest& request, + const WebCore::ResourceRequest& initialRequest, + const WebCore::ResourceResponse& response); + virtual PassRefPtr<WebCore::Frame> createFrame( + const WebCore::KURL& url, + const WebCore::String& name, + WebCore::HTMLFrameOwnerElement* ownerElement, + const WebCore::String& referrer, + bool allowsScrolling, int marginWidth, + int marginHeight); + virtual WebCore::Widget* createPlugin(const WebCore::IntSize&, + WebCore::Element*, + const WebCore::KURL&, + const WTF::Vector<WebCore::String>&, + const WTF::Vector<WebCore::String>&, + const WebCore::String&, + bool loadManually); + virtual void redirectDataToPlugin(WebCore::Widget* pluginWidget); + + virtual WebCore::Widget* createJavaAppletWidget(const WebCore::IntSize&, + WebCore::Element*, const WebCore::KURL& baseURL, + const WTF::Vector<WebCore::String>& paramNames, + const WTF::Vector<WebCore::String>& paramValues); + + virtual WebCore::ObjectContentType objectContentType(const WebCore::KURL& url, + const WebCore::String& mimeType); + virtual WebCore::String overrideMediaType() const; + + virtual void didPerformFirstNavigation() const; + + virtual void registerForIconNotification(bool listen = true); + + virtual void unloadListenerChanged(); + +#if defined(__APPLE__) + virtual NSCachedURLResponse* willCacheResponse(WebCore::DocumentLoader*, + unsigned long identifier, + NSCachedURLResponse*) const; +#endif + + // Callback function for download of alternate 404 pages. If the server is + // down or we take more than 1s to download the page, html will be an empty + // string. + void Alt404PageFinished(WebCore::DocumentLoader* loader, + const std::string& html); + + private: + // Given a NavigationAction, determine the associated window opening + // disposition. For example, a middle click means "open in background tab". + static bool ActionSpecifiesDisposition( + const WebCore::NavigationAction& action, + WindowOpenDisposition* disposition); + + // Returns a valid GURL if we have an alt 404 server URL. + GURL GetAlt404PageUrl(WebCore::DocumentLoader* loader); + + // Returns NavigationGestureAuto if the last load was not user initiated, + // otherwise returns NavigationGestureUnknown. + NavigationGesture NavigationGestureForLastLoad(); + + // The WebFrame that owns this object and manages its lifetime. Therefore, + // the web frame object is guaranteed to exist. + WebFrameImpl* webframe_; + + // Resource fetcher for downloading an alternate 404 page. + scoped_ptr<Alt404PageResourceFetcher> alt_404_page_fetcher_; + + bool postpone_loading_data_; + std::string postponed_data_; + + // True if makeRepresentation was called. We don't actually have a concept + // of a "representation", but we need to know when we're expected to have one. + // See finishedLoading(). + bool has_representation_; + + // Used to help track client redirects. When a provisional load starts, it + // has no redirects in its chain. But in the case of client redirects, we want + // to add that initial load as a redirect. When we get a new provisional load + // and the dest URL matches that load, we know that it was the result of a + // previous client redirect and the source should be added as a redirect. + // Both should be empty if unused. + GURL expected_client_redirect_src_; + GURL expected_client_redirect_dest_; + + // Contains a pointer to the plugin widget. + WebPluginContainer* plugin_widget_; + // Indicates if we need to send over the initial notification to the plugin + // which specifies that the plugin should be ready to accept data. + bool sent_initial_response_to_plugin_; + + // The disposition to use for the next call to dispatchCreatePage. + WindowOpenDisposition next_window_open_disposition_; +}; + +#endif // #ifndef WEBKIT_GLUE_WEBFRAMELOADERCLIENT_IMPL_H__ diff --git a/webkit/glue/webhistoryitem.h b/webkit/glue/webhistoryitem.h new file mode 100644 index 0000000..3a2f0df --- /dev/null +++ b/webkit/glue/webhistoryitem.h @@ -0,0 +1,68 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBHISTORYITEM_H__ +#define WEBKIT_GLUE_WEBHISTORYITEM_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "webkit/glue/weburlrequest.h" // for WebRequest::ExtraData + +class GURL; + +class WebHistoryItem : public base::RefCounted<WebHistoryItem> { + public: + // Create a new history item. + static WebHistoryItem* Create(const GURL& url, + const std::wstring& title, + const std::string& history_state, + WebRequest::ExtraData* extra_data); + + WebHistoryItem() { } + virtual ~WebHistoryItem() { } + + // Returns the URL. + virtual const GURL& GetURL() const = 0; + + // Returns the title. + virtual const std::wstring& GetTitle() const = 0; + + // Returns the string representation of the history state for this entry. + virtual const std::string& GetHistoryState() const = 0; + + // Returns any ExtraData associated with this history entry. + virtual WebRequest::ExtraData* GetExtraData() const = 0; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebHistoryItem); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBHISTORYITEM_H__ diff --git a/webkit/glue/webhistoryitem_impl.cc b/webkit/glue/webhistoryitem_impl.cc new file mode 100644 index 0000000..7ee880d --- /dev/null +++ b/webkit/glue/webhistoryitem_impl.cc @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006 Apple Computer, 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 "webkit/glue/webhistoryitem_impl.h" + +#include "webkit/glue/glue_serialize.h" + +#pragma warning(push, 0) +#include "HistoryItem.h" +#pragma warning(pop) + + +WebHistoryItem* WebHistoryItem::Create(const GURL& url, + const std::wstring& title, + const std::string& history_state, + WebRequest::ExtraData* extra_data) { + return new WebHistoryItemImpl(url, title, history_state, extra_data); +} + +WebHistoryItemImpl::WebHistoryItemImpl(const GURL& url, + const std::wstring& title, + const std::string& history_state, + WebRequest::ExtraData* extra_data) : + url_(url), + title_(title), + history_state_(history_state), + history_item_(NULL), + extra_data_(extra_data) { +} + +WebHistoryItemImpl::~WebHistoryItemImpl() { +} + +const std::wstring& WebHistoryItemImpl::GetTitle() const { + return title_; +} + +const std::string& WebHistoryItemImpl::GetHistoryState() const { + return history_state_; +} + +WebRequest::ExtraData* WebHistoryItemImpl::GetExtraData() const { + return extra_data_.get(); +} + +WebCore::HistoryItem* WebHistoryItemImpl::GetHistoryItem() const { + if (history_item_) + return history_item_.get(); + + history_item_ = webkit_glue::HistoryItemFromString(history_state_); + return history_item_.get(); +} + +const GURL& WebHistoryItemImpl::GetURL() const { + return url_; +} diff --git a/webkit/glue/webhistoryitem_impl.h b/webkit/glue/webhistoryitem_impl.h new file mode 100644 index 0000000..312b82e --- /dev/null +++ b/webkit/glue/webhistoryitem_impl.h @@ -0,0 +1,73 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBHISTORYITEM_IMPL_H__ +#define WEBKIT_GLUE_WEBHISTORYITEM_IMPL_H__ + +#include "base/basictypes.h" +#include "webkit/glue/webhistoryitem.h" +#include "googleurl/src/gurl.h" + +#include "RefPtr.h" + +namespace WebCore { + class HistoryItem; +} + +class WebHistoryItemImpl : public WebHistoryItem { + public: + WebHistoryItemImpl(const GURL& url, + const std::wstring& title, + const std::string& history_state, + WebRequest::ExtraData* extra_data); + virtual ~WebHistoryItemImpl(); + + // WebHistoryItem + virtual const GURL& GetURL() const; + virtual const std::wstring& GetTitle() const; + virtual const std::string& GetHistoryState() const; + virtual WebRequest::ExtraData* GetExtraData() const; + + // WebHistoryItemImpl + // Returns a WebCore::HistoryItem based on the history_state. This is + // lazily-created and cached. + WebCore::HistoryItem* GetHistoryItem() const; + + protected: + GURL url_; + std::wstring title_; + std::string history_state_; + mutable RefPtr<WebCore::HistoryItem> history_item_; + scoped_refptr<WebRequest::ExtraData> extra_data_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebHistoryItemImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBHISTORYITEM_IMPL_H__ diff --git a/webkit/glue/webinputevent.cc b/webkit/glue/webinputevent.cc new file mode 100644 index 0000000..79f8afbe --- /dev/null +++ b/webkit/glue/webinputevent.cc @@ -0,0 +1,328 @@ +// Copyright 2008, 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 "webkit/glue/webinputevent.h" + +#include "webkit/glue/event_conversion.h" + +#undef LOG +#include "base/logging.h" + +static const unsigned long kDefaultScrollLinesPerWheelDelta = 3; + +// WebMouseEvent -------------------------------------------------------------- + +static LPARAM GetRelativeCursorPos(HWND hwnd) { + POINT pos = {-1, -1}; + GetCursorPos(&pos); + ScreenToClient(hwnd, &pos); + return MAKELPARAM(pos.x, pos.y); +} + +WebMouseEvent::WebMouseEvent(HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam) { + switch (message) { + case WM_MOUSEMOVE: + type = MOUSE_MOVE; + if (wparam & MK_LBUTTON) + button = BUTTON_LEFT; + else if (wparam & MK_MBUTTON) + button = BUTTON_MIDDLE; + else if (wparam & MK_RBUTTON) + button = BUTTON_MIDDLE; + else + button = BUTTON_NONE; + break; + case WM_MOUSELEAVE: + type = MOUSE_LEAVE; + button = BUTTON_NONE; + // set the current mouse position (relative to the client area of the + // current window) since none is specified for this event + lparam = GetRelativeCursorPos(hwnd); + break; + case WM_LBUTTONDOWN: + type = MOUSE_DOWN; + button = BUTTON_LEFT; + break; + case WM_MBUTTONDOWN: + type = MOUSE_DOWN; + button = BUTTON_MIDDLE; + break; + case WM_RBUTTONDOWN: + type = MOUSE_DOWN; + button = BUTTON_RIGHT; + break; + case WM_LBUTTONUP: + type = MOUSE_UP; + button = BUTTON_LEFT; + break; + case WM_MBUTTONUP: + type = MOUSE_UP; + button = BUTTON_MIDDLE; + break; + case WM_RBUTTONUP: + type = MOUSE_UP; + button = BUTTON_RIGHT; + break; + case WM_LBUTTONDBLCLK: + type = MOUSE_DOUBLE_CLICK; + button = BUTTON_LEFT; + break; + case WM_MBUTTONDBLCLK: + type = MOUSE_DOUBLE_CLICK; + button = BUTTON_MIDDLE; + break; + case WM_RBUTTONDBLCLK: + type = MOUSE_DOUBLE_CLICK; + button = BUTTON_RIGHT; + break; + default: + NOTREACHED() << "unexpected native message"; + } + + // set position fields: + + x = static_cast<short>(LOWORD(lparam)); + y = static_cast<short>(HIWORD(lparam)); + + POINT global_point = { x, y }; + ClientToScreen(hwnd, &global_point); + + global_x = global_point.x; + global_y = global_point.y; + + // set modifiers: + + modifiers = 0; + if (wparam & MK_CONTROL) + modifiers |= CTRL_KEY; + if (wparam & MK_SHIFT) + modifiers |= SHIFT_KEY; + if (GetKeyState(VK_MENU) & 0x8000) + modifiers |= (ALT_KEY | META_KEY); // TODO(darin): set META properly + + // TODO(pkasting): http://b/1117926 Instead of using GetTickCount() here, we + // should use GetMessageTime() on the original Windows message in the browser + // process, and pass that in the WebMouseEvent. + timestamp_sec = GetTickCount() / 1000.0; + + layout_test_click_count = 0; +} + +// WebMouseWheelEvent --------------------------------------------------------- + +WebMouseWheelEvent::WebMouseWheelEvent(HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam) { + type = MOUSE_WHEEL; + button = BUTTON_NONE; + + UINT key_state = GET_KEYSTATE_WPARAM(wparam); + int wheel_delta = static_cast<int>(GET_WHEEL_DELTA_WPARAM(wparam)); + + typedef SHORT (WINAPI *GetKeyStateFunction)(int key); + GetKeyStateFunction get_key_state = GetKeyState; + + // Synthesize mousewheel event from a scroll event. + // This is needed to simulate middle mouse scrolling in some + // laptops (Thinkpads) + if ((WM_VSCROLL == message) || (WM_HSCROLL == message)) { + + POINT cursor_position = {0}; + GetCursorPos(&cursor_position); + + global_x = cursor_position.x; + global_y = cursor_position.y; + + key_state = 0; + + // Since we are synthesizing the wheel event, we have to + // use GetAsyncKeyState + if (GetAsyncKeyState(VK_SHIFT)) + key_state |= MK_SHIFT; + + if (GetAsyncKeyState(VK_CONTROL)) + key_state |= MK_CONTROL; + + // Add a simple workaround to scroll multiples units per page. + // The right fix needs to extend webkit's implementation of + // wheel events and that's not something we want to do at + // this time. See bug# 928509 + // TODO(joshia): Implement the right fix for bug# 928509 + const int kPageScroll = 10; // 10 times wheel scroll + switch (LOWORD(wparam)) { + case SB_LINEUP: // == SB_LINELEFT + wheel_delta = WHEEL_DELTA; + break; + case SB_LINEDOWN: // == SB_LINERIGHT + wheel_delta = -WHEEL_DELTA; + break; + case SB_PAGEUP: + wheel_delta = kPageScroll * WHEEL_DELTA; + break; + case SB_PAGEDOWN: + wheel_delta = -kPageScroll * WHEEL_DELTA; + break; + // TODO(joshia): Handle SB_THUMBPOSITION and SB_THUMBTRACK + // for compeleteness + default: + break; + } + + // Windows sends the following messages for tilt-wheel events. + // * Tilt a mousewheel (left) + // message == WM_HSCROLL, wparam == SB_LINELEFT (== SB_LINEUP). + // * Tilt a mousewheel (right) + // message == WM_HSCROLL, wparam == SB_LINERIGHT (== SB_LINEDOWN). + // To convert these events to the shift + mousewheel ones, we do not only + // add a shift but also change the signs of their |wheel_delta| values. + if (WM_HSCROLL == message) { + key_state |= MK_SHIFT; + wheel_delta = -wheel_delta; + } + + // Use GetAsyncKeyState for key state since we are synthesizing + // the input + get_key_state = GetAsyncKeyState; + } else { + + global_x = static_cast<short>(LOWORD(lparam)); + global_y = static_cast<short>(HIWORD(lparam)); + } + + POINT client_point = { global_x, global_y }; + ScreenToClient(hwnd, &client_point); + x = client_point.x; + y = client_point.y; + + // compute the scroll delta based on Raymond Chen's algorithm: + // http://blogs.msdn.com/oldnewthing/archive/2003/08/07/54615.aspx + + static int carryover = 0; + static HWND last_window = NULL; + + if (hwnd != last_window) { + last_window = hwnd; + carryover = 0; + } + + unsigned long scroll_lines = kDefaultScrollLinesPerWheelDelta; + SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scroll_lines, 0); + + // TODO(darin): handle the case where scroll_lines is WHEEL_PAGESIZE + int delta_lines = 0; + if (scroll_lines == 0) { + carryover = 0; + } else { + const int delta = carryover + wheel_delta; + + // see how many lines we should scroll. relies on round-toward-zero. + delta_lines = delta * static_cast<int>(scroll_lines) / WHEEL_DELTA; + + // record the unused portion as the next carryover. + carryover = + delta - delta_lines * WHEEL_DELTA / static_cast<int>(scroll_lines); + } + + // Scroll horizontally if shift is held. WebKit's WebKit/win/WebView.cpp + // does the equivalent. + // TODO(jackson): Support WM_MOUSEHWHEEL = 0x020E event as well. + // (Need a mouse with horizontal scrolling capabilities to test it.) + if (key_state & MK_SHIFT) { + // Scrolling up should move left, scrolling down should move right + delta_x = -delta_lines; + delta_y = 0; + } else { + delta_x = 0; + delta_y = delta_lines; + } + + if (key_state & MK_SHIFT) + modifiers |= SHIFT_KEY; + if (key_state & MK_CONTROL) + modifiers |= CTRL_KEY; + + // Get any additional key states needed + if (get_key_state(VK_MENU) & 0x8000) + modifiers |= (ALT_KEY | META_KEY); +} + +// WebKeyboardEvent ----------------------------------------------------------- + +WebKeyboardEvent::WebKeyboardEvent(HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam) { + modifiers = 0; + system_key = false; + + actual_message.hwnd = hwnd; + actual_message.message = message; + actual_message.wParam = wparam; + actual_message.lParam = lparam; + + key_code = static_cast<int>(wparam); + key_data = static_cast<int>(lparam); + + switch (message) { + case WM_SYSKEYDOWN: + system_key = true; + case WM_KEYDOWN: + type = KEY_DOWN; + break; + case WM_SYSKEYUP: + system_key = true; + case WM_KEYUP: + type = KEY_UP; + break; + case WM_IME_CHAR: + key_data = static_cast<int>(wparam); + type = CHAR; + break; + case WM_SYSCHAR: + system_key = true; + type = CHAR; + case WM_CHAR: + type = CHAR; + break; + default: + NOTREACHED() << "unexpected native message"; + } + + if (GetKeyState(VK_SHIFT) & 0x8000) + modifiers |= SHIFT_KEY; + if (GetKeyState(VK_CONTROL) & 0x8000) + modifiers |= CTRL_KEY; + if (GetKeyState(VK_MENU) & 0x8000) + modifiers |= (ALT_KEY | META_KEY); + + if (LOWORD(lparam) > 1) + modifiers |= IS_AUTO_REPEAT; + + // TODO(darin): figure out if we should set IS_KEYPAD +} diff --git a/webkit/glue/webinputevent.h b/webkit/glue/webinputevent.h new file mode 100644 index 0000000..fad7165 --- /dev/null +++ b/webkit/glue/webinputevent.h @@ -0,0 +1,135 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBINPUTEVENT_H__ +#define WEBKIT_GLUE_WEBINPUTEVENT_H__ + +#include <windows.h> +#include "base/basictypes.h" + +// The classes defined in this file are intended to be used with WebView's +// HandleInputEvent method. These event types are cross-platform; however, +// there are platform-specific constructors that accept native UI events. +// +// The fields of these event classes roughly correspond to the fields required +// by WebCore's platform event classes. + +// WebInputEvent -------------------------------------------------------------- + +class WebInputEvent { + public: + enum Type { + // WebMouseEvent + MOUSE_DOWN, + MOUSE_UP, + MOUSE_MOVE, + MOUSE_LEAVE, + MOUSE_DOUBLE_CLICK, + + // WebMouseWheelEvent + MOUSE_WHEEL, + + // WebKeyboardEvent + KEY_DOWN, + KEY_UP, + CHAR + }; + + enum Modifiers { + // modifiers for all events: + SHIFT_KEY = 1 << 0, + CTRL_KEY = 1 << 1, + ALT_KEY = 1 << 2, + META_KEY = 1 << 3, + + // modifiers for keyboard events: + IS_KEYPAD = 1 << 4, + IS_AUTO_REPEAT = 1 << 5 + }; + + Type type; + int modifiers; +}; + +// WebMouseEvent -------------------------------------------------------------- + +class WebMouseEvent : public WebInputEvent { + public: + // These values defined for WebCore::MouseButton + enum Button { + BUTTON_NONE = -1, + BUTTON_LEFT, + BUTTON_MIDDLE, + BUTTON_RIGHT + }; + + Button button; + int x; + int y; + int global_x; + int global_y; + double timestamp_sec; // Seconds since epoch. + int layout_test_click_count; // Only used during layout tests. + + WebMouseEvent() {} + WebMouseEvent(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); +}; + +// WebMouseWheelEvent --------------------------------------------------------- + +class WebMouseWheelEvent : public WebMouseEvent { + public: + int delta_x; + int delta_y; + + WebMouseWheelEvent() {} + WebMouseWheelEvent(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); +}; + +// WebKeyboardEvent ----------------------------------------------------------- + +class WebKeyboardEvent : public WebInputEvent { + public: + bool system_key; // Set if we receive a SYSKEYDOWN/WM_SYSKEYUP message. + MSG actual_message; // Set to the current keyboard message. + int key_code; + int key_data; + + WebKeyboardEvent() + : system_key(false), + key_code(0), + key_data(0) { + memset(&actual_message, 0, sizeof(actual_message)); + } + + WebKeyboardEvent(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); +}; + + +#endif // WEBKIT_GLUE_WEBINPUTEVENT_H__ diff --git a/webkit/glue/webkit_glue.cc b/webkit/glue/webkit_glue.cc new file mode 100644 index 0000000..82de57d --- /dev/null +++ b/webkit/glue/webkit_glue.cc @@ -0,0 +1,399 @@ +// Copyright 2008, 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 <objidl.h> +#include <mlang.h> + +#include "config.h" +#pragma warning(push, 0) +#include "BackForwardList.h" +#include "Document.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "Frame.h" +#include "HistoryItem.h" +#include "ImageSource.h" +#include "KURL.h" +#include "LogWin.h" +#include "Page.h" +#include "PlatformString.h" +#include "RenderTreeAsText.h" +#include "SharedBuffer.h" +#pragma warning(pop) + +#if USE(V8_BINDING) || USE(JAVASCRIPTCORE_BINDINGS) +#include "JSBridge.h" // for set flags +#endif + +#undef LOG +#undef notImplemented +#include "webkit/glue/webkit_glue.h" + +#include "base/file_version_info.h" +#include "base/string_util.h" +#include "skia/include/SkBitmap.h" +#include "webkit/glue/event_conversion.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/weburlrequest_impl.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webview_impl.h" + +//------------------------------------------------------------------------------ +// webkit_glue impl: + +namespace webkit_glue { + +void SetJavaScriptFlags(const std::wstring& str) { +#if USE(V8_BINDING) || USE(JAVASCRIPTCORE_BINDINGS) + std::string utf8_str = WideToUTF8(str); + WebCore::JSBridge::setFlags(utf8_str.data(), static_cast<int>(utf8_str.size())); +#endif +} + +void SetRecordPlaybackMode(bool value) { +#if USE(V8_BINDING) || USE(JAVASCRIPTCORE_BINDINGS) + WebCore::JSBridge::setRecordPlaybackMode(value); +#endif +} + +static bool layout_test_mode_ = false; + +void SetLayoutTestMode(bool enable) { + layout_test_mode_ = enable; +} + +bool IsLayoutTestMode() { + return layout_test_mode_; +} + +MONITORINFOEX GetMonitorInfoForWindowHelper(HWND window) { + HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); + MONITORINFOEX monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(monitor, &monitorInfo); + return monitorInfo; +} + +IMLangFontLink2* GetLangFontLinkHelper() { + // TODO(hbono): http://b/1072298 Experimentally disabled this code to + // prevent registry leaks caused by this IMLangFontLink2 interface. + // If you find any font-rendering regressions. Please feel free to blame me. + // TODO(hbono): http://b/1072298 The test shell does not use our font metrics + // but it uses its own font metrics which heavily depend on this + // IMLangFontLink2 interface. Even though we should change the test shell to + // use out font metrics and re-baseline such tests, we temporarily let the + // test shell use this interface until we complete the said change. + if (!IsLayoutTestMode()) + return NULL; + + static IMultiLanguage *multi_language = NULL; + + if (!multi_language) { + if (CoCreateInstance(CLSID_CMultiLanguage, + 0, + CLSCTX_ALL, + IID_IMultiLanguage, + reinterpret_cast<void**>(&multi_language)) != S_OK) { + return 0; + } + } + + static IMLangFontLink2* lang_font_link; + if (!lang_font_link) { + if (multi_language->QueryInterface( + IID_IMLangFontLink2, + reinterpret_cast<void**>(&lang_font_link)) != S_OK) { + return 0; + } + } + + return lang_font_link; +} + +std::wstring DumpDocumentText(WebFrame* web_frame) { + WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(web_frame); + WebCore::Frame* frame = webFrameImpl->frame(); + + // We use the document element's text instead of the body text here because + // not all documents have a body, such as XML documents. + return StringToStdWString(frame->document()->documentElement()->innerText()); +} + +std::wstring DumpFramesAsText(WebFrame* web_frame, bool recursive) { + WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(web_frame); + std::wstring result; + + // Add header for all but the main frame. + if (webFrameImpl->GetParent()) { + result.append(L"\n--------\nFrame: '"); + result.append(webFrameImpl->GetName()); + result.append(L"'\n--------\n"); + } + + result.append(DumpDocumentText(web_frame)); + result.append(L"\n"); + + if (recursive) { + WebCore::Frame* child = webFrameImpl->frame()->tree()->firstChild(); + for (; child; child = child->tree()->nextSibling()) { + result.append( + DumpFramesAsText(WebFrameImpl::FromFrame(child), recursive)); + } + } + + return result; +} + +std::wstring DumpRenderer(WebFrame* web_frame) { + WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(web_frame); + WebCore::Frame* frame = webFrameImpl->frame(); + + // This implicitly converts from a DeprecatedString. + return StringToStdWString(WebCore::externalRepresentation(frame->renderer())); +} + +std::wstring DumpFrameScrollPosition(WebFrame* web_frame, bool recursive) { + WebFrameImpl* webFrameImpl = static_cast<WebFrameImpl*>(web_frame); + WebCore::IntSize offset = webFrameImpl->frameview()->scrollOffset(); + std::wstring result; + + if (offset.width() > 0 || offset.height() > 0) { + if (webFrameImpl->GetParent()) { + StringAppendF(&result, L"frame '%s' ", StringToStdWString( + webFrameImpl->frame()->tree()->name()).c_str()); + } + StringAppendF(&result, L"scrolled to %d,%d\n", + offset.width(), offset.height()); + } + + if (recursive) { + WebCore::Frame* child = webFrameImpl->frame()->tree()->firstChild(); + for (; child; child = child->tree()->nextSibling()) { + result.append(DumpFrameScrollPosition(WebFrameImpl::FromFrame(child), + recursive)); + } + } + + return result; +} + +// Returns True if item1 < item2. +static bool HistoryItemCompareLess(PassRefPtr<WebCore::HistoryItem> item1, + PassRefPtr<WebCore::HistoryItem> item2) { + std::wstring target1 = StringToStdWString(item1->target()); + std::wstring target2 = StringToStdWString(item2->target()); + std::transform(target1.begin(), target1.end(), target1.begin(), tolower); + std::transform(target2.begin(), target2.end(), target2.begin(), tolower); + return target1 < target2; +} + +// Writes out a HistoryItem into a string in a readable format. +static void DumpHistoryItem(WebCore::HistoryItem* item, int indent, + bool is_current, std::wstring* result) { + if (is_current) { + result->append(L"curr->"); + result->append(indent - 6, L' '); // 6 == L"curr->".length() + } else { + result->append(indent, L' '); + } + + result->append(StringToStdWString(item->urlString())); + if (!item->target().isEmpty()) { + result->append(L" (in frame \"" + StringToStdWString(item->target()) + + L"\")"); + } + if (item->isTargetItem()) + result->append(L" **nav target**"); + result->append(L"\n"); + + if (item->hasChildren()) { + WebCore::HistoryItemVector children = item->children(); + // Must sort to eliminate arbitrary result ordering which defeats + // reproducible testing. + std::sort(children.begin(), children.end(), HistoryItemCompareLess); + for (unsigned i = 0; i < children.size(); i++) { + DumpHistoryItem(children[i].get(), indent+4, false, result); + } + } +} + +void DumpBackForwardList(WebView* view, void* previous_history_item, + std::wstring* result) { + result->append(L"\n============== Back Forward List ==============\n"); + + WebCore::Frame* frame = + static_cast<WebFrameImpl*>(view->GetMainFrame())->frame(); + WebCore::BackForwardList* list = frame->page()->backForwardList(); + + // Skip everything before the previous_history_item, if it's in the back list. + // If it isn't found, assume it fell off the end, and include everything. + int start_index = -list->backListCount(); + WebCore::HistoryItem* prev_item = + static_cast<WebCore::HistoryItem*>(previous_history_item); + for (int i = -list->backListCount(); i < 0; ++i) { + if (prev_item == list->itemAtIndex(i)) + start_index = i+1; + } + + for (int i = start_index; i < 0; ++i) + DumpHistoryItem(list->itemAtIndex(i), 8, false, result); + + DumpHistoryItem(list->currentItem(), 8, true, result); + + for (int i = 1; i <= list->forwardListCount(); ++i) + DumpHistoryItem(list->itemAtIndex(i), 8, false, result); + + result->append(L"===============================================\n"); +} + +void ResetBeforeTestRun(WebView* view) { + WebFrameImpl* webframe = static_cast<WebFrameImpl*>(view->GetMainFrame()); + WebCore::Frame* frame = webframe->frame(); + + // Reset the main frame name since tests always expect it to be empty. It + // is normally not reset between page loads (even in IE and FF). + if (frame && frame->tree()) + frame->tree()->setName(""); + + // This is papering over b/850700. But it passes a few more tests, so we'll + // keep it for now. + if (frame && frame->scriptBridge()) + frame->scriptBridge()->setEventHandlerLineno(0); + + // Reset the last click information so the clicks generated from previous + // test aren't inherited (otherwise can mistake single/double/triple clicks) + MakePlatformMouseEvent::ResetLastClick(); +} + +#ifndef NDEBUG +// The log macro was having problems due to collisions with WTF, so we just +// code here what that would have inlined. +void DumpLeakedObject(const char* file, int line, const char* object, int count) { + std::string msg = StringPrintf("%s LEAKED %d TIMES", object, count); + AppendToLog(file, line, msg.c_str()); +} +#endif + +void CheckForLeaks() { +#ifndef NDEBUG + int count = WebFrameImpl::live_object_count(); + if (count) + DumpLeakedObject(__FILE__, __LINE__, "WebFrame", count); +#endif +} + +bool DecodeImage(const std::string& image_data, SkBitmap* image) { + RefPtr<WebCore::SharedBuffer> buffer( + new WebCore::SharedBuffer(image_data.data(), + static_cast<int>(image_data.length()))); + WebCore::ImageSource image_source; + image_source.setData(buffer.get(), true); + + if (image_source.frameCount() > 0) { + *image = *reinterpret_cast<SkBitmap*>(image_source.createFrameAtIndex(0)); + return true; + } + // We failed to decode the image. + return false; +} + +// Convert from WebKit types to Glue types and notify the embedder. This should +// not perform complex processing since it may be called a lot. +void NotifyFormStateChanged(const WebCore::Document* document) { + if (!document) + return; + + WebCore::Frame* frame = document->frame(); + if (!frame) + return; + + // Dispatch to the delegate of the view that owns the frame. + WebFrame* webframe = WebFrameImpl::FromFrame(document->frame()); + WebView* webview = webframe->GetView(); + if (!webview) + return; + WebViewDelegate* delegate = webview->GetDelegate(); + if (!delegate) + return; + delegate->OnNavStateChanged(webview); +} + +const std::string& GetDefaultUserAgent() { + static std::string user_agent; + static bool generated_user_agent; + if (!generated_user_agent) { + OSVERSIONINFO info = {0}; + info.dwOSVersionInfoSize = sizeof(info); + GetVersionEx(&info); + + // Get the product name and version, and replace Safari's Version/X string + // with it. This is done to expose our product name in a manner that is + // maximally compatible with Safari, we hope!! + std::string product; +#ifdef CHROME_LAST_MINUTE + FileVersionInfo* version_info = + FileVersionInfo::CreateFileVersionInfoForCurrentModule(); + if (version_info) + product = "Chrome/" + WideToASCII(version_info->product_version()); +#endif + if (product.empty()) + product = "Version/3.1"; + + // Derived from Safari's UA string. + StringAppendF( + &user_agent, + "Mozilla/5.0 (Windows; U; Windows NT %d.%d; en-US) AppleWebKit/525.13" + " (KHTML, like Gecko) %s Safari/525.13", + info.dwMajorVersion, + info.dwMinorVersion, + product.c_str()); + + generated_user_agent = true; + } + + return user_agent; +} + + +void NotifyJSOutOfMemory(WebCore::Frame* frame) { + if (!frame) + return; + + // Dispatch to the delegate of the view that owns the frame. + WebFrame* webframe = WebFrameImpl::FromFrame(frame); + WebView* webview = webframe->GetView(); + if (!webview) + return; + WebViewDelegate* delegate = webview->GetDelegate(); + if (!delegate) + return; + delegate->JSOutOfMemory(); +} + +} // namespace webkit_glue diff --git a/webkit/glue/webkit_glue.h b/webkit/glue/webkit_glue.h new file mode 100644 index 0000000..80249fb --- /dev/null +++ b/webkit/glue/webkit_glue.h @@ -0,0 +1,303 @@ +// Copyright 2008, 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 WEBKIT_GLUE_H__ +#define WEBKIT_GLUE_H__ + +#include <string> +#include <vector> +#ifdef _WIN32 +#include <windows.h> +#endif +#include "base/string16.h" +#include "webkit/glue/webplugin.h" + +// We do not include the header files for these interfaces since this header +// file is included by code in webkit/port +class SharedCursor; +class WebView; +class WebViewDelegate; +class WebRequest; +class WebFrame; +class WebFrameImpl; +class GURL; +struct _NPNetscapeFuncs; +typedef _NPNetscapeFuncs NPNetscapeFuncs; + +#ifdef _WIN32 +struct IMLangFontLink2; +#endif + +namespace WebCore { + +class Document; +class Frame; + +} // namespace WebCore + +class SkBitmap; + +namespace webkit_glue { + +//----------------------------------------------------------------------------- +// Functions implemented by JS engines. +void SetJavaScriptFlags(const std::wstring& flags); +void SetRecordPlaybackMode(bool value); + +//----------------------------------------------------------------------------- +// Functions implemented by WebKit, called by the embedder: + +// Turns on "layout test" mode, which tries to mimic the font and widget sizing +// of the Mac DumpRenderTree. +void SetLayoutTestMode(bool enable); +bool IsLayoutTestMode(); + +#ifdef _WIN32 +// Returns the com object pointer for the FontLink interface. This is the +// default way to do this operation. It can be called directly from +// GetLangFontLink. +IMLangFontLink2* GetLangFontLinkHelper(); + +// Returns the monitor information corresponding to the window. +// This is the default implementation. +MONITORINFOEX GetMonitorInfoForWindowHelper(HWND window); +#endif + +// Returns the text of the document element. +std::wstring DumpDocumentText(WebFrame* web_frame); + +// Returns the text of the document element and optionally its child frames. +// If recursive is false, this is equivalent to DumpDocumentText followed by +// a newline. If recursive is true, it recursively dumps all frames as text. +std::wstring DumpFramesAsText(WebFrame* web_frame, bool recursive); + +// Returns the renderer's description of its tree (its externalRepresentation). +std::wstring DumpRenderer(WebFrame* web_frame); + +// Returns a dump of the scroll position of the webframe. +std::wstring DumpFrameScrollPosition(WebFrame* web_frame, bool recursive); + +// Returns a representation of the back/forward list. +void DumpBackForwardList(WebView* view, void* previous_history_item, + std::wstring* result); + +// Cleans up state left over from the previous test run. +void ResetBeforeTestRun(WebView* view); + +// Returns the user agent. +const std::string& GetDefaultUserAgent(); + +// Creates serialized state for the specified URL. This is a variant of +// HistoryItemToString (in glue_serialize) that is used during session restore +// if the saved state is empty. +std::string CreateHistoryStateForURL(const GURL& url); + +#ifndef NDEBUG +// Checks various important objects to see if there are any in memory, and +// calls AppendToLog with any leaked objects. Designed to be called on shutdown +void CheckForLeaks(); +#endif + +// Decodes the image from the data in |image_data| into |image|. +// Returns false if the image could not be decoded. +bool DecodeImage(const std::string& image_data, SkBitmap* image); + +//----------------------------------------------------------------------------- +// Functions implemented by the embedder, called by WebKit: + +// This function is called to check if the given URL string exists in the +// user's browsing history db. The given URL may NOT be in canonical form and +// it will NOT be null-terminated; use the length instead. This function also +// causes the hostnames' DNS record to be prefetched if is_dns_prefetch_enabled +// is true or document_host matches the URL being checked. The hostname will +// likewise not be null-terminated; use document_host_length instead. +bool HistoryContains(const char16* url, int url_length, + const char* document_host, int document_host_length, + bool is_dns_prefetch_enabled); + +// This function is called to request a prefetch of the DNS resolution for the +// embedded URL's hostname. The given URL may NOT be in canonical form and +// it will NOT be null-terminated; use the length instead. +void DnsPrefetchUrl(const char16* url, int url_length); + +// This function is called to request a prefetch of the entire URL, loading it +// into our cache for (expected) future needs. The given URL may NOT be in +// canonical form and it will NOT be null-terminated; use the length instead. +void PrecacheUrl(const char16* url, int url_length); + +// This function is called to add a line to the application's log file. +void AppendToLog(const char* filename, int line, const char* message); + +// Get the mime type (if any) that is associated with the given file extension. +// Returns true if a corresponding mime type exists. +bool GetMimeTypeFromExtension(std::wstring &ext, std::string *mime_type); + +// Get the mime type (if any) that is associated with the given file. +// Returns true if a corresponding mime type exists. +bool GetMimeTypeFromFile(const std::wstring &file_path, std::string *mime_type); + +// Get the preferred extension (if any) associated with the given mime type. +// Returns true if a corresponding file extension exists. +bool GetPreferredExtensionForMimeType(const std::string& mime_type, + std::wstring *ext); + +#ifdef _WIN32 +// Returns the com object pointer for the FontLink interface +IMLangFontLink2* GetLangFontLink(); +#endif + +// Sets a cookie string for the given URL. The policy_url argument indicates +// the URL of the topmost frame, which may be useful for determining whether or +// not to allow this cookie setting. NOTE: the cookie string is a standard +// cookie string of the form "name=value; option1=x; option2=y" +void SetCookie(const GURL& url, const GURL& policy_url, + const std::string& cookie); + +// Returns all cookies in the form "a=1; b=2; c=3" for the given URL. NOTE: +// this string should not include any options that may have been specified when +// the cookie was set. Semicolons delimit individual cookies in this context. +std::string GetCookies(const GURL& url, const GURL& policy_url); + +// Gather usage statistics from the in-memory cache and inform our host, if +// applicable. +void NotifyCacheStats(); + +// Glue to get resources from the embedder. + +// Gets a localized string given a message id. Returns an empty string if the +// message id is not found. +std::wstring GetLocalizedString(int message_id); + +// Returns the raw data for a resource. This resource must have been +// specified as BINDATA in the relevant .rc file. +std::string GetDataResource(int resource_id); + +#ifdef _WIN32 +// Loads and returns a cursor. +HCURSOR LoadCursor(int cursor_id); +#endif + +// Glue to access the clipboard. + +// Clear the clipboard. It is usually a good idea to clear the clipboard +// before writing content to the clipboard. +void ClipboardClear(); + +// Adds UNICODE and ASCII text to the clipboard. +void ClipboardWriteText(const std::wstring& text); + +// Adds HTML to the clipboard. The url parameter is optional, but especially +// useful if the HTML fragment contains relative links +void ClipboardWriteHTML(const std::wstring& html, const GURL& url); + +// Adds a bookmark to the clipboard +void ClipboardWriteBookmark(const std::wstring& title, const GURL& url); + +// Adds a bitmap to the clipboard +void ClipboardWriteBitmap(const SkBitmap& bitmap); + +// Used by WebKit to determine whether WebKit wrote the clipboard last +void ClipboardWriteWebSmartPaste(); + +// Tests whether the clipboard contains a certain format +bool ClipboardIsFormatAvailable(unsigned int format); + +// Reads UNICODE text from the clipboard, if available. +void ClipboardReadText(std::wstring* result); + +// Reads ASCII text from the clipboard, if available. +void ClipboardReadAsciiText(std::string* result); + +// Reads HTML from the clipboard, if available. +void ClipboardReadHTML(std::wstring* markup, GURL* url); + +// Gets the directory where the application data and libraries exist. This +// may be a versioned subdirectory, or it may be the same directory as the +// GetExeDirectory(), depending on the embedder's implementation. +// Path is an output parameter to receive the path. +// Returns true if successful, false otherwise. +bool GetApplicationDirectory(std::wstring *path); + +// Gets the URL where the inspector's HTML file resides. It must use the +// protocol returned by GetUIResourceProtocol. +GURL GetInspectorURL(); + +// Gets the protocol that is used for all user interface resources, including +// the Inspector. It must end with "-resource". +std::string GetUIResourceProtocol(); + +// Gets the directory where the launching executable resides on disk. +// Path is an output parameter to receive the path. +// Returns true if successful, false otherwise. +bool GetExeDirectory(std::wstring *path); + +// Embedders implement this function to return the list of plugins to Webkit. +bool GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins); + +// Returns true if the plugins run in the same process as the renderer, and +// false otherwise. +bool IsPluginRunningInRendererProcess(); + +#ifdef _WIN32 +// Asks the browser to load the font. +bool EnsureFontLoaded(HFONT font); + +// Returns the monitor information corresponding to the window. +MONITORINFOEX GetMonitorInfoForWindow(HWND window); +#endif + +// Functions implemented by webkit_glue for WebKit ---------------------------- + +// Notifies the embedder that a form element value has changed. The document +// pointer, which MAY BE NULL, indicates the document that owns the form +// element that changed, if any. +void NotifyFormStateChanged(const WebCore::Document* document); + +// Returns a bool indicating if the Null plugin should be enabled or not. +bool IsDefaultPluginEnabled(); + +#ifdef _WIN32 +// Downloads the file specified by the URL. On sucess a WM_COPYDATA message +// will be sent to the caller_window. +bool DownloadUrl(const std::string& url, HWND caller_window); +#endif + +// Returns the plugin finder URL. +bool GetPluginFinderURL(std::string* plugin_finder_url); + +// Returns the locale that this instance of webkit is running as. This is of +// the form language-country (e.g., en-US or pt-BR). +std::wstring GetWebKitLocale(); + +// Notifies the browser that the current page runs out of JS memory. +void NotifyJSOutOfMemory(WebCore::Frame* frame); + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_H__ diff --git a/webkit/glue/webkit_resources.h b/webkit/glue/webkit_resources.h new file mode 100644 index 0000000..075d0fd --- /dev/null +++ b/webkit/glue/webkit_resources.h @@ -0,0 +1,25 @@ +// These values shouldn't clash with other values defined in .rc files. +// TODO(tc): Come up with a way to automate the generation of these +// IDs so they don't collide with other rc files. +#define IDR_BROKENIMAGE 2000 +#define IDR_TICKMARK_DASH 2001 +#define IDR_FEED_PREVIEW 2002 + +#define IDC_ALIAS 2100 +#define IDC_CELL 2101 +#define IDC_COLRESIZE 2102 +#define IDC_COPYCUR 2103 +#define IDC_ROWRESIZE 2104 +#define IDC_VERTICALTEXT 2105 +#define IDC_ZOOMIN 2106 +#define IDC_ZOOMOUT 2107 + +#define IDD_DEFAULT_PLUGIN_INSTALL_DIALOG 2108 +#define IDI_DEFAULT_PLUGIN_ICON 2109 +#define IDB_GET_THE_PLUGIN 2110 +#define IDC_PLUGIN_MAIN_LABEL 2111 +#define IDC_PLUGIN_INSTALL_LABEL 2112 +#define IDC_PLUGIN_URL_LABEL 2113 +#define IDC_PLUGIN_INSTALL_CONFIRMATION_LABEL 2114 +#define IDC_PLUGIN_NAME 2115 +#define IDC_PLUGIN_NAME_VALUE 2116 diff --git a/webkit/glue/webkit_resources.rc b/webkit/glue/webkit_resources.rc new file mode 100644 index 0000000..b71c338 --- /dev/null +++ b/webkit/glue/webkit_resources.rc @@ -0,0 +1,55 @@ +// Resources used by webkit/*. +// +// Paths in this file are relative to SolutionDir. + +#ifdef APSTUDIO_INVOKED + #error // Don't open in the Visual Studio resource editor! +#endif //APSTUDIO_INVOKED + +#include <winuser.h> +#include "webkit\\glue\\webkit_resources.h" + +///////////////////////////////////////////////////////////////////////////// +// +// data resources +// + +IDR_BROKENIMAGE BINDATA "webkit\\glue\\resources\\broken-image.gif" +IDR_TICKMARK_DASH BINDATA "webkit\\glue\\resources\\dash.png" + +IDR_FEED_PREVIEW BINDATA "webkit\\glue\\resources\\feed.html" + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_ALIAS CURSOR "webkit\\glue\\resources\\aliasb.cur" +IDC_CELL CURSOR "webkit\\glue\\resources\\cell.cur" +IDC_COLRESIZE CURSOR "webkit\\glue\\resources\\col_resize.cur" +IDC_COPYCUR CURSOR "webkit\\glue\\resources\\copy.cur" +IDC_ROWRESIZE CURSOR "webkit\\glue\\resources\\row_resize.cur" +IDC_VERTICALTEXT CURSOR "webkit\\glue\\resources\\vertical_text.cur" +IDC_ZOOMIN CURSOR "webkit\\glue\\resources\\zoom_in.cur" +IDC_ZOOMOUT CURSOR "webkit\\glue\\resources\\zoom_out.cur" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog template for the plugin installation dialog. +// +IDD_DEFAULT_PLUGIN_INSTALL_DIALOG DIALOGEX 0, 0, 240, 130 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Plug-in Needed" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "",IDB_GET_THE_PLUGIN,32,100,81,14 + PUSHBUTTON "",IDCANCEL,133,100,74,14 + LTEXT "",IDC_PLUGIN_INSTALL_CONFIRMATION_LABEL,17,50,204,27 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon used by the default plugin window. +// +IDI_DEFAULT_PLUGIN_ICON ICON "webkit\\default_plugin\\default_plugin.ico" diff --git a/webkit/glue/webkit_strings.grd b/webkit/glue/webkit_strings.grd new file mode 100644 index 0000000..d8efc4a --- /dev/null +++ b/webkit/glue/webkit_strings.grd @@ -0,0 +1,284 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- This file contains definitions of resources that will be translated for +each locale. Specifically, these are UI strings that are used by webkit that +need to be translated for each locale.--> + +<!-- Some of these strings and string descriptions were taken from +WebKit/win/WebCoreLocalizedStrings.cpp so we include the original license +below: + +/* + * Copyright (C) 2007 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. + */ +--> + +<grit base_dir="." latest_public_release="0" current_release="1" + source_lang_id="en" enc_check="möl"> + <outputs> + <!-- TODO add each of your output files. Modify the three below, and add + your own for your various languages. See the user's guide + (http://wiki/Main/GritUsersGuide) for more details. + Note that all output references are relative to the output directory + which is specified at build time. --> + <output filename="webkit_strings.h" type="rc_header"> + <emit emit_type='prepend'></emit> + </output> + <output filename="webkit_strings_ar.rc" type="rc_all" lang="ar" /> + <output filename="webkit_strings_bg.rc" type="rc_all" lang="bg" /> + <output filename="webkit_strings_ca.rc" type="rc_all" lang="ca" /> + <output filename="webkit_strings_cs.rc" type="rc_all" lang="cs" /> + <output filename="webkit_strings_da.rc" type="rc_all" lang="da" /> + <output filename="webkit_strings_de.rc" type="rc_all" lang="de" /> + <output filename="webkit_strings_el.rc" type="rc_all" lang="el" /> + <output filename="webkit_strings_en-GB.rc" type="rc_all" lang="en-GB" /> + <output filename="webkit_strings_en-US.rc" type="rc_all" lang="en" /> + <output filename="webkit_strings_es.rc" type="rc_all" lang="es" /> + <output filename="webkit_strings_es-419.rc" type="rc_all" lang="es-419" /> + <output filename="webkit_strings_et.rc" type="rc_all" lang="et" /> + <output filename="webkit_strings_fi.rc" type="rc_all" lang="fi" /> + <output filename="webkit_strings_fil.rc" type="rc_all" lang="fil" /> + <output filename="webkit_strings_fr.rc" type="rc_all" lang="fr" /> + <output filename="webkit_strings_he.rc" type="rc_all" lang="he" /> + <output filename="webkit_strings_hi.rc" type="rc_all" lang="hi" /> + <output filename="webkit_strings_hr.rc" type="rc_all" lang="hr" /> + <output filename="webkit_strings_hu.rc" type="rc_all" lang="hu" /> + <output filename="webkit_strings_id.rc" type="rc_all" lang="id" /> + <output filename="webkit_strings_it.rc" type="rc_all" lang="it" /> + <output filename="webkit_strings_ja.rc" type="rc_all" lang="ja" /> + <output filename="webkit_strings_ko.rc" type="rc_all" lang="ko" /> + <output filename="webkit_strings_lt.rc" type="rc_all" lang="lt" /> + <output filename="webkit_strings_lv.rc" type="rc_all" lang="lv" /> + <output filename="webkit_strings_nl.rc" type="rc_all" lang="nl" /> + <!-- The translation console uses 'no' for Norwegian Bokmål. It should + be 'nb'. --> + <output filename="webkit_strings_nb.rc" type="rc_all" lang="no" /> + <output filename="webkit_strings_pl.rc" type="rc_all" lang="pl" /> + <output filename="webkit_strings_pt-BR.rc" type="rc_all" lang="pt-BR" /> + <output filename="webkit_strings_pt-PT.rc" type="rc_all" lang="pt-PT" /> + <output filename="webkit_strings_ro.rc" type="rc_all" lang="ro" /> + <output filename="webkit_strings_ru.rc" type="rc_all" lang="ru" /> + <output filename="webkit_strings_sk.rc" type="rc_all" lang="sk" /> + <output filename="webkit_strings_sl.rc" type="rc_all" lang="sl" /> + <output filename="webkit_strings_sr.rc" type="rc_all" lang="sr" /> + <output filename="webkit_strings_sv.rc" type="rc_all" lang="sv" /> + <output filename="webkit_strings_th.rc" type="rc_all" lang="th" /> + <output filename="webkit_strings_tr.rc" type="rc_all" lang="tr" /> + <output filename="webkit_strings_uk.rc" type="rc_all" lang="uk" /> + <output filename="webkit_strings_vi.rc" type="rc_all" lang="vi" /> + <output filename="webkit_strings_zh-CN.rc" type="rc_all" lang="zh-CN" /> + <output filename="webkit_strings_zh-TW.rc" type="rc_all" lang="zh-TW" /> + </outputs> + <translations> + <file path="resources/webkit_strings_ar.xtb" lang="ar" /> + <file path="resources/webkit_strings_bg.xtb" lang="bg" /> + <file path="resources/webkit_strings_ca.xtb" lang="ca" /> + <file path="resources/webkit_strings_cs.xtb" lang="cs" /> + <file path="resources/webkit_strings_da.xtb" lang="da" /> + <file path="resources/webkit_strings_de.xtb" lang="de" /> + <file path="resources/webkit_strings_el.xtb" lang="el" /> + <file path="resources/webkit_strings_en-GB.xtb" lang="en-GB" /> + <file path="resources/webkit_strings_es.xtb" lang="es" /> + <file path="resources/webkit_strings_es-419.xtb" lang="es-419" /> + <file path="resources/webkit_strings_et.xtb" lang="et" /> + <file path="resources/webkit_strings_fi.xtb" lang="fi" /> + <file path="resources/webkit_strings_fil.xtb" lang="fil" /> + <file path="resources/webkit_strings_fr.xtb" lang="fr" /> + <file path="resources/webkit_strings_he.xtb" lang="he" /> + <file path="resources/webkit_strings_hi.xtb" lang="hi" /> + <file path="resources/webkit_strings_hr.xtb" lang="hr" /> + <file path="resources/webkit_strings_hu.xtb" lang="hu" /> + <file path="resources/webkit_strings_id.xtb" lang="id" /> + <file path="resources/webkit_strings_it.xtb" lang="it" /> + <file path="resources/webkit_strings_ja.xtb" lang="ja" /> + <file path="resources/webkit_strings_ko.xtb" lang="ko" /> + <file path="resources/webkit_strings_lt.xtb" lang="lt" /> + <file path="resources/webkit_strings_lv.xtb" lang="lv" /> + <file path="resources/webkit_strings_nl.xtb" lang="nl" /> + <file path="resources/webkit_strings_no.xtb" lang="no" /> + <file path="resources/webkit_strings_pl.xtb" lang="pl" /> + <file path="resources/webkit_strings_pt-BR.xtb" lang="pt-BR" /> + <file path="resources/webkit_strings_pt-PT.xtb" lang="pt-PT" /> + <file path="resources/webkit_strings_ro.xtb" lang="ro" /> + <file path="resources/webkit_strings_ru.xtb" lang="ru" /> + <file path="resources/webkit_strings_sk.xtb" lang="sk" /> + <file path="resources/webkit_strings_sl.xtb" lang="sl" /> + <file path="resources/webkit_strings_sr.xtb" lang="sr" /> + <file path="resources/webkit_strings_sv.xtb" lang="sv" /> + <file path="resources/webkit_strings_th.xtb" lang="th" /> + <file path="resources/webkit_strings_tr.xtb" lang="tr" /> + <file path="resources/webkit_strings_uk.xtb" lang="uk" /> + <file path="resources/webkit_strings_vi.xtb" lang="vi" /> + <file path="resources/webkit_strings_zh-CN.xtb" lang="zh-CN" /> + <file path="resources/webkit_strings_zh-TW.xtb" lang="zh-TW" /> + </translations> + <release seq="1"> + <messages> + <!-- TODO add all of your "string table" messages here. Remember to + change nontranslateable parts of the messages into placeholders (using the + <ph> element). You can also use the 'grit add' tool to help you identify + nontranslateable parts and create placeholders for them. --> + + <message name="IDS_SEARCHABLE_INDEX_INTRO" desc="Text that appears at the start of nearly-obsolete web pages in the form of a 'searchable index'."> + This is a searchable index. Enter search keywords: ''' + </message> + <message name="IDS_FORM_SUBMIT_LABEL" desc="Default label for Submit buttons in forms on web pages."> + Submit + </message> + <message name="IDS_FORM_INPUT_ALT" desc="alt text for <input> elements with no alt, title, or value"> + Submit + </message> + <message name="IDS_FORM_RESET_LABEL" desc="default label for Reset buttons in forms on web pages"> + Reset + </message> + <message name="IDS_FORM_FILE_BUTTON_LABEL" desc="title for file button used in HTML forms"> + Choose File + </message> + <message name="IDS_FORM_FILE_NO_FILE_LABEL" desc="text to display in file button used in HTML forms when no file is selected"> + No file chosen + </message> + <message name="IDS_FORM_FILE_NO_FILE_DRAG_LABEL" desc="text to display in file button used in HTML forms when no file is selected to indicate that files can be dragged onto the file button"> + Drag file here + </message> + + <message name="IDS_RECENT_SEARCHES_NONE" desc="Label for only item in menu that appears when clicking on the search field image, when no searches have been performed"> + No recent searches + </message> + <message name="IDS_RECENT_SEARCHES" desc="label for first item in the menu that appears when clicking on the search field image, used as embedded menu title"> + Recent Searches + </message> + <message name="IDS_RECENT_SEARCHES_CLEAR" desc="menu item in Recent Searches menu that empties menu's contents"> + Clear Recent Searches + </message> + + <message name="IDS_IMAGE_TITLE_FOR_FILENAME" desc="window title for a standalone image (uses mutiplication symbol, not x)"> + <ph name="FILENAME">%s<ex>My Cool Image.gif</ex></ph><ph name="WIDTH">%d<ex>400</ex></ph>×<ph name="HEIGHT">%d<ex>600</ex></ph> + </message> + + <message name="IDS_AX_ROLE_WEB_AREA" desc="accessibility role description for web area"> + web area + </message> + <message name="IDS_AX_ROLE_LINK" desc="accessibility role description for link"> + link + </message> + <message name="IDS_AX_ROLE_LIST_MARKER" desc="accessibility role description for list marker"> + list marker + </message> + <message name="IDS_AX_ROLE_IMAGE_MAP" desc="accessibility role description for image map"> + image map + </message> + <message name="IDS_AX_ROLE_HEADING" desc="accessibility role description for headings"> + heading + </message> + + <message name="IDS_AX_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a button is pressed, as used by accessibility."> + press + </message> + <message name="IDS_AX_RADIO_BUTTON_ACTION_VERB" desc="Verb stating the action that will occur when a radio button is clicked, as used by accessibility."> + select + </message> + <message name="IDS_AX_TEXT_FIELD_ACTION_VERB" desc="Verb stating the action that will occur when a text field is selected, as used by accessibility."> + activate + </message> + <message name="IDS_AX_CHECKED_CHECK_BOX_ACTION_VERB" desc="Verb stating the action that will occur when a checked checkbox is clicked, as used by accessibility."> + uncheck + </message> + <message name="IDS_AX_UNCHECKED_CHECK_BOX_ACTION_VERB" desc="Verb stating the action that will occur when an unchecked checkbox is clicked, as used by accessibility."> + check + </message> + <message name="IDS_AX_LINK_ACTION_VERB" desc="Verb stating the action that will occur when a link is clicked, as used by accessibility."> + jump + </message> + + <message name="IDS_KEYGEN_HIGH_GRADE_KEY" desc="High-grade cryptographic key size menu item"> + 2048 (High Grade) + </message> + <message name="IDS_KEYGEN_MED_GRADE_KEY" desc="Medium-grade cryptographic key size menu item"> + 1024 (Medium Grade) + </message> + + <message name="IDS_DEFAULT_PLUGIN_GET_PLUGIN_MSG" desc="Message displayed by the default plugin in its main window"> + <ph name="PLUGIN">$1<ex>Realplayer</ex></ph> plugin is not installed + </message> + + <message name="IDS_DEFAULT_PLUGIN_GET_PLUGIN_MSG_NO_PLUGIN_NAME" desc="Message displayed by the default plugin in its main window when we don't know the plugin name"> + The required plugin is not installed + </message> + + <message name="IDS_DEFAULT_PLUGIN_GET_PLUGIN_MSG_2" desc="Second Message displayed by the default plugin in its main window"> + Click here to download plugin + </message> + + <message name="IDS_DEFAULT_PLUGIN_REFRESH_PLUGIN_MSG" desc="Message displayed by the default plugin to refresh the window after installing the required plugin"> + After installing the plugin, click here to refresh + </message> + + <message name="IDS_DEFAULT_PLUGIN_NO_PLUGIN_AVAILABLE_MSG" desc="Message displayed by the default plugin when no plugin was found for the page."> + No plugin available to display this content + </message> + + <message name="IDS_DEFAULT_PLUGIN_DOWNLOADING_PLUGIN_MSG" desc="Message displayed by the default plugin when a download has been initiated for the third party plugin."> + Downloading plugin... + </message> + + <message name="IDS_DEFAULT_PLUGIN_GET_THE_PLUGIN_BTN_MSG" desc="Message displayed by the default plugin on the button which the user should click to fetch the plugin."> + Get Plugin + </message> + + <message name="IDS_DEFAULT_PLUGIN_CANCEL_PLUGIN_DOWNLOAD_MSG" desc="Message displayed by the default plugin on the button which the user should click to cancel the plugin download."> + Cancel + </message> + + <message name="IDS_DEFAULT_PLUGIN_CONFIRMATION_DIALOG_TITLE" desc="Default plugin confirmation dialog title."> + <ph name="PLUGIN">$1<ex>Realplayer</ex></ph> plugin needed + </message> + + <message name="IDS_DEFAULT_PLUGIN_CONFIRMATION_DIALOG_TITLE_NO_PLUGIN_NAME" desc="Default plugin confirmation dialog title when we don't know the plugin name"> + Additional plugin needed + </message> + + <message name="IDS_DEFAULT_PLUGIN_USER_OPTION_MSG" desc="Message displayed by the default plugin in the install dialog indicating information on the user action."> + Please confirm that you would like to install the <ph name="PLUGIN">$1<ex>realplayer</ex></ph> plugin. You should only install plugins that you trust. + </message> + <message name="IDS_DEFAULT_PLUGIN_USER_OPTION_MSG_NO_PLUGIN_NAME" desc="Message displayed by the default plugin in the install dialog indicating information on the user action when we don't know the plugin name"> + Please confirm that you would like to install this plugin. You should only install plugins that you trust. + </message> + + <message name="IDS_DEFAULT_PLUGIN_USE_OPTION_CONFIRM" desc="Button to confirm installation of plugin"> + Install + </message> + <message name="IDS_DEFAULT_PLUGIN_USE_OPTION_CANCEL" desc="Button to cancel installation of plugin"> + Cancel + </message> + + <message name="IDS_DEFAULT_PLUGIN_DOWNLOAD_FAILED_MSG" desc="Message displayed by the default plugin when download is failed."> + Failed to install plugin from <ph name="URL">$1<ex>http://www.google.com/blablah.exe</ex></ph> + </message> + + <message name="IDS_DEFAULT_PLUGIN_INSTALLATION_FAILED_MSG" desc="Message displayed by the default plugin when installation is failed."> + Plugin installation failed + </message> + </messages> + </release> +</grit> diff --git a/webkit/glue/webplugin.h b/webkit/glue/webplugin.h new file mode 100644 index 0000000..c07c08a --- /dev/null +++ b/webkit/glue/webplugin.h @@ -0,0 +1,170 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBPLUGIN_H__ +#define WEBKIT_GLUE_WEBPLUGIN_H__ + +#include <string> +#include <vector> +#include <windows.h> + +#include "base/basictypes.h" +#include "base/gfx/rect.h" + +typedef struct HWND__* HWND; + +class GURL; +class WebFrame; +class WebPluginResourceClient; + +struct NPObject; + +// Describes a mime type entry for a plugin. +struct WebPluginMimeType { + // The actual mime type. + std::string mime_type; + + // A list of all the file extensions for this mime type. + std::vector<std::string> file_extensions; + + // Description of the mime type. + std::wstring description; +}; + + +// Describes an available NPAPI plugin. +struct WebPluginInfo { + // The name of the plugin (i.e. Flash). + std::wstring name; + + // The path to the dll. + std::wstring file; + + // The version number of the plugin file (may be OS-specific) + std::wstring version; + + // A description of the plugin that we get from it's version info. + std::wstring desc; + + // A list of all the mime types that this plugin supports. + std::vector<WebPluginMimeType> mime_types; +}; + + +// Describes the new location for a plugin window. +struct WebPluginGeometry { + HWND window; + gfx::Rect window_rect; + gfx::Rect clip_rect; + bool visible; +}; + + +enum RoutingStatus { + ROUTED, + NOT_ROUTED, + INVALID_URL, + GENERAL_FAILURE +}; + +// The WebKit side of a plugin implementation. It provides wrappers around +// operations that need to interact with the frame and other WebCore objects. +class WebPlugin { + public: + WebPlugin() { } + virtual ~WebPlugin() { } + + // Called by the plugin delegate to let the WebPlugin know if the plugin is + // windowed (i.e. handle is not NULL) or windowless (handle is NULL). This + // tells the WebPlugin to send mouse/keyboard events to the plugin delegate, + // as well as the information about the HDC for paint operations. + // The pump_messages_event is a event handle which is valid only for + // windowless plugins and is used in NPP_HandleEvent calls to pump messages + // if the plugin enters a modal loop. + virtual void SetWindow(HWND window, HANDLE pump_messages_event) = 0; + // Cancels a pending request. + virtual void CancelResource(int id) = 0; + virtual void Invalidate() = 0; + virtual void InvalidateRect(const gfx::Rect& rect) = 0; + + // Returns the NPObject for the browser's window object. + virtual NPObject* GetWindowScriptNPObject() = 0; + + // Returns the DOM element that loaded the plugin. + virtual NPObject* GetPluginElement() = 0; + + // Cookies + virtual void SetCookie(const GURL& url, + const GURL& policy_url, + const std::string& cookie) = 0; + virtual std::string GetCookies(const GURL& url, + const GURL& policy_url) = 0; + + // Shows a modal HTML dialog containing the given URL. json_arguments are + // passed to the dialog via the DOM 'window.chrome.dialogArguments', and the + // retval is the string returned by 'window.chrome.send("DialogClose", + // retval)'. + virtual void ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval) = 0; + + // When a default plugin has downloaded the plugin list and finds it is + // available, it calls this method to notify the renderer. Also it will update + // the status when user clicks on the plugin to install. + virtual void OnMissingPluginStatus(int status) = 0; + + // Handles GetURL/GetURLNotify/PostURL/PostURLNotify requests initiated + // by plugins. + virtual void HandleURLRequest(const char *method, + bool is_javascript_url, + const char* target, unsigned int len, + const char* buf, bool is_file_data, + bool notify, const char* url, + void* notify_data, bool popups_allowed) = 0; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebPlugin); +}; + +// Simpler version of ResourceHandleClient that lends itself to proxying. +class WebPluginResourceClient { + public: + virtual void WillSendRequest(const GURL& url) = 0; + virtual void DidReceiveResponse(const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified, + bool* cancel) = 0; + virtual void DidReceiveData(const char* buffer, int length) = 0; + virtual void DidFinishLoading() = 0; + virtual void DidFail() = 0; +}; + + +#endif // #ifndef WEBKIT_GLUE_WEBPLUGIN_H__ diff --git a/webkit/glue/webplugin_delegate.h b/webkit/glue/webplugin_delegate.h new file mode 100644 index 0000000..43ab88c --- /dev/null +++ b/webkit/glue/webplugin_delegate.h @@ -0,0 +1,145 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H__ +#define WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/gfx/rect.h" +#include "bindings/npapi.h" + +typedef struct HDC__* HDC; + +class GURL; +class WebPlugin; +struct NPObject; +class WebCursor; +class WebPluginResourceClient; +// This is the interface that a plugin implementation needs to provide. +class WebPluginDelegate { + public: + WebPluginDelegate() {} + + // Initializes the plugin implementation with the given (UTF8) arguments. + // Note that the lifetime of WebPlugin must be longer than this delegate. + // If this function returns false the plugin isn't started and shouldn't be + // called again. If this method succeeds, then the WebPlugin is valid until + // PluginDestroyed is called. + // The load_manually parameter if true indicates that the plugin data would + // be passed from webkit. if false indicates that the plugin should download + // the data. This also controls whether the plugin is instantiated as a full + // page plugin (NP_FULL) or embedded (NP_EMBED) + virtual bool Initialize(const GURL& url, char** argn, char** argv, + int argc, WebPlugin* plugin, bool load_manually) = 0; + + // Called when the WebPlugin is being destroyed. This is a signal to the + // delegate that it should tear-down the plugin implementation and not call + // methods on the WebPlugin again. + virtual void PluginDestroyed() = 0; + + // Update the geometry of the plugin. This is a request to move the plugin, + // relative to its containing window, to the coords given by window_rect. + // Its contents should be clipped to the coords given by clip_rect, which are + // relative to the origin of the plugin window. + virtual void UpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible) = 0; + + // Tells the plugin to paint the damaged rect. The HDC is only used for + // windowless plugins. + virtual void Paint(HDC hdc, const gfx::Rect& rect) = 0; + + // Tells the plugin to print itself. + virtual void Print(HDC hdc) = 0; + + // Informs the plugin that it now has focus. + virtual void SetFocus() = 0; + + // For windowless plugins, gives them a user event like mouse/keyboard. + // Returns whether the event was handled. + virtual bool HandleEvent(NPEvent* event, WebCursor* cursor) = 0; + + // Gets the NPObject associated with the plugin for scripting. + virtual NPObject* GetPluginScriptableObject() = 0; + + // Receives notification about a resource load that the plugin initiated + // for a frame. + virtual void DidFinishLoadWithReason(NPReason reason) = 0; + + // Returns the process id of the process that is running the plugin. + virtual int GetProcessId() = 0; + + // Returns the window handle for this plugin if it's a windowed plugin, + // or NULL otherwise. + virtual HWND GetWindowHandle() = 0; + + virtual void FlushGeometryUpdates() = 0; + + // The result of the script execution is returned via this function. + virtual void SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, bool notify_needed, + int notify_data) = 0; + + // Receives notification about data being available. + virtual void DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified) = 0; + + // Receives the data. + virtual void DidReceiveManualData(const char* buffer, int length) = 0; + + // Indicates end of data load. + virtual void DidFinishManualLoading() = 0; + + // Indicates a failure in data receipt. + virtual void DidManualLoadFail() = 0; + + // Only Available after Initialize is called. + virtual std::wstring GetPluginPath() = 0; + + // Only Supported when the plugin is the default plugin. + virtual void InstallMissingPlugin() = 0; + + // Creates a WebPluginResourceClient instance and returns the same. + virtual WebPluginResourceClient* CreateResourceClient(int resource_id, + const std::string &url, + bool notify_needed, + void *notify_data) = 0; + // Notifies the delegate about a Get/Post URL request getting routed + virtual void URLRequestRouted(const std::string&url, bool notify_needed, + void* notify_data) = 0; + private: + DISALLOW_EVIL_CONSTRUCTORS(WebPluginDelegate); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H__ diff --git a/webkit/glue/webplugin_impl.cc b/webkit/glue/webplugin_impl.cc new file mode 100644 index 0000000..421c91f --- /dev/null +++ b/webkit/glue/webplugin_impl.cc @@ -0,0 +1,1059 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "Document.h" +#include "Element.h" +#include "Event.h" +#include "EventNames.h" +#include "FormData.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLPluginElement.h" +#include "IntRect.h" +#include "KURL.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "PlatformString.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceResponse.h" +#include "ScrollView.h" +#include "Widget.h" +#pragma warning(pop) +#undef LOG + +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "net/base/escape.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webplugin_impl.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/webview_impl.h" +#include "googleurl/src/gurl.h" +#include "webkit/port/platform/cursor.h" + +WebPluginContainer::WebPluginContainer(WebPluginImpl* impl) : impl_(impl) { } + +WebPluginContainer::~WebPluginContainer() { + impl_->SetContainer(NULL); + MessageLoop::current()->DeleteSoon(FROM_HERE, impl_); +} + +NPObject* WebPluginContainer::GetPluginScriptableObject() { + return impl_->GetPluginScriptableObject(); +} + +WebCore::IntRect WebPluginContainer::windowClipRect() const { + return impl_->windowClipRect(); +} + +void WebPluginContainer::geometryChanged() const { + impl_->geometryChanged(); +} + +void WebPluginContainer::setFrameGeometry(const WebCore::IntRect& rect) { + WebCore::Widget::setFrameGeometry(rect); + impl_->setFrameGeometry(rect); +} + +void WebPluginContainer::paint(WebCore::GraphicsContext* gc, + const WebCore::IntRect& damage_rect) { + // In theory, we should call impl_->print(gc); when + // impl_->webframe_->printing() is true but it still has placement issues so + // keep that code off for now. + impl_->paint(gc, damage_rect); +} + +void WebPluginContainer::setFocus() { + WebCore::Widget::setFocus(); + impl_->setFocus(); +} + +void WebPluginContainer::show() { + // We don't want to force a geometry update when the plugin widget is + // already visible as this involves a geometry update which may lead + // to unnecessary window moves in the plugin process. The only case + // where this does not apply is if the force_geometry_update_ flag + // is set, which occurs when a plugin is created and does not have + // a parent. We can send out geometry updates only when the plugin + // widget has a parent. + if (!impl_->visible_ || impl_->force_geometry_update_) { + impl_->show(); + WebCore::Widget::show(); + // This is to force an updategeometry call to the plugin process + // where the plugin window can be hidden or shown. + geometryChanged(); + } +} + +void WebPluginContainer::hide() { + // Please refer to WebPluginContainer::show for the reasoning behind + // the if check below. + if (impl_->visible_ || impl_->force_geometry_update_) { + impl_->hide(); + WebCore::Widget::hide(); + // This is to force an updategeometry call to the plugin process + // where the plugin window can be hidden or shown. + geometryChanged(); + } +} + +void WebPluginContainer::handleEvent(WebCore::Event* event) { + impl_->handleEvent(event); +} + +void WebPluginContainer::attachToWindow() { + Widget::attachToWindow(); + show(); +} + +void WebPluginContainer::detachFromWindow() { + Widget::detachFromWindow(); + hide(); +} + +void WebPluginContainer::didReceiveResponse( + const WebCore::ResourceResponse& response) { + + std::wstring url = webkit_glue::StringToStdWString(response.url().string()); + std::string ascii_url = WideToASCII(url); + + std::wstring mime_type(webkit_glue::StringToStdWString(response.mimeType())); + + uint32 last_modified = static_cast<uint32>(response.lastModifiedDate()); + uint32 expected_length = + static_cast<uint32>(response.expectedContentLength()); + WebCore::String content_encoding = + response.httpHeaderField("Content-Encoding"); + if (!content_encoding.isNull() && content_encoding != "identity") { + // Don't send the compressed content length to the plugin, which only + // cares about the decoded length. + expected_length = 0; + } + + impl_->delegate_->DidReceiveManualResponse( + ascii_url, WideToNativeMB(mime_type), + WideToNativeMB(impl_->GetAllHeaders(response)), + expected_length, last_modified); +} + +void WebPluginContainer::didReceiveData(const char *buffer, int length) { + impl_->delegate_->DidReceiveManualData(buffer, length); +} + +void WebPluginContainer::didFinishLoading() { + impl_->delegate_->DidFinishManualLoading(); +} + +void WebPluginContainer::didFail(const WebCore::ResourceError&) { + impl_->delegate_->DidManualLoadFail(); +} + +WebCore::Widget* WebPluginImpl::Create(const GURL& url, + char** argn, + char** argv, + int argc, + WebCore::Element *element, + WebFrameImpl *frame, + WebPluginDelegate* delegate, + bool load_manually) { + WebPluginImpl* webplugin = new WebPluginImpl(element, frame, delegate); + + if (!delegate->Initialize(url, argn, argv, argc, webplugin, load_manually)) { + delegate->PluginDestroyed(); + delegate = NULL; + delete webplugin; + return NULL; + } + + WebPluginContainer* container = new WebPluginContainer(webplugin); + webplugin->SetContainer(container); + return container; +} + +WebPluginImpl::WebPluginImpl(WebCore::Element* element, + WebFrameImpl* webframe, + WebPluginDelegate* delegate) + : element_(element), + webframe_(webframe), + delegate_(delegate), + windowless_(false), + window_(NULL), + force_geometry_update_(false), + visible_(false), + widget_(NULL), + received_first_paint_notification_(false) { +} + +WebPluginImpl::~WebPluginImpl() { +} + +void WebPluginImpl::SetWindow(HWND window, HANDLE pump_messages_event) { + if (window) { + DCHECK(!windowless_); // Make sure not called twice. + window_ = window; + } else { + DCHECK(!window_); // Make sure not called twice. + windowless_ = true; + } +} + +bool WebPluginImpl::CompleteURL(const std::string& url_in, + std::string* url_out) { + if (!frame() || !frame()->document()) { + NOTREACHED(); + return false; + } + + WebCore::String str(webkit_glue::StdStringToString(url_in)); + WebCore::String url = frame()->document()->completeURL(str); + std::wstring wurl = webkit_glue::StringToStdWString(url); + *url_out = WideToUTF8(wurl); + return true; +} + +bool WebPluginImpl::ExecuteScript(const std::string& url, + const std::wstring& script, + bool notify_needed, + int notify_data, + bool popups_allowed) { + // This could happen if the WebPluginContainer was already deleted. + if (!frame()) + return false; + + // Pending resource fetches should also not trigger a callback. + webframe_->set_plugin_delegate(NULL); + + WebCore::String script_str(webkit_glue::StdWStringToString(script)); + + // Note: the call to executeScript might result in the frame being + // deleted, so add an extra reference to it in this scope. + // For KJS, keeping a pointer to the JSBridge is enough, but for V8 + // we also need to addref the frame. + WTF::RefPtr<WebCore::Frame> cur_frame(frame()); + WebCore::JSBridge* bridge = cur_frame->scriptBridge(); + + bool succ = false; + WebCore::String result_str = frame()->loader()->executeScript(script_str, + &succ, + popups_allowed); + std::wstring result; + if (succ) + result = webkit_glue::StringToStdWString(result_str); + + // delegate_ could be NULL because executeScript caused the container to be + // deleted. + if (delegate_) + delegate_->SendJavaScriptStream(url, result, succ, notify_needed, + notify_data); + + return succ; +} + +void WebPluginImpl::CancelResource(int id) { + for (size_t i = 0; i < clients_.size(); ++i) { + if (clients_[i].id == id) { + if (clients_[i].handle) { + clients_[i].handle->cancel(); + RemoveClient(i); + } + + return; + } + } +} + +bool WebPluginImpl::SetPostData(WebCore::ResourceRequest* request, + const char *buf, + uint32 length) { + std::vector<std::string> names; + std::vector<std::string> values; + std::vector<char> body; + bool rv = NPAPI::PluginHost::SetPostData(buf, length, &names, &values, &body); + + for (size_t i = 0; i < names.size(); ++i) + request->addHTTPHeaderField(webkit_glue::StdStringToString(names[i]), + webkit_glue::StdStringToString(values[i])); + + WebCore::FormData *data = new WebCore::FormData(); + if (body.size()) + data->appendData(&body.front(), body.size()); + + request->setHTTPBody(data); // request refcounts FormData + + return rv; +} + +RoutingStatus WebPluginImpl::RouteToFrame(const char *method, + bool is_javascript_url, + const char* target, unsigned int len, + const char* buf, bool is_file_data, + bool notify, const char* url, + GURL* completeURL) { + // If there is no target, there is nothing to do + if (!target) + return NOT_ROUTED; + + // This could happen if the WebPluginContainer was already deleted. + if (!frame()) + return NOT_ROUTED; + + // Take special action for javascript URLs + WebCore::DeprecatedString str_target = target; + if (is_javascript_url) { + WebCore::Frame *frameTarget = frame()->tree()->find(str_target); + // For security reasons, do not allow javascript on frames + // other than this frame. + if (frameTarget != frame()) { + // FIXME - might be good to log this into a security + // log somewhere. + return ROUTED; + } + + // Route javascript calls back to the plugin. + return NOT_ROUTED; + } + + // If we got this far, we're routing content to a target frame. + // Go fetch the URL. + + WebCore::String complete_url_str = frame()->document()->completeURL( + WebCore::String(url)); + + WebCore::KURL complete_url_kurl(complete_url_str.deprecatedString()); + + if (strcmp(method, "GET") != 0) { + const WebCore::DeprecatedString& protocol_scheme = + complete_url_kurl.protocol(); + // We're only going to route HTTP/HTTPS requests + if ((protocol_scheme != "http") && (protocol_scheme != "https")) + return INVALID_URL; + } + + // url.deprecatedString()); + *completeURL = webkit_glue::KURLToGURL(complete_url_kurl); + WebCore::ResourceRequest request(complete_url_kurl); + request.setHTTPMethod(method); + if (len > 0) { + if (!is_file_data) { + if (!SetPostData(&request, buf, len)) { + // Uhoh - we're in trouble. There isn't a good way + // to recover at this point. Break out. + ASSERT_NOT_REACHED(); + return ROUTED; + } + } else { + // TODO: Support "file" mode. For now, just break out + // since proceeding may do something unintentional. + ASSERT_NOT_REACHED(); + return ROUTED; + } + } + WebCore::FrameLoadRequest load_request(request); + load_request.setFrameName(str_target); + WebCore::FrameLoader *loader = frame()->loader(); + // we actually don't know whether usergesture is true or false, + // passing true since all we can do is assume it is okay. + loader->load(load_request, + false, // lock history + true, // user gesture + 0, // event + 0, // form element + HashMap<WebCore::String, WebCore::String>()); + + // load() can cause the frame to go away. + if (webframe_) { + WebPluginDelegate* last_plugin = webframe_->plugin_delegate(); + if (last_plugin) { + last_plugin->DidFinishLoadWithReason(NPRES_USER_BREAK); + webframe_->set_plugin_delegate(NULL); + } + + if (notify) + webframe_->set_plugin_delegate(delegate_); + } + + return ROUTED; +} + +NPObject* WebPluginImpl::GetWindowScriptNPObject() { + if (!frame()) { + ASSERT_NOT_REACHED(); + return 0; + } + + return frame()->windowScriptNPObject(); +} + +NPObject* WebPluginImpl::GetPluginElement() { + // We don't really know that this is a + // HTMLPluginElement. Cast to it and hope? + WebCore::HTMLPlugInElement *plugin_element = + static_cast<WebCore::HTMLPlugInElement*>(element_); + return plugin_element->getNPObject(); +} + +void WebPluginImpl::SetCookie(const GURL& url, + const GURL& policy_url, + const std::string& cookie) { + webkit_glue::SetCookie(url, policy_url, cookie); +} + +std::string WebPluginImpl::GetCookies(const GURL& url, const GURL& policy_url) { + return webkit_glue::GetCookies(url, policy_url); +} + +void WebPluginImpl::ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval) { + // TODO(mpcomplete): Figure out how to call out to the RenderView and + // implement this. Though, this is never called atm - only the out-of-process + // version is used. + NOTREACHED(); +} + +void WebPluginImpl::OnMissingPluginStatus(int status) { + NOTREACHED(); +} + +void WebPluginImpl::Invalidate() { + if (widget_) + widget_->invalidate(); +} + +void WebPluginImpl::InvalidateRect(const gfx::Rect& rect) { + if (widget_) + widget_->invalidateRect(WebCore::IntRect(rect.ToRECT())); +} + +WebCore::IntRect WebPluginImpl::windowClipRect() const { + // This is based on the code in WebCore/plugins/win/PluginViewWin.cpp: + WebCore::IntRect rect(0, 0, widget_->width(), widget_->height()); + + // Start by clipping to our bounds. + WebCore::IntRect clip_rect = widget_->convertToContainingWindow( + WebCore::IntRect(0, 0, widget_->width(), widget_->height())); + + // Take our element and get the clip rect from the enclosing layer and + // frame view. + WebCore::RenderLayer* layer = element_->renderer()->enclosingLayer(); + + // document()->renderer() can be NULL when we receive messages from the + // plugins while we are destroying a frame. + if (element_->renderer()->document()->renderer()) { + WebCore::FrameView* parent_view = element_->document()->view(); + clip_rect.intersect(parent_view->windowClipRectForLayer(layer, true)); + } + + return clip_rect; +} + +void WebPluginImpl::geometryChanged() const { + if (!widget_) + return; + + // This is a hack to tickle re-positioning of the plugin in the case where + // our parent view was scrolled. + const_cast<WebPluginImpl*>(this)->widget_->setFrameGeometry( + widget_->frameGeometry()); + } + +void WebPluginImpl::setFrameGeometry(const WebCore::IntRect& rect) { + // Compute a new position and clip rect for ourselves relative to the + // containing window. We ask our delegate to reposition us accordingly. + + // When the plugin is loaded we don't have a parent frame yet. We need + // to force the plugin window to get created in the plugin process, + // when the plugin widget position is updated. This occurs just after + // the plugin is loaded (See http://b/issue?id=892174). + if (!parent()) { + force_geometry_update_ = true; + return; + } + + WebCore::Frame* frame = element_->document()->frame(); + WebFrameImpl* webframe = WebFrameImpl::FromFrame(frame); + WebViewImpl* webview = webframe->webview_impl(); + // It is valid for this function to be invoked in code paths where the + // the webview is closed. + if (!webview->delegate()) { + return; + } + + WebCore::IntRect window_rect; + WebCore::IntRect clip_rect; + CalculateBounds(rect, &window_rect, &clip_rect); + + if (window_ && received_first_paint_notification_) { + // Let the WebViewDelegate know that the plugin window needs to be moved, + // so that all the HWNDs are moved together. + WebPluginGeometry move; + move.window = window_; + move.window_rect = gfx::Rect(window_rect); + move.clip_rect = gfx::Rect(clip_rect); + move.visible = visible_; + webview->delegate()->DidMove(webview, move); + } + + delegate_->UpdateGeometry( + gfx::Rect(window_rect), gfx::Rect(clip_rect), + received_first_paint_notification_? visible_ : false); + + // delegate_ can go away as a result of above call, so check it first. + if (force_geometry_update_ && delegate_) { + force_geometry_update_ = false; + delegate_->FlushGeometryUpdates(); + } +} + +void WebPluginImpl::paint(WebCore::GraphicsContext* gc, + const WebCore::IntRect& damage_rect) { + if (gc->paintingDisabled()) + return; + + if (!parent()) + return; + + // Don't paint anything if the plugin doesn't intersect the damage rect. + if (!widget_->frameGeometry().intersects(damage_rect)) + return; + + // A windowed plugin starts out by being invisible regardless of the style + // which webkit tells us. The paint notification from webkit indicates that + // the plugin widget is being shown and we need to make sure that + // it becomes visible. + // Please refer to https://bugs.webkit.org/show_bug.cgi?id=18901 for more + // details on this issue. + // TODO(iyengar): Remove this hack when this issue is fixed in webkit. + if (!received_first_paint_notification_) { + received_first_paint_notification_ = true; + + if (!windowless_) { + WebCore::IntRect window_rect; + WebCore::IntRect clip_rect; + + CalculateBounds(widget_->frameGeometry(), &window_rect, &clip_rect); + + delegate_->UpdateGeometry(gfx::Rect(window_rect), gfx::Rect(clip_rect), + visible_); + delegate_->FlushGeometryUpdates(); + } + } + + gc->save(); + + DCHECK(parent()->isFrameView()); + WebCore::FrameView* view = static_cast<WebCore::FrameView*>(parent()); + + // The plugin is positioned in window coordinates, so it needs to be painted + // in window coordinates. + WebCore::IntPoint origin = view->windowToContents(WebCore::IntPoint(0, 0)); + gc->translate(static_cast<float>(origin.x()), + static_cast<float>(origin.y())); + + // HDC is only used when in windowless mode. + HDC hdc = gc->getWindowsContext(); + + WebCore::IntRect window_rect = + WebCore::IntRect(view->contentsToWindow(damage_rect.location()), + damage_rect.size()); + + delegate_->Paint(hdc, gfx::Rect(window_rect)); + + gc->releaseWindowsContext(hdc); + gc->restore(); +} + +void WebPluginImpl::print(WebCore::GraphicsContext* gc) { + if (gc->paintingDisabled()) + return; + + if (!parent()) + return; + + gc->save(); + HDC hdc = gc->getWindowsContext(); + delegate_->Print(hdc); + gc->releaseWindowsContext(hdc); + gc->restore(); +} + +void WebPluginImpl::setFocus() { + if (windowless_) + delegate_->SetFocus(); +} + +void WebPluginImpl::show() { + visible_ = true; +} + +void WebPluginImpl::hide() { + visible_ = false; +} + +void WebPluginImpl::handleEvent(WebCore::Event* event) { + if (!windowless_) + return; + + // Pass events to the plugin. + // The events we pass are defined at: + // http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/structures5.html#1000000 + // Don't take the documentation as truth, however. I've found + // many cases where mozilla behaves differently than the spec. + if (event->isMouseEvent()) + handleMouseEvent(static_cast<WebCore::MouseEvent*>(event)); + else if (event->isKeyboardEvent()) + handleKeyboardEvent(static_cast<WebCore::KeyboardEvent*>(event)); +} + +void WebPluginImpl::handleMouseEvent(WebCore::MouseEvent* event) { + DCHECK(parent()->isFrameView()); + WebCore::IntPoint p = + static_cast<WebCore::FrameView*>(parent())->contentsToWindow( + WebCore::IntPoint(event->pageX(), event->pageY())); + NPEvent np_event; + np_event.lParam = static_cast<uint32>(MAKELPARAM(p.x(), p.y())); + np_event.wParam = 0; + + if (event->ctrlKey()) + np_event.wParam |= MK_CONTROL; + if (event->shiftKey()) + np_event.wParam |= MK_SHIFT; + + if ((event->type() == WebCore::EventNames::mousemoveEvent) || + (event->type() == WebCore::EventNames::mouseoutEvent) || + (event->type() == WebCore::EventNames::mouseoverEvent)) { + np_event.event = WM_MOUSEMOVE; + if (event->buttonDown()) { + switch (event->button()) { + case WebCore::LeftButton: + np_event.wParam |= MK_LBUTTON; + break; + case WebCore::MiddleButton: + np_event.wParam |= MK_MBUTTON; + break; + case WebCore::RightButton: + np_event.wParam |= MK_RBUTTON; + break; + } + } + } else if (event->type() == WebCore::EventNames::mousedownEvent) { + // Ensure that the frame containing the plugin has focus. + WebCore::Frame* containing_frame = webframe_->frame(); + if (WebCore::Page* current_page = containing_frame->page()) { + current_page->focusController()->setFocusedFrame(containing_frame); + } + // Give focus to our containing HTMLPluginElement. + containing_frame->document()->setFocusedNode(element_); + + // Ideally we'd translate to WM_xBUTTONDBLCLK here if the click count were + // a multiple of 2. But there seems to be no way to get at the click count + // or the original Windows message from the WebCore::Event. + switch (event->button()) { + case WebCore::LeftButton: + np_event.event = WM_LBUTTONDOWN; + np_event.wParam |= MK_LBUTTON; + break; + case WebCore::MiddleButton: + np_event.event = WM_MBUTTONDOWN; + np_event.wParam |= MK_MBUTTON; + break; + case WebCore::RightButton: + np_event.event = WM_RBUTTONDOWN; + np_event.wParam |= MK_RBUTTON; + break; + } + } else if (event->type() == WebCore::EventNames::mouseupEvent) { + switch (event->button()) { + case WebCore::LeftButton: + np_event.event = WM_LBUTTONUP; + break; + case WebCore::MiddleButton: + np_event.event = WM_MBUTTONUP; + break; + case WebCore::RightButton: + np_event.event = WM_RBUTTONUP; + break; + } + } else { + // Skip all other mouse events. + return; + } + + // TODO(pkasting): http://b/1119691 This conditional seems exactly backwards, + // but it matches Safari's code, and if I reverse it, giving focus to a + // transparent (windowless) plugin fails. + WebCursor current_web_cursor; + if (!delegate_->HandleEvent(&np_event, ¤t_web_cursor)) + event->setDefaultHandled(); + // A windowless plugin can change the cursor in response to the WM_MOUSEMOVE + // event. We need to reflect the changed cursor in the frame view as the + // the mouse is moved in the boundaries of the windowless plugin. + parent()->setCursor(current_web_cursor); +} + +void WebPluginImpl::handleKeyboardEvent(WebCore::KeyboardEvent* event) { + NPEvent np_event; + np_event.wParam = event->keyCode(); + + if (event->type() == WebCore::EventNames::keydownEvent) { + np_event.event = WM_KEYDOWN; + np_event.lParam = 0; + } else if (event->type() == WebCore::EventNames::keyupEvent) { + np_event.event = WM_KEYUP; + np_event.lParam = 0x8000; + } else { + // Skip all other keyboard events. + return; + } + + // TODO(pkasting): http://b/1119691 See above. + WebCursor current_web_cursor; + if (!delegate_->HandleEvent(&np_event, ¤t_web_cursor)) + event->setDefaultHandled(); +} + +NPObject* WebPluginImpl::GetPluginScriptableObject() { + return delegate_->GetPluginScriptableObject(); +} + +WebPluginResourceClient* WebPluginImpl::GetClientFromHandle( + WebCore::ResourceHandle* handle) { + for (size_t i = 0; i < clients_.size(); ++i) { + if (clients_[i].handle.get() == handle) + return clients_[i].client; + } + + NOTREACHED(); + return 0; +} + + +void WebPluginImpl::willSendRequest(WebCore::ResourceHandle* handle, + WebCore::ResourceRequest& request, + const WebCore::ResourceResponse&) { + WebPluginResourceClient* client = GetClientFromHandle(handle); + if (client) { + GURL gurl(webkit_glue::KURLToGURL(request.url())); + client->WillSendRequest(gurl); + } +} + +std::wstring WebPluginImpl::GetAllHeaders( + const WebCore::ResourceResponse& response) { + std::wstring result; + const WebCore::String& status = response.httpStatusText(); + if (status.isEmpty()) + return result; + + result.append(L"HTTP "); + result.append(FormatNumber(response.httpStatusCode())); + result.append(L" "); + result.append(status.characters(), status.length()); + result.append(L"\n"); + + WebCore::HTTPHeaderMap::const_iterator it = + response.httpHeaderFields().begin(); + for (; it != response.httpHeaderFields().end(); ++it) { + if (!it->first.isEmpty() && !it->second.isEmpty()) { + result.append(std::wstring(it->first.characters(), it->first.length())); + result.append(L": "); + result.append(std::wstring(it->second.characters(), it->second.length())); + result.append(L"\n"); + } + } + + return result; +} + +void WebPluginImpl::didReceiveResponse(WebCore::ResourceHandle* handle, + const WebCore::ResourceResponse& response) { + WebPluginResourceClient* client = GetClientFromHandle(handle); + if (!client) + return; + + bool cancel = false; + std::wstring mime_type(webkit_glue::StringToStdWString(response.mimeType())); + + uint32 last_modified = static_cast<uint32>(response.lastModifiedDate()); + uint32 expected_length = + static_cast<uint32>(response.expectedContentLength()); + WebCore::String content_encoding = + response.httpHeaderField("Content-Encoding"); + if (!content_encoding.isNull() && content_encoding != "identity") { + // Don't send the compressed content length to the plugin, which only + // cares about the decoded length. + expected_length = 0; + } + + client->DidReceiveResponse(WideToNativeMB(mime_type), + WideToNativeMB(GetAllHeaders(response)), + expected_length, + last_modified, + &cancel); + if (cancel) { + handle->cancel(); + RemoveClient(handle); + return; + } + + // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP + // error codes in the stream header and as a result, was unaware of the + // fate of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF + // destroy the stream and invoke the NPP_DestroyStream function on the + // plugin if the HTTP request fails. + const WebCore::DeprecatedString& protocol_scheme = + response.url().protocol(); + if ((protocol_scheme == "http") || (protocol_scheme == "https")) { + if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400) { + // The plugin instance could be in the process of deletion here. + // Verify if the WebPluginResourceClient instance still exists before + // use. + WebPluginResourceClient* resource_client = GetClientFromHandle(handle); + if (resource_client) { + handle->cancel(); + resource_client->DidFail(); + RemoveClient(handle); + } + } + } +} + +void WebPluginImpl::didReceiveData(WebCore::ResourceHandle* handle, + const char *buffer, + int length, int) { + WebPluginResourceClient* client = GetClientFromHandle(handle); + if (client) + client->DidReceiveData(buffer, length); +} + +void WebPluginImpl::didFinishLoading(WebCore::ResourceHandle* handle) { + WebPluginResourceClient* client = GetClientFromHandle(handle); + if (client) + client->DidFinishLoading(); + + RemoveClient(handle); +} + +void WebPluginImpl::didFail(WebCore::ResourceHandle* handle, + const WebCore::ResourceError&) { + WebPluginResourceClient* client = GetClientFromHandle(handle); + if (client) + client->DidFail(); + + RemoveClient(handle); +} + +void WebPluginImpl::RemoveClient(size_t i) { + clients_.erase(clients_.begin() + i); +} + +void WebPluginImpl::RemoveClient(WebCore::ResourceHandle* handle) { + for (size_t i = 0; i < clients_.size(); ++i) { + if (clients_[i].handle.get() == handle) { + RemoveClient(i); + return; + } + } +} + +void WebPluginImpl::SetContainer(WebPluginContainer* container) { + if (container == NULL) { + // The frame maintains a list of JSObjects which are related to this + // plugin. Tell the frame we're gone so that it can invalidate all + // of those sub JSObjects. + if (frame()) { + ASSERT(widget_ != NULL); + frame()->cleanupScriptObjectsForPlugin(widget_); + } + + // Call PluginDestroyed() first to prevent the plugin from calling us back + // in the middle of tearing down the render tree. + delegate_->PluginDestroyed(); + delegate_ = NULL; + + // Cancel any pending requests because otherwise this deleted object will be + // called by the ResourceDispatcher. + int int_offset = 0; + while (!clients_.empty()) { + if (clients_[int_offset].handle) + clients_[int_offset].handle->cancel(); + WebPluginResourceClient* resource_client = clients_[int_offset].client; + RemoveClient(int_offset); + if (resource_client) + resource_client->DidFail(); + } + + // This needs to be called now and not in the destructor since the + // webframe_ might not be valid anymore. + webframe_->set_plugin_delegate(NULL); + webframe_ = NULL; + } + widget_ = container; +} + +WebCore::ScrollView* WebPluginImpl::parent() const { + if (widget_) + return widget_->parent(); + + return NULL; +} + +void WebPluginImpl::CalculateBounds(const WebCore::IntRect& frame_rect, + WebCore::IntRect* window_rect, + WebCore::IntRect* clip_rect) { + DCHECK(parent()->isFrameView()); + WebCore::FrameView* view = static_cast<WebCore::FrameView*>(parent()); + + *window_rect = + WebCore::IntRect(view->contentsToWindow(frame_rect.location()), + frame_rect.size()); + // Calculate a clip-rect so that we don't overlap the scrollbars, etc. + *clip_rect = widget_->windowClipRect(); + clip_rect->move(-window_rect->x(), -window_rect->y()); +} + +void WebPluginImpl::HandleURLRequest(const char *method, + bool is_javascript_url, + const char* target, unsigned int len, + const char* buf, bool is_file_data, + bool notify, const char* url, + void* notify_data, bool popups_allowed) { + // For this request, we either route the output to a frame + // because a target has been specified, or we handle the request + // here, i.e. by executing the script if it is a javascript url + // or by initiating a download on the URL, etc. There is one special + // case in that the request is a javascript url and the target is "_self", + // in which case we route the output to the plugin rather than routing it + // to the plugin's frame. + GURL complete_url; + int routing_status = RouteToFrame(method, is_javascript_url, target, len, + buf, is_file_data, notify, url, + &complete_url); + if (routing_status == ROUTED) { + // The delegate could have gone away because of this call. + if (delegate_) + delegate_->URLRequestRouted(url, notify, notify_data); + return; + } + + if (is_javascript_url) { + std::string original_url = url; + + // Convert the javascript: URL to javascript by unescaping. WebCore uses + // decode_string for this, so we do, too. + std::string escaped_script = original_url.substr(strlen("javascript:")); + WebCore::DeprecatedString script = WebCore::KURL::decode_string( + WebCore::DeprecatedString(escaped_script.data(), + static_cast<int>(escaped_script.length()))); + + ExecuteScript(original_url, + webkit_glue::DeprecatedStringToStdWString(script), notify, + reinterpret_cast<int>(notify_data), popups_allowed); + } else { + std::string complete_url_string; + CompleteURL(url, &complete_url_string); + + int resource_id = GetNextResourceId(); + WebPluginResourceClient* resource_client = + delegate_->CreateResourceClient(resource_id, complete_url_string, + notify, notify_data); + + // If the RouteToFrame call returned a failure then inform the result + // back to the plugin asynchronously. + if ((routing_status == INVALID_URL) || + (routing_status == GENERAL_FAILURE)) { + resource_client->DidFail(); + return; + } + + InitiateHTTPRequest(resource_id, resource_client, method, buf, len, + GURL(complete_url_string)); + } +} + +int WebPluginImpl::GetNextResourceId() { + static int next_id = 0; + return ++next_id; +} + +bool WebPluginImpl::InitiateHTTPRequest(int resource_id, + WebPluginResourceClient* client, + const char* method, const char* buf, + int buf_len, + const GURL& complete_url_string) { + if (!client) { + NOTREACHED(); + return false; + } + + ClientInfo info; + info.id = resource_id; + info.client = client; + info.request.setFrame(frame()); + info.request.setURL(webkit_glue::GURLToKURL(complete_url_string)); + info.request.setOriginPid(delegate_->GetProcessId()); + info.request.setResourceType(ResourceType::OBJECT); + info.request.setHTTPMethod(method); + + const WebCore::String& referrer = frame()->loader()->outgoingReferrer(); + if (!WebCore::FrameLoader::shouldHideReferrer( + complete_url_string.spec().c_str(), referrer)) { + info.request.setHTTPReferrer(referrer); + } + + if (lstrcmpA(method, "POST") == 0) { + // Adds headers or form data to a request. This must be called before + // we initiate the actual request. + SetPostData(&info.request, buf, buf_len); + } + + info.handle = WebCore::ResourceHandle::create(info.request, this, frame(), + false, false); + if (!info.handle) { + return false; + } + + clients_.push_back(info); + return true; +} diff --git a/webkit/glue/webplugin_impl.h b/webkit/glue/webplugin_impl.h new file mode 100644 index 0000000..dd78d53 --- /dev/null +++ b/webkit/glue/webplugin_impl.h @@ -0,0 +1,282 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBPLUGIN_IMPL_H__ +#define WEBKIT_GLUE_WEBPLUGIN_IMPL_H__ + +#include <string> +#include <vector> + +#include "config.h" +#pragma warning(push, 0) +#include "ResourceHandleClient.h" +#include "ResourceRequest.h" +#include "Widget.h" +#pragma warning(pop) + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webplugin_delegate.h" + +class WebFrameImpl; +class WebPluginDelegate; +class WebPluginImpl; + +namespace WebCore { + class DeprecatedString; + class Element; + class Event; + class Frame; + class IntRect; + class KeyboardEvent; + class KURL; + class MouseEvent; + class ResourceHandle; + class ResourceError; + class ResourceResponse; + class String; + class Widget; +} + +// Implements WebCore::Widget functions that WebPluginImpl needs. This class +// exists because it is possible for the plugin widget to be deleted at any +// time because of a delegate javascript call. However we don't want the +// WebPluginImpl to be deleted from under us because it could be lower in the +// call stack. +class WebPluginContainer : public WebCore::Widget { + public: + WebPluginContainer(WebPluginImpl* impl); + virtual ~WebPluginContainer(); + NPObject* GetPluginScriptableObject(); + virtual WebCore::IntRect windowClipRect() const; + virtual void geometryChanged() const; + virtual void setFrameGeometry(const WebCore::IntRect& rect); + virtual void paint(WebCore::GraphicsContext*, const WebCore::IntRect& rect); + virtual void setFocus(); + virtual void show(); + virtual void hide(); + virtual void handleEvent(WebCore::Event* event); + virtual void attachToWindow(); + virtual void detachFromWindow(); + + // These methods are invoked from webkit when it has data to be sent to the + // plugin. The plugin in this case does not initiate a download for the data. + void didReceiveResponse(const WebCore::ResourceResponse& response); + void didReceiveData(const char *buffer, int length); + void didFinishLoading(); + void didFail(const WebCore::ResourceError&); + + private: + WebPluginImpl* impl_; +}; + +// This is the WebKit side of the plugin implementation that forwards calls, +// after changing out of WebCore types, to a delegate. The delegate will +// be in a different process. +class WebPluginImpl : public WebPlugin, + public WebCore::ResourceHandleClient { + public: + // Creates a WebPlugin instance, as long as the delegate's initialization + // succeeds. If it fails, the delegate is deleted and NULL is returned. + // Note that argn and argv are UTF8. + static WebCore::Widget* Create(const GURL& url, + char** argn, + char** argv, + int argc, + WebCore::Element* element, + WebFrameImpl* frame, + WebPluginDelegate* delegate, + bool load_manually); + virtual ~WebPluginImpl(); + + virtual NPObject* GetPluginScriptableObject(); + + // Helper function for sorting post data. + static bool SetPostData(WebCore::ResourceRequest* request, + const char *buf, + uint32 length); + + private: + friend class WebPluginContainer; + + WebPluginImpl(WebCore::Element *element, WebFrameImpl *frame, + WebPluginDelegate* delegate); + + // WebPlugin implementation: + void SetWindow(HWND window, HANDLE pump_messages_event); + + // Given a (maybe partial) url, completes using the base url. + bool CompleteURL(const std::string& url_in, std::string* url_out); + + // Executes the script passed in. The notify_needed and notify_data arguments + // are passed in by the plugin process. These indicate whether the plugin + // expects a notification on script execution. We pass them back to the + // plugin as is. This avoids having to track the notification arguments + // in the plugin process. + bool ExecuteScript(const std::string& url, const std::wstring& script, + bool notify_needed, int notify_data, bool popups_allowed); + + // Given a download request, check if we need to route the output + // to a frame. Returns ROUTED if the load is done and routed to + // a frame, NOT_ROUTED or corresponding error codes otherwise. + RoutingStatus RouteToFrame(const char *method, bool is_javascript_url, + const char* target, unsigned int len, + const char* buf, bool is_file_data, bool notify, + const char* url, GURL* completeURL); + + // Cancels a pending request. + void CancelResource(int id); + + // Returns the next avaiable resource id. + int GetNextResourceId(); + + // Initiates HTTP GET/POST requests. + // Returns true on success. + bool InitiateHTTPRequest(int resource_id, WebPluginResourceClient* client, + const char* method, const char* buf, int buf_len, + const GURL& complete_url_string); + + gfx::Rect GetWindowClipRect(const gfx::Rect& rect); + + NPObject* GetWindowScriptNPObject(); + NPObject* GetPluginElement(); + + void SetCookie(const GURL& url, + const GURL& policy_url, + const std::string& cookie); + std::string GetCookies(const GURL& url, + const GURL& policy_url); + + void ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval); + void OnMissingPluginStatus(int status); + void Invalidate(); + void InvalidateRect(const gfx::Rect& rect); + + // Widget implementation: + virtual WebCore::IntRect windowClipRect() const; + virtual void geometryChanged() const; + + // Override for when our window changes size or position. + // Used to notify the plugin when the size or position changes. + virtual void setFrameGeometry(const WebCore::IntRect& rect); + + // Overrides paint so we can notify the underlying widget to repaint. + virtual void paint(WebCore::GraphicsContext*, const WebCore::IntRect& rect); + virtual void print(WebCore::GraphicsContext*); + + // Override setFocus so we can notify the Plugin. + virtual void setFocus(); + + // Override show and hide to be able to control the visible state of the + // plugin window. + virtual void show(); + virtual void hide(); + + // Handle widget events. + virtual void handleEvent(WebCore::Event* event); + void handleMouseEvent(WebCore::MouseEvent* event); + void handleKeyboardEvent(WebCore::KeyboardEvent* event); + + // Sets the actual Widget for the plugin. + void SetContainer(WebPluginContainer* container); + + WebCore::ScrollView* parent() const; + + // ResourceHandleClient implementation. We implement this interface in the + // renderer process, and then use the simple WebPluginResourceClient interface + // to relay the callbacks to the plugin. + void willSendRequest(WebCore::ResourceHandle* handle, + WebCore::ResourceRequest& request, + const WebCore::ResourceResponse&); + + void didReceiveResponse(WebCore::ResourceHandle* handle, + const WebCore::ResourceResponse& response); + void didReceiveData(WebCore::ResourceHandle* handle, const char *buffer, + int length, int); + void didFinishLoading(WebCore::ResourceHandle* handle); + void didFail(WebCore::ResourceHandle* handle, const WebCore::ResourceError&); + + // Helper function + WebPluginResourceClient* GetClientFromHandle(WebCore::ResourceHandle* handle); + + // Helper function to remove the stored information about a resource + // request given its index in m_clients. + void RemoveClient(size_t i); + + // Helper function to remove the stored information about a resource + // request given a handle. + void RemoveClient(WebCore::ResourceHandle* handle); + + // Returns all the response headers in one string, including the status code. + std::wstring GetAllHeaders(const WebCore::ResourceResponse& response); + + WebCore::Frame* frame() { return webframe_ ? webframe_->frame() : NULL; } + + // Calculates the bounds of the plugin widget based on the frame rect passed in. + void CalculateBounds(const WebCore::IntRect& frame_rect, + WebCore::IntRect* window_rect, + WebCore::IntRect* clip_rect); + + void HandleURLRequest(const char *method, + bool is_javascript_url, + const char* target, unsigned int len, + const char* buf, bool is_file_data, + bool notify, const char* url, + void* notify_data, bool popups_allowed); + + struct ClientInfo { + int id; + WebPluginResourceClient* client; + WebCore::ResourceRequest request; + RefPtr<WebCore::ResourceHandle> handle; + }; + + std::vector<ClientInfo> clients_; + + bool windowless_; + HWND window_; + WebCore::Element* element_; + WebFrameImpl* webframe_; + + WebPluginDelegate* delegate_; + bool force_geometry_update_; + bool visible_; + // Set when we receive the first paint notification for the plugin widget. + bool received_first_paint_notification_; + + WebPluginContainer* widget_; + + DISALLOW_EVIL_CONSTRUCTORS(WebPluginImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBPLUGIN_IMPL_H__ diff --git a/webkit/glue/webplugin_impl_unittest.cc b/webkit/glue/webplugin_impl_unittest.cc new file mode 100644 index 0000000..51a9ea4 --- /dev/null +++ b/webkit/glue/webplugin_impl_unittest.cc @@ -0,0 +1,225 @@ +// Copyright 2008, 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 "testing/gtest/include/gtest/gtest.h" + +#pragma warning(push, 0) +#include "ResourceRequest.h" +#include "CString.h" +#pragma warning(pop) +#undef LOG + +#include "webkit/glue/webplugin_impl.h" + +namespace { + class WebPluginImplTest : public testing::Test { + }; +} + +// These exist only to support the gTest assertion macros, and +// shouldn't be used in normal program code. +std::ostream& operator<<(std::ostream& out, const WebCore::String& str) +{ + return out << str.latin1().data(); +} + +// The Host functions for NPN_PostURL and NPN_PostURLNotify +// need to parse out some HTTP headers. Make sure it works +// with the following tests + +TEST(WebPluginImplTest, PostParserSimple) { + // Test a simple case with headers & data + char *ex1 = "foo: bar\nContent-length: 10\n\nabcdefghij"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace()); + EXPECT_EQ(0, request.httpHeaderField("bar").length()); + EXPECT_EQ(0, request.httpHeaderField("Content-length").length()); + EXPECT_EQ("abcdefghij", request.httpBody()->flattenToString()); +} + +TEST(WebPluginImplTest, PostParserLongHeader) { + // Test a simple case with long headers + char *ex1 = "foo: 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n\nabcdefghij"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ(100, request.httpHeaderField("foo").stripWhiteSpace().length()); +} + +TEST(WebPluginImplTest, PostParserManyHeaders) { + // Test a simple case with long headers + char *ex1 = "h1:h1\nh2:h2\nh3:h3\nh4:h4\nh5:h5\nh6:h6\nh7:h7\nh8:h8\nh9:h9\nh10:h10\n\nbody"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ("h1", request.httpHeaderField("h1").stripWhiteSpace()); + EXPECT_EQ("h2", request.httpHeaderField("h2").stripWhiteSpace()); + EXPECT_EQ("h3", request.httpHeaderField("h3").stripWhiteSpace()); + EXPECT_EQ("h4", request.httpHeaderField("h4").stripWhiteSpace()); + EXPECT_EQ("h5", request.httpHeaderField("h5").stripWhiteSpace()); + EXPECT_EQ("h6", request.httpHeaderField("h6").stripWhiteSpace()); + EXPECT_EQ("h7", request.httpHeaderField("h7").stripWhiteSpace()); + EXPECT_EQ("h8", request.httpHeaderField("h8").stripWhiteSpace()); + EXPECT_EQ("h9", request.httpHeaderField("h9").stripWhiteSpace()); + EXPECT_EQ("h10", request.httpHeaderField("h10").stripWhiteSpace()); + WebCore::FormData *form_data = request.httpBody(); + WebCore::String string_data = form_data->flattenToString(); + EXPECT_EQ(string_data, "body"); +} + +TEST(WebPluginImplTest, PostParserDuplicateHeaders) { + // Test a simple case with long headers + // What value gets returned doesn't really matter. It shouldn't error + // out. + char *ex1 = "h1:h1\nh1:h2\n\nbody"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); +} + +TEST(WebPluginImplTest, PostParserNoHeaders) { + // Test a simple case with no headers but with data + char *ex1 = "\nabcdefghij"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ(0, request.httpHeaderField("foo").length()); + EXPECT_EQ(0, request.httpHeaderField("bar").length()); + EXPECT_EQ(0, request.httpHeaderField("Content-length").length()); + EXPECT_EQ("abcdefghij", request.httpBody()->flattenToString()); +} + +TEST(WebPluginImplTest, PostParserNoBody) { + // Test a simple case with headers and no body + char *ex1 = "Foo:bar\n\n"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace()); + EXPECT_EQ(0, request.httpHeaderField("bar").length()); + EXPECT_EQ(0, request.httpHeaderField("Content-length").length()); + EXPECT_EQ(0, request.httpBody()->flattenToString().length()); +} + +TEST(WebPluginImplTest, PostParserBodyWithNewLines) { + // Test a simple case with headers and no body + char *ex1 = "Foo:bar\n\n\n\nabcdefg\n\nabcdefg"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ(request.httpBody()->flattenToString(), "\n\nabcdefg\n\nabcdefg"); +} + +TEST(WebPluginImplTest, PostParserErrorNoBody) { + // Test an error case with headers and no body + char *ex1 = "Foo:bar\n"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(false, rv); +} + +TEST(WebPluginImplTest, PostParserErrorEmpty) { + // Test an error case with an empty string + char *ex1 = ""; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(false, rv); +} + +TEST(WebPluginImplTest, PostParserEmptyName) { + // Test an error case with an empty header name field + char *ex1 = "foo:bar\n:blat\n\nbody"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace()); + EXPECT_EQ("body", request.httpBody()->flattenToString()); +} + +TEST(WebPluginImplTest, PostParserEmptyValue) { + // Test an error case with an empty value field + char *ex1 = "foo:bar\nbar:\n\nbody"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace()); + EXPECT_EQ(0, request.httpHeaderField("bar").length()); + EXPECT_EQ("body", request.httpBody()->flattenToString()); +} + +TEST(WebPluginImplTest, PostParserCRLF) { + // Test an error case with an empty value field + char *ex1 = "foo: bar\r\nbar:\r\n\r\nbody\r\n\r\nbody2"; + WebCore::ResourceRequest request; + bool rv= WebPluginImpl::SetPostData(&request, ex1, + static_cast<uint32>(strlen(ex1))); + EXPECT_EQ(true, rv); + EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace()); + EXPECT_EQ(0, request.httpHeaderField("bar").length()); + EXPECT_EQ("body\r\n\r\nbody2", request.httpBody()->flattenToString()); +} + +TEST(WebPluginImplTest, PostParserBodyWithBinaryData) { + // Test a simple case with headers and binary data. + char ex1[33] = "foo: bar\nContent-length: 10\n\n"; + unsigned int binary_data = 0xFFFFFFF0; + memcpy(ex1 + strlen("foo: bar\nContent-length: 10\n\n"), &binary_data, + sizeof(binary_data)); + + WebCore::ResourceRequest request; + bool rv = WebPluginImpl::SetPostData(&request, ex1, + sizeof(ex1)/sizeof(ex1[0])); + EXPECT_EQ(true, rv); + EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace()); + EXPECT_EQ(0, request.httpHeaderField("bar").length()); + EXPECT_EQ(0, request.httpHeaderField("Content-length").length()); + + Vector<char> expected_data; + request.httpBody()->flatten(expected_data); + + EXPECT_EQ(0xF0, (unsigned char)expected_data[0]); + EXPECT_EQ(0xFF, (unsigned char)expected_data[1]); + EXPECT_EQ(0xFF, (unsigned char)expected_data[2]); + EXPECT_EQ(0xFF, (unsigned char)expected_data[3]); +}
\ No newline at end of file diff --git a/webkit/glue/webpreferences.h b/webkit/glue/webpreferences.h new file mode 100644 index 0000000..21b5181 --- /dev/null +++ b/webkit/glue/webpreferences.h @@ -0,0 +1,103 @@ +// Copyright 2008, 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. +// +// A struct for managing webkit's settings. +// +// Adding new values to this class probably involves updating +// WebViewImpl::SetPreferences, common/render_messages.h, and +// browser/profile.cc. + +#ifndef WEBKIT_GLUE_WEBPREFERENCES_H__ +#define WEBKIT_GLUE_WEBPREFERENCES_H__ + +#include <string> +#include "googleurl/src/gurl.h" + +struct WebPreferences { + std::wstring standard_font_family; + std::wstring fixed_font_family; + std::wstring serif_font_family; + std::wstring sans_serif_font_family; + std::wstring cursive_font_family; + std::wstring fantasy_font_family; + int default_font_size; + int default_fixed_font_size; + int minimum_font_size; + int minimum_logical_font_size; + std::wstring default_encoding; + bool javascript_enabled; + bool javascript_can_open_windows_automatically; + bool loads_images_automatically; + bool plugins_enabled; + bool dom_paste_enabled; + bool developer_extras_enabled; + bool shrinks_standalone_images_to_fit; + bool uses_universal_detector; + bool text_areas_are_resizable; + bool dashboard_compatibility_mode; + bool java_enabled; + + // TODO(tc): User style sheets will not work in chrome because it tries to + // load the style sheet using a request without a frame. + bool user_style_sheet_enabled; + GURL user_style_sheet_location; + + std::string user_agent; + + // We try to keep the default values the same as the default values in + // chrome, except for the cases where it would require lots of extra work for + // the embedder to use the same default value. + WebPreferences() + : standard_font_family(L"Times New Roman"), + fixed_font_family(L"Courier New"), + serif_font_family(L"Times New Roman"), + sans_serif_font_family(L"Arial"), + cursive_font_family(L"Script"), + fantasy_font_family(), // Not sure what to use on Windows. + default_font_size(16), + default_fixed_font_size(13), + minimum_font_size(1), + minimum_logical_font_size(6), + default_encoding(L"ISO-8859-1"), + javascript_enabled(true), + javascript_can_open_windows_automatically(true), + loads_images_automatically(true), + plugins_enabled(true), + dom_paste_enabled(false), // enables execCommand("paste") + developer_extras_enabled(false), // Requires extra work by embedder + shrinks_standalone_images_to_fit(true), + uses_universal_detector(false), // Disabled: page cycler regression + user_style_sheet_enabled(false), + text_areas_are_resizable(true), + dashboard_compatibility_mode(false), + java_enabled(true) { + } +}; + +#endif // WEBKIT_GLUE_WEBPREFERENCES_H__ diff --git a/webkit/glue/webresponse.h b/webkit/glue/webresponse.h new file mode 100644 index 0000000..e83c08b --- /dev/null +++ b/webkit/glue/webresponse.h @@ -0,0 +1,57 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBURLRESPONSE_H__ +#define WEBKIT_GLUE_WEBURLRESPONSE_H__ + +#include <string> + +class GURL; + +class WebResponse { + public: + // Get the URL. + virtual GURL GetURL() const = 0; + + // Get the http status code. + virtual int GetHttpStatusCode() const = 0; + + // Returns an opaque value containing the state of the SSL connection that + // the resource was loaded on, or an empty string if no SSL connection was + // used. + virtual std::string GetSecurityInfo() const = 0; + + WebResponse() { } + virtual ~WebResponse() { } + +private: + DISALLOW_EVIL_CONSTRUCTORS(WebResponse); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBURLRESPONSE_H__ diff --git a/webkit/glue/webresponse_impl.h b/webkit/glue/webresponse_impl.h new file mode 100644 index 0000000..3e2c356 --- /dev/null +++ b/webkit/glue/webresponse_impl.h @@ -0,0 +1,72 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBRESPONSEIMPL_H__ +#define WEBKIT_GLUE_WEBRESPONSEIMPL_H__ + +#include <string> + +#include "googleurl/src/gurl.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webresponse.h" + +#include "ResourceResponse.h" + +class WebResponseImpl : public WebResponse { + public: + WebResponseImpl() { } + explicit WebResponseImpl(const WebCore::ResourceResponse& response) + : response_(response) { } + + virtual ~WebResponseImpl() { } + + // Get the URL. + virtual GURL GetURL() const { + return webkit_glue::KURLToGURL(response_.url()); + } + + // Get the http status code. + virtual int GetHttpStatusCode() const { return response_.httpStatusCode(); } + + // Get the security info (state of the SSL connection). + virtual std::string GetSecurityInfo() const { + return webkit_glue::CStringToStdString(response_.getSecurityInfo()); + } + + void set_resource_response(const WebCore::ResourceResponse& response) { + response_ = response; + } + + private: + WebCore::ResourceResponse response_; + + DISALLOW_EVIL_CONSTRUCTORS(WebResponseImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBRESPONSEIMPL_H__ diff --git a/webkit/glue/webtextinput.h b/webkit/glue/webtextinput.h new file mode 100644 index 0000000..87582ff --- /dev/null +++ b/webkit/glue/webtextinput.h @@ -0,0 +1,87 @@ +// Copyright 2008, 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. +// +// Class WebTextInput provides text input APIs used by TextInputController +// in test_shell. It only facilitates layout tests and should not be used +// by renderers. + +#ifndef WEBKIT_GLUE_WEBTEXTINPUT_H__ +#define WEBKIT_GLUE_WEBTEXTINPUT_H__ + +#include <string> +#include "base/basictypes.h" + +class WebTextInput { + public: + WebTextInput() {} + virtual ~WebTextInput() {} + + // Inserts text to the associated frame. + virtual void InsertText(std::string& text) = 0; + + // Executes the given editing command on the frame. + virtual void DoCommand(std::string& command) = 0; + + // Sets marked text region on the frame. + virtual void SetMarkedText(std::string& text, + int32_t location, int32_t length) = 0; + + // Clears the marked text region on the frame. + virtual void UnMarkText() = 0; + + // Returns true if there are marked texts on the frame, returns false + // otherwise. + virtual bool HasMarkedText() = 0; + + // Writes the textual representation of the marked range on the frame to + // range_str. + virtual void MarkedRange(std::string* range_str) = 0; + + // Writes the textual representation of the selected range on the frame to + // range_str. + virtual void SelectedRange(std::string* range_str) = 0; + + // Writes the textual representation of the attributes of marked text range + // on the frame to attributes. + virtual void ValidAttributesForMarkedText(std::string* attributes) = 0; + + virtual void ConversationIdentifier() = 0; + virtual void SubstringFromRange(int32_t location, int32_t length) = 0; + virtual void AttributedSubstringFromRange(int32_t location, + int32_t length) = 0; + virtual void FirstRectForCharacterRange(int32_t location, + int32_t length) = 0; + virtual void CharacterIndexForPoint(double x, double y) = 0; + virtual void MakeAttributedString(std::string& str) = 0; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebTextInput); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBTEXTINPUT_H__ diff --git a/webkit/glue/webtextinput_impl.cc b/webkit/glue/webtextinput_impl.cc new file mode 100644 index 0000000..03cb895 --- /dev/null +++ b/webkit/glue/webtextinput_impl.cc @@ -0,0 +1,178 @@ +// Copyright 2008, 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 <ctype.h> +#include "config.h" + +#pragma warning(push, 0) +#include "Frame.h" +#include "Editor.h" +#pragma warning(pop) + +#undef LOG + +#include "base/string_util.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webtextinput_impl.h" + +WebTextInputImpl::WebTextInputImpl(WebFrameImpl* web_frame_impl) + : WebTextInput(), + web_frame_impl_(web_frame_impl) { +} + +WebTextInputImpl::~WebTextInputImpl() { +} + +WebCore::Frame* WebTextInputImpl::GetFrame() { + return web_frame_impl_->frame(); +} + +WebCore::Editor* WebTextInputImpl::GetEditor() { + return web_frame_impl_->frame()->editor(); +} + +void WebTextInputImpl::InsertText(std::string& text) { + WebCore::String str(text.c_str()); + GetEditor()->insertText(str, NULL); +} + +void WebTextInputImpl::DoCommand(std::string& command) { + // Since we don't have NSControl, we will convert the format of command + // string and call the function on Editor directly. + + if (command.length() <= 2) + return; + + // Make sure the first letter is upper case. + command.replace(0, 1, 1, toupper(command.at(0))); + + // Remove the trailing ':' if existing. + if (command.at(command.length() - 1) == ':') + command.erase(command.length() - 1, 1); + + // Specially handling commands that Editor::execCommand does not directly + // support. + if (!command.compare("DeleteToEndOfParagraph")) { + DeleteToEndOfParagraph(); + } else if(!command.compare("Indent")) { + GetEditor()->indent(); + } else if(!command.compare("Outdent")) { + GetEditor()->outdent(); + } else if(!command.compare("DeleteBackward")) { + WebCore::AtomicString editor_command("BackwardDelete"); + GetEditor()->command(editor_command).execute(); + } else if(!command.compare("DeleteForward")) { + WebCore::AtomicString editor_command("ForwardDelete"); + GetEditor()->command(editor_command).execute(); + } else { + WebCore::AtomicString editor_command(command.c_str()); + GetEditor()->command(editor_command).execute(); + } + + return; +} + +void WebTextInputImpl::SetMarkedText(std::string& text, + int32_t location, + int32_t length) { + WebCore::Editor* editor = GetEditor(); + WebCore::String str(text.c_str()); + + editor->confirmComposition(str); + + WebCore::Frame* frame = GetFrame(); + WTF::Vector<WebCore::CompositionUnderline> decorations; + + editor->setComposition(str, decorations, location, length); +} + +void WebTextInputImpl::UnMarkText() { + GetEditor()->confirmCompositionWithoutDisturbingSelection();; +} + +bool WebTextInputImpl::HasMarkedText() { + return GetEditor()->hasComposition(); +} + +void WebTextInputImpl::ConversationIdentifier() { +} + +void WebTextInputImpl::SubstringFromRange(int32_t location, int32_t length) { +} + +void WebTextInputImpl::AttributedSubstringFromRange(int32_t location, + int32_t length) { +} + +void WebTextInputImpl::MarkedRange(std::string* range_str) { + RefPtr<WebCore::Range> range = GetEditor()->compositionRange(); + + // Range::toString() returns a string different from what test expects. + // So we need to construct the string ourselves. + SStringPrintf(range_str, "%d,%d", range->startPosition().offset(), + range->endPosition().offset()); +} + +void WebTextInputImpl::SelectedRange(std::string* range_str) { + WTF::RefPtr<WebCore::Range> range + = GetFrame()->selectionController()->toRange(); + + // Range::toString() returns a string different from what test expects. + // So we need to construct the string ourselves. + SStringPrintf(range_str, "%d,%d", range.get()->startPosition().offset(), + range.get()->endPosition().offset()); +} + +void WebTextInputImpl::FirstRectForCharacterRange(int32_t location, + int32_t length) { +} + +void WebTextInputImpl::CharacterIndexForPoint(double x, double y) { +} + +void WebTextInputImpl::ValidAttributesForMarkedText(std::string* attributes) { + // We simply return a string with relevant keywords. + attributes->assign("NSUnderline,NSUnderlineColor,NSMarkedClauseSegment,NSTextInputReplacementRangeAttributeName"); +} + +void WebTextInputImpl::MakeAttributedString(std::string& str) { +} + +void WebTextInputImpl::DeleteToEndOfParagraph() { + WebCore::Editor* editor = GetEditor(); + if (!editor->deleteWithDirection(WebCore::SelectionController::FORWARD, + WebCore::ParagraphBoundary, + true, + false)) { + editor->deleteWithDirection(WebCore::SelectionController::FORWARD, + WebCore::CharacterGranularity, + true, + false); + } +} diff --git a/webkit/glue/webtextinput_impl.h b/webkit/glue/webtextinput_impl.h new file mode 100644 index 0000000..ee6a147 --- /dev/null +++ b/webkit/glue/webtextinput_impl.h @@ -0,0 +1,81 @@ +// Copyright 2008, 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. +// +// Class WebTextInputImpl provides implementation of WebTextInput which is +// used by TextInputController in test_shell. It only facilitates layout tests +// and should not be used by renderers. + +#ifndef WEBKIT_GLUE_WEBTEXTINPUT_IMPL_H__ +#define WEBKIT_GLUE_WEBTEXTINPUT_IMPL_H__ + +#include "webkit/glue/webtextinput.h" + +class WebFrameImpl; +class WebCore::Editor; + +class WebTextInputImpl : public WebTextInput { + public: + WebTextInputImpl(WebFrameImpl* web_frame_impl); + virtual ~WebTextInputImpl(); + + // WebTextInput methods + virtual void InsertText(std::string& text); + virtual void DoCommand(std::string& command); + virtual void SetMarkedText(std::string& text, + int32_t location, int32_t length); + virtual void UnMarkText(); + virtual bool HasMarkedText(); + virtual void MarkedRange(std::string* range_str); + virtual void SelectedRange(std::string* range_str); + virtual void ValidAttributesForMarkedText(std::string* attributes); + + // TODO(huanr): examine all layout tests involving TextInputController + // and implement these functions if necessary. + virtual void ConversationIdentifier(); + virtual void SubstringFromRange(int32_t location, int32_t length); + virtual void AttributedSubstringFromRange(int32_t location, + int32_t length); + virtual void FirstRectForCharacterRange(int32_t location, + int32_t length); + virtual void CharacterIndexForPoint(double x, double y); + virtual void MakeAttributedString(std::string& str); + + private: + WebCore::Frame* GetFrame(); + WebCore::Editor* GetEditor(); + + void DeleteToEndOfParagraph(); + + // Holding a non-owning pointer to the web frame we are associated with. + WebFrameImpl* web_frame_impl_; + + DISALLOW_EVIL_CONSTRUCTORS(WebTextInputImpl); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBTEXTINPUT_IMPL_H__ diff --git a/webkit/glue/weburlrequest.h b/webkit/glue/weburlrequest.h new file mode 100644 index 0000000..5e7476f --- /dev/null +++ b/webkit/glue/weburlrequest.h @@ -0,0 +1,128 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBURLREQUEST_H__ +#define WEBKIT_GLUE_WEBURLREQUEST_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" + +enum WebRequestCachePolicy { + WebRequestUseProtocolCachePolicy, + WebRequestReloadIgnoringCacheData, + WebRequestReturnCacheDataElseLoad, + WebRequestReturnCacheDataDontLoad +}; + +class GURL; + +class WebRequest { + public: + // Extra information that is associated with a request. The embedder derives + // from this REFERENCE COUNTED class to associated data with a request and + // get it back when the page loads. + // + // Note that for reloads (and possibly things like back/forward), there is no + // way to specify the request that it will use, so the extra data pointer + // will be invalid. Users should always check for NULL. + class ExtraData : public base::RefCounted<ExtraData> { + public: + virtual ~ExtraData() {} + }; + + // Creates a WebRequest. + static WebRequest* Create(const GURL& url); + + // Creates a copy of this WebRequest. + virtual WebRequest* Clone() const = 0; + + // Sets the extra request info that the embedder can retrieve later. This + // will AddRef the ExtraData and store it with the request. + virtual void SetExtraData(ExtraData* extra) = 0; + + // Returns any previously set request info. It does not AddRef, the caller + // is assumed to assign this to a RefPtr. This may return NULL if no extra + // data has been set on this request. Even if the embedder sets request info + // for every request, WebRequests can get created during reload operations + // so callers should not assume the data is always valid. + virtual ExtraData* GetExtraData() const = 0; + + // Get/set the URL. + virtual GURL GetURL() const = 0; + virtual void SetURL(const GURL& url) = 0; + + // Get/set the main document URL, which may be different from the URL for a + // subframe load. + virtual GURL GetMainDocumentURL() const = 0; + virtual void SetMainDocumentURL(const GURL& url) = 0; + + // Get/set the cache policy. + virtual WebRequestCachePolicy GetCachePolicy() const = 0; + virtual void SetCachePolicy(WebRequestCachePolicy policy) = 0; + + // Get/set the HTTP request method. + virtual std::wstring GetHttpMethod() const = 0; + virtual void SetHttpMethod(const std::wstring& method) = 0; + + // Returns the string corresponding to a header set in the request. If the + // given header was not set in the request, the empty string is returned. + virtual std::wstring GetHttpHeaderValue(const std::wstring& field) const = 0; + + // Helper function for GetHeaderValue to retrieve the referrer. This + // referrer is generated automatically by WebKit when navigation events + // occur. If there was no referrer (for example, the browser instructed + // WebKit to navigate), the returned string will be empty. + // + // It is preferred to call this instead of GetHttpHeaderValue, because the + // way referrers are stored may change in the future. + // + virtual std::wstring GetHttpReferrer() const = 0; + + // Get/set the opaque history state (used for back/forward navigations). + virtual std::string GetHistoryState() const = 0; + virtual void SetHistoryState(const std::string& state) = 0; + + // Get/set an opaque value containing the security info (including SSL + // connection state) that should be reported as used in the response for that + // request, or an empty string if no security info should be reported. This + // is usually used to simulate security errors on a page (typically an error + // page that should contain the errors of the actual page that has the + // errors). + virtual std::string GetSecurityInfo() const = 0; + virtual void SetSecurityInfo(const std::string& info) = 0; + + // Returns true if this request contains history state that has form data. + virtual bool HasFormData() const = 0; + + virtual ~WebRequest() { } +}; + +#endif // #ifndef WEBKIT_GLUE_WEBURLREQUEST_H__ diff --git a/webkit/glue/weburlrequest_impl.cc b/webkit/glue/weburlrequest_impl.cc new file mode 100644 index 0000000..1f36cb3 --- /dev/null +++ b/webkit/glue/weburlrequest_impl.cc @@ -0,0 +1,145 @@ +// Copyright 2008, 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 "webkit/glue/weburlrequest_impl.h" +#include "webkit/glue/glue_serialize.h" +#include "webkit/glue/glue_util.h" + +using WebCore::FrameLoadRequest; +using WebCore::ResourceRequest; +using WebCore::ResourceRequestCachePolicy; +using WebCore::String; + +// WebRequest ----------------------------------------------------------------- + +WebRequestImpl::WebRequestImpl() { +} + +WebRequestImpl::WebRequestImpl(const GURL& url) + : request_(FrameLoadRequest(webkit_glue::GURLToKURL(url))) { +} + +WebRequestImpl::WebRequestImpl(const WebCore::ResourceRequest& request) + : request_(FrameLoadRequest(request)) { +} + +WebRequestImpl::WebRequestImpl(const WebCore::FrameLoadRequest& request) + : request_(request) { +} + +WebRequest* WebRequestImpl::Clone() const { + return new WebRequestImpl(*this); +} + +void WebRequestImpl::SetExtraData(ExtraData* extra) { + extra_data_ = extra; +} + +WebRequest::ExtraData* WebRequestImpl::GetExtraData() const { + return extra_data_.get(); +} + +GURL WebRequestImpl::GetURL() const { + return webkit_glue::KURLToGURL(request_.resourceRequest().url()); +} + +void WebRequestImpl::SetURL(const GURL& url) { + request_.resourceRequest().setURL(webkit_glue::GURLToKURL(url)); +} + +GURL WebRequestImpl::GetMainDocumentURL() const { + return webkit_glue::KURLToGURL(request_.resourceRequest().mainDocumentURL()); +} + +void WebRequestImpl::SetMainDocumentURL(const GURL& url) { + request_.resourceRequest().setMainDocumentURL(webkit_glue::GURLToKURL(url)); +} + +WebRequestCachePolicy WebRequestImpl::GetCachePolicy() const { + // WebRequestCachePolicy mirrors ResourceRequestCachePolicy + return static_cast<WebRequestCachePolicy>( + request_.resourceRequest().cachePolicy()); +} + +void WebRequestImpl::SetCachePolicy(WebRequestCachePolicy policy) { + // WebRequestCachePolicy mirrors ResourceRequestCachePolicy + request_.resourceRequest().setCachePolicy( + static_cast<WebCore::ResourceRequestCachePolicy>(policy)); +} + +std::wstring WebRequestImpl::GetHttpMethod() const { + return webkit_glue::StringToStdWString( + request_.resourceRequest().httpMethod()); +} + +void WebRequestImpl::SetHttpMethod(const std::wstring& method) { + request_.resourceRequest().setHTTPMethod( + webkit_glue::StdWStringToString(method)); +} + +std::wstring WebRequestImpl::GetHttpHeaderValue(const std::wstring& field) const { + return webkit_glue::StringToStdWString( + request_.resourceRequest().httpHeaderField( + webkit_glue::StdWStringToString(field))); +} + +std::wstring WebRequestImpl::GetHttpReferrer() const { + return webkit_glue::StringToStdWString( + request_.resourceRequest().httpReferrer()); +} + +std::string WebRequestImpl::GetHistoryState() const { + std::string value; + webkit_glue::HistoryItemToString(history_item_, &value); + return value; +} + +void WebRequestImpl::SetHistoryState(const std::string& value) { + history_item_ = webkit_glue::HistoryItemFromString(value); +} + +std::string WebRequestImpl::GetSecurityInfo() const { + return webkit_glue::CStringToStdString( + request_.resourceRequest().securityInfo()); +} + +void WebRequestImpl::SetSecurityInfo(const std::string& value) { + request_.resourceRequest().setSecurityInfo( + webkit_glue::StdStringToCString(value)); +} + +bool WebRequestImpl::HasFormData() const { + return history_item() && history_item()->formData(); +} + +// static +WebRequest* WebRequest::Create(const GURL& url) { + return new WebRequestImpl(url); +} diff --git a/webkit/glue/weburlrequest_impl.h b/webkit/glue/weburlrequest_impl.h new file mode 100644 index 0000000..4cab62c --- /dev/null +++ b/webkit/glue/weburlrequest_impl.h @@ -0,0 +1,87 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBURLREQUEST_IMPL_H__ +#define WEBKIT_GLUE_WEBURLREQUEST_IMPL_H__ + +#include "webkit/glue/weburlrequest.h" +#include "base/basictypes.h" + +#pragma warning(push, 0) +#include "FrameLoadRequest.h" +#include "HistoryItem.h" +#pragma warning(pop) + +class WebRequestImpl : public WebRequest { + public: + WebRequestImpl(); + + explicit WebRequestImpl(const GURL& url); + explicit WebRequestImpl(const WebCore::ResourceRequest& request); + explicit WebRequestImpl(const WebCore::FrameLoadRequest& request); + + // WebRequest + virtual WebRequest* Clone() const; + virtual void SetExtraData(ExtraData* extra); + virtual ExtraData* GetExtraData() const; + virtual void SetURL(const GURL& url); + virtual GURL GetURL() const; + virtual void SetMainDocumentURL(const GURL& url); + virtual GURL GetMainDocumentURL() const; + virtual WebRequestCachePolicy GetCachePolicy() const; + virtual void SetCachePolicy(WebRequestCachePolicy policy); + virtual std::wstring GetHttpMethod() const; + virtual void SetHttpMethod(const std::wstring& method); + virtual std::wstring GetHttpHeaderValue(const std::wstring& field) const; + virtual std::wstring GetHttpReferrer() const; + virtual std::string GetHistoryState() const; + virtual void SetHistoryState(const std::string& value); + virtual std::string GetSecurityInfo() const; + virtual void SetSecurityInfo(const std::string& value); + virtual bool HasFormData() const; + + // WebRequestImpl + const WebCore::FrameLoadRequest& frame_load_request() const { + return request_; + } + void set_frame_load_request(const WebCore::FrameLoadRequest& request) { + request_ = request; + } + + PassRefPtr<WebCore::HistoryItem> history_item() const { + return history_item_; + } + + protected: + WebCore::FrameLoadRequest request_; + RefPtr<WebCore::HistoryItem> history_item_; + scoped_refptr<ExtraData> extra_data_; +}; + +#endif // #ifndef WEBKIT_GLUE_WEBURLREQUEST_IMPL_H__ diff --git a/webkit/glue/webview.h b/webkit/glue/webview.h new file mode 100644 index 0000000..9f44769 --- /dev/null +++ b/webkit/glue/webview.h @@ -0,0 +1,219 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBVIEW_H__ +#define WEBKIT_GLUE_WEBVIEW_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "webkit/glue/webwidget.h" + +struct WebDropData; +struct WebPreferences; +class GURL; +class WebFrame; +class WebViewDelegate; + +// +// @class WebView +// WebView manages the interaction between WebFrameViews and WebDataSources. +// Modification of the policies and behavior of the WebKit is largely managed +// by WebViews and their delegates. +// +// Typical usage: +// +// WebView *webView; +// WebFrame *mainFrame; +// +// webView = [[WebView alloc] initWithFrame: NSMakeRect (0,0,640,480)]; +// mainFrame = [webView mainFrame]; +// [mainFrame loadRequest:request]; +// +// WebViews have a WebViewDelegate that the embedding application implements +// that are required for tasks like opening new windows and controlling the +// user interface elements in those windows, monitoring the progress of loads, +// monitoring URL changes, and making determinations about how content of +// certain types should be handled. +class WebView : public WebWidget { + public: + WebView() {} + virtual ~WebView() {} + + // This method creates a WebView that is initially sized to an empty rect. + static WebView* Create(WebViewDelegate* delegate, + const WebPreferences& prefs); + + // Returns the delegate for this WebView. This is the pointer that was + // passed to WebView::Create. The caller must check this value before using + // it, it will be NULL during closing of the view. + virtual WebViewDelegate* GetDelegate() = 0; + + // Instructs the EditorClient whether to pass editing notifications on to a + // delegate, if one is present. This allows embedders that haven't + // overridden any editor delegate methods to avoid the performance impact of + // calling them. + virtual void SetUseEditorDelegate(bool value) = 0; + + // Method that controls whether pressing Tab key cycles through page elements + // or inserts a '\t' char in text area + virtual void SetTabKeyCyclesThroughElements(bool value) = 0; + + // Returns whether the current view can be closed, after running any + // onbeforeunload event handlers. + virtual bool ShouldClose() = 0; + + // + // @method mainFrame + // @abstract Return the top level frame. + // @discussion Note that even document that are not framesets will have a + // mainFrame. + // @result The main frame. + // - (WebFrame *)mainFrame; + virtual WebFrame* GetMainFrame() = 0; + + // Returns the currently focused frame. + virtual WebFrame* GetFocusedFrame() = 0; + + // Sets focus to the frame passed in. + virtual void SetFocusedFrame(WebFrame* frame) = 0; + + // Returns the frame with the given name, or NULL if not found. + virtual WebFrame* GetFrameWithName(const std::wstring& name) = 0; + + // Returns the frame previous to the specified frame, by traversing the frame + // tree, wrapping around if necessary. + virtual WebFrame* GetPreviousFrameBefore(WebFrame* frame, bool wrap) = 0; + + // Returns the frame after to the specified frame, by traversing the frame + // tree, wrapping around if necessary. + virtual WebFrame* GetNextFrameAfter(WebFrame* frame, bool wrap) = 0; + + // ---- TODO(darin): remove from here ---- + + // + // - (IBAction)stopLoading:(id)sender; + virtual void StopLoading() = 0; + + // Sets the maximum size to allow WebCore's internal B/F list to grow to. + // If not called, the list will have the default capacity specified in + // BackForwardList.cpp. + virtual void SetBackForwardListSize(int size) = 0; + + // ---- TODO(darin): remove to here ---- + + // Restores focus to the previously focused element. + // This method is invoked when the webview is shown after being + // hidden, and focus is to be restored. When WebView loses focus, it remembers + // the frame/element that had focus, so that when this method is invoked + // focus is then restored. + virtual void RestoreFocus() = 0; + + // Focus the first (last if reverse is true) focusable node. + virtual void SetInitialFocus(bool reverse) = 0; + + // Stores the focused node and clears it if |frame| is the focused frame. + // TODO(jcampan): http://b/issue?id=1157486 this is needed to work-around + // issues caused by the fix for bug #792423 and should be removed when that + // bug is fixed. + virtual void StoreFocusForFrame(WebFrame* frame) = 0; + + // Returns whether or not the focused control needs spell-checking. + // Currently, this function just retrieves the focused node and determines + // whether or not it is a <textarea> element or an element whose + // contenteditable attribute is true. + // TODO(hbono): Bug 740540: This code just implements the default behavior + // proposed in this issue. We should also retrieve "spellcheck" attributes + // for text fields and create a flag to over-write the default behavior. + virtual bool FocusedFrameNeedsSpellchecking() = 0; + + // Requests the webview to download an image. When done, the delegate is + // notified by way of DidDownloadImage. Returns true if the request was + // successfully started, false otherwise. id is used to uniquely identify the + // request and passed back to the DidDownloadImage method. If the image has + // multiple frames, the frame whose size is image_size is returned. If the + // image doesn't have a frame at the specified size, the first is returned. + virtual bool DownloadImage(int id, const GURL& image_url, int image_size) = 0; + + // Replace the standard setting for the WebView with |preferences|. + virtual void SetPreferences(const WebPreferences& preferences) = 0; + virtual const WebPreferences& GetPreferences() = 0; + + // Set the encoding of the current main frame. The value comes from + // the encoding menu. WebKit uses the function named + // SetCustomTextEncodingName to do override encoding job. + virtual void SetPageEncoding(const std::wstring& encoding_name) = 0; + + // Return the canonical encoding name of current main webframe in webview. + virtual std::wstring GetMainFrameEncodingName() = 0; + + // Change the text zoom level. Text size is made 20% larger or smaller. + virtual void MakeTextLarger() = 0; + virtual void MakeTextSmaller() = 0; + virtual void MakeTextStandardSize() = 0; + + // Copy to the clipboard the image located at a particular point in the + // WebView (if there is such an image) + virtual void CopyImageAt(int x, int y) = 0; + + // Inspect a particular point in the WebView. (x = -1 || y = -1) is a special + // case which means inspect the current page and not a specific point. + virtual void InspectElement(int x, int y) = 0; + + // Show the JavaScript console. + virtual void ShowJavaScriptConsole() = 0; + + // Notifies the webview that a drag has terminated. + virtual void DragSourceEndedAt( + int client_x, int client_y, int screen_x, int screen_y) = 0; + + // Notifies the webview that a drag and drop operation is in progress, with + // dropable items over the view. + virtual void DragSourceMovedTo( + int client_x, int client_y, int screen_x, int screen_y) = 0; + + // Notfies the webview that the system drag and drop operation has ended. + virtual void DragSourceSystemDragEnded() = 0; + + // Callback methods when a drag and drop operation is trying to drop + // something on the renderer. + virtual bool DragTargetDragEnter(const WebDropData& drop_data, + int client_x, int client_y, int screen_x, int screen_y) = 0; + virtual bool DragTargetDragOver( + int client_x, int client_y, int screen_x, int screen_y) = 0; + virtual void DragTargetDragLeave() = 0; + virtual void DragTargetDrop( + int client_x, int client_y, int screen_x, int screen_y) = 0; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebView); +}; + +#endif // WEBKIT_GLUE_WEBVIEW_H__ diff --git a/webkit/glue/webview_delegate.h b/webkit/glue/webview_delegate.h new file mode 100644 index 0000000..f7aedd5 --- /dev/null +++ b/webkit/glue/webview_delegate.h @@ -0,0 +1,731 @@ +// Copyright 2008, 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. + +// WebCore provides hooks for several kinds of functionality, allowing separate +// classes termed "delegates" to receive notifications (in the form of direct +// function calls) when certain events are about to occur or have just occurred. +// In some cases, the delegate implements the needed functionality; in others, +// the delegate has some control over the behavior but doesn't actually +// implement it. For example, the UI delegate is responsible for showing a +// dialog box or otherwise handling a JavaScript window.alert() call, via the +// RunJavaScriptAlert() method. On the other hand, the editor delegate doesn't +// actually handle editing functionality, although it could (for example) +// override whether a content-editable node accepts editing focus by returning +// false from ShouldBeginEditing(). (It would also possible for a more +// special-purpose editing delegate to act on the edited node in some way, e.g. +// to highlight modified text in the DidChangeContents() method.) + +// WebKit divides the delegated tasks into several different classes, but we +// combine them into a single WebViewDelegate. This single delegate encompasses +// the needed functionality of the WebKit UIDelegate, ContextMenuDelegate, +// PolicyDelegate, FrameLoadDelegate, and EditorDelegate; additional portions +// of ChromeClient and FrameLoaderClient not delegated in the WebKit +// implementation; and some WebView additions. + +#ifndef WEBKIT_GLUE_WEBVIEW_DELEGATE_H__ +#define WEBKIT_GLUE_WEBVIEW_DELEGATE_H__ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "googleurl/src/gurl.h" +#include "webkit/glue/context_node_types.h" +#include "webkit/glue/webwidget_delegate.h" + +namespace gfx { + class Point; + class Rect; +} + +enum WindowOpenDisposition; +struct PasswordForm; +struct WebDropData; +struct WebPreferences; +class SkBitmap; +class WebError; +class WebFrame; +class WebHistoryItem; +class WebPluginDelegate; +class WebRequest; +class WebResponse; +class WebView; +class WebWidget; + +enum WebNavigationType { + WebNavigationTypeLinkClicked, + WebNavigationTypeFormSubmitted, + WebNavigationTypeBackForward, + WebNavigationTypeReload, + WebNavigationTypeFormResubmitted, + WebNavigationTypeOther +}; + +enum NavigationGesture { + NavigationGestureUser, // User initiated navigation/load. This is not + // currently used due to the untrustworthy nature + // of userGestureHint (wasRunByUserGesture). See + // bug 1051891. + NavigationGestureAuto, // Non-user initiated navigation / load. For example + // onload or setTimeout triggered document.location + // changes, and form.submits. See bug 1046841 for + // some cases that should be treated this way but + // aren't yet. + NavigationGestureUnknown, // What we assign when userGestureHint returns true + // because we can't trust it. +}; + + +// Interface passed in to the WebViewDelegate to receive notification of the +// result of an open file dialog. +class WebFileChooserCallback { + public: + WebFileChooserCallback() {} + virtual ~WebFileChooserCallback() {} + virtual void OnFileChoose(const std::wstring& file_name) { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebFileChooserCallback); +}; + + +// Inheritance here is somewhat weird, but since a WebView is a WebWidget, +// it makes sense that a WebViewDelegate is a WebWidgetDelegate. +class WebViewDelegate : virtual public WebWidgetDelegate { + public: + // WebView additions ------------------------------------------------------- + + // This method is called to create a new WebView. The WebView should not be + // made visible until the new WebView's Delegate has its Show method called. + // The returned WebView pointer is assumed to be owned by the host window, + // and the caller of CreateWebView should not release the given WebView. + // user_gesture is true if a user action initiated this call. + virtual WebView* CreateWebView(WebView* webview, bool user_gesture) { + return NULL; + } + + // This method is called to create a new WebWidget to act as a popup + // (like a drop-down menu). + virtual WebWidget* CreatePopupWidget(WebView* webview) { + return NULL; + } + + // This method is called to create a WebPluginDelegate implementation when a + // new plugin is instanced. See webkit_glue::CreateWebPluginDelegateHelper + // for a default WebPluginDelegate implementation. + virtual WebPluginDelegate* CreatePluginDelegate( + WebView* webview, + const GURL& url, + const std::string& mime_type, + const std::string& clsid, + std::string* actual_mime_type) { + return NULL; + } + + // This method is called when default plugin has been correctly created and + // initialized, and found that the missing plugin is available to install or + // user has started installation. + virtual void OnMissingPluginStatus(WebPluginDelegate* delegate, int status) { + } + + // This method is called to open a URL in the specified manner. + virtual void OpenURL(WebView* webview, const GURL& url, + WindowOpenDisposition disposition) { + } + + // Notifies how many matches have been found so far, for a given request_id. + // |final_update| specifies whether this is the last update (all frames have + // completed scoping). + virtual void ReportFindInPageMatchCount(int count, int request_id, + bool final_update) { + } + + // Notifies the browser what tick-mark rect is currently selected. Parameter + // |request_id| lets the recipient know which request this message belongs to, + // so that it can choose to ignore the message if it has moved on to other + // things. |selection_rect| is expected to have coordinates relative to the + // top left corner of the web page area and represent where on the screen the + // selection rect is currently located. + virtual void ReportFindInPageSelection(int request_id, + int active_match_ordinal, + const gfx::Rect& selection_rect) { + } + + // This function is called to retrieve a resource bitmap from the + // renderer that was cached as a result of the renderer receiving a + // ViewMsg_Preload_Bitmap message from the browser. + virtual const SkBitmap* GetPreloadedResourceBitmap(int resource_id) { + return NULL; + } + + // Returns whether this WebView was opened by a user gesture. + virtual bool WasOpenedByUserGesture(WebView* webview) const { + return true; + } + + // FrameLoaderClient ------------------------------------------------------- + + // Notifies the delegate that a load has begun. + virtual void DidStartLoading(WebView* webview) { + } + + // Notifies the delegate that all loads are finished. + virtual void DidStopLoading(WebView* webview) { + } + + // The original version of this is WindowScriptObjectAvailable, below. This + // is a Chrome-specific version that serves the same purpose, but has been + // renamed since we haven't implemented WebScriptObject. Our embedding + // implementation binds native objects to the window via the webframe instead. + // TODO(pamg): If we do implement WebScriptObject, we may wish to switch to + // using the original version of this function. + virtual void WindowObjectCleared(WebFrame* webframe) { + } + + // PolicyDelegate ---------------------------------------------------------- + + // This method is called to notify the delegate, and let it modify a + // proposed navigation. It will be called before loading starts, and + // on every redirect. + // + // disposition specifies what should normally happen for this + // navigation (open in current tab, start a new tab, start a new + // window, etc). This method can return an altered disposition, and + // take any additional separate action it wants to. + // + // is_redirect is true if this is a redirect rather than user action. + virtual WindowOpenDisposition DispositionForNavigationAction( + WebView* webview, + WebFrame* frame, + const WebRequest* request, + WebNavigationType type, + WindowOpenDisposition disposition, + bool is_redirect) { + return disposition; + } + + // FrameLoadDelegate ------------------------------------------------------- + + // Notifies the delegate that the provisional load of a specified frame in a + // given WebView has started. By the time the provisional load for a frame has + // started, we know whether or not the current load is due to a client + // redirect or not, so we pass this information through to allow us to set + // the referrer properly in those cases. The consumed_client_redirect_src is + // an empty invalid GURL in other cases. + virtual void DidStartProvisionalLoadForFrame( + WebView* webview, + WebFrame* frame, + NavigationGesture gesture) { + } + + // Called when a provisional load is redirected (see GetProvisionalDataSource + // for more info on provisional loads). This happens when the server sends + // back any type of redirect HTTP response. + // + // The redirect information can be retrieved from the provisional data + // source's redirect chain, which will be updated prior to this callback. + // The last element in that vector will be the new URL (which will be the + // same as the provisional data source's current URL), and the next-to-last + // element will be the referring URL. + virtual void DidReceiveProvisionalLoadServerRedirect(WebView* webview, + WebFrame* frame) { + } + + // @method webView:didFailProvisionalLoadWithError:forFrame: + // @abstract Notifies the delegate that the provisional load has failed + // @param webView The WebView sending the message + // @param error The error that occurred + // @param frame The frame for which the error occurred + // @discussion This method is called after the provisional data source has + // failed to load. The frame will continue to display the contents of the + // committed data source if there is one. + virtual void DidFailProvisionalLoadWithError(WebView* webview, + const WebError& error, + WebFrame* frame) { + } + + // If the provisional load fails, we try to load a an error page describing + // the user about the load failure. |html| is the UTF8 text to display. If + // |html| is empty, we will fall back on a local error page. + virtual void LoadNavigationErrorPage(WebFrame* frame, + const WebRequest* failed_request, + const WebError& error, + const std::string& html, + bool replace) { + } + + // Notifies the delegate that the load has changed from provisional to + // committed. This method is called after the provisional data source has + // become the committed data source. + // + // In some cases, a single load may be committed more than once. This + // happens in the case of multipart/x-mixed-replace, also known as "server + // push". In this case, a single location change leads to multiple documents + // that are loaded in sequence. When this happens, a new commit will be sent + // for each document. + // + // The "is_new_navigation" flag will be true when a new session history entry + // was created for the load. The frame's GetHistoryState method can be used + // to get the corresponding session history state. + virtual void DidCommitLoadForFrame(WebView* webview, WebFrame* frame, + bool is_new_navigation) { + } + + // + // @method webView:didReceiveTitle:forFrame: + // @abstract Notifies the delegate that the page title for a frame has been received + // @param webView The WebView sending the message + // @param title The new page title + // @param frame The frame for which the title has been received + // @discussion The title may update during loading; clients should be prepared for this. + // - (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame; + virtual void DidReceiveTitle(WebView* webview, + const std::wstring& title, + WebFrame* frame) { + } + + // + // @method webView:didFinishLoadForFrame: + // @abstract Notifies the delegate that the committed load of a frame has completed + // @param webView The WebView sending the message + // @param frame The frame that finished loading + // @discussion This method is called after the committed data source of a frame has successfully loaded + // and will only be called when all subresources such as images and stylesheets are done loading. + // Plug-In content and JavaScript-requested loads may occur after this method is called. + // - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame; + virtual void DidFinishLoadForFrame(WebView* webview, + WebFrame* frame) { + } + + // + // @method webView:didFailLoadWithError:forFrame: + // @abstract Notifies the delegate that the committed load of a frame has failed + // @param webView The WebView sending the message + // @param error The error that occurred + // @param frame The frame that failed to load + // @discussion This method is called after a data source has committed but failed to completely load. + // - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame; + virtual void DidFailLoadWithError(WebView* webview, + const WebError& error, + WebFrame* forFrame) { + } + + // Notifies the delegate of a DOMContentLoaded event. + // This is called when the html resource has been loaded, but + // not necessarily all subresources (images, stylesheets). So, this is called + // before DidFinishLoadForFrame. + virtual void DidFinishDocumentLoadForFrame(WebView* webview, WebFrame* frame) { + } + + // This method is called when we load a resource from an in-memory cache. + // A return value of |false| indicates the load should proceed, but WebCore + // appears to largely ignore the return value. + virtual bool DidLoadResourceFromMemoryCache(WebView* webview, + const WebRequest& request, + const WebResponse& response, + WebFrame* frame) { + return false; + } + + // This is called after javascript onload handlers have been fired. + virtual void DidHandleOnloadEventsForFrame(WebView* webview, WebFrame* frame) { + } + + // This method is called when anchors within a page have been clicked. + // It is very similar to DidCommitLoadForFrame. + virtual void DidChangeLocationWithinPageForFrame(WebView* webview, + WebFrame* frame, + bool is_new_navigation) { + } + + // This is called when the favicon for a frame has been received. + virtual void DidReceiveIconForFrame(WebView* webview, WebFrame* frame) { + } + + // Notifies the delegate that a frame will start a client-side redirect. When + // this function is called, the redirect has not yet been started (it may + // not even be scheduled to happen until some point in the future). When the + // redirect has been cancelled or has succeeded, DidStopClientRedirect will + // be called. + // + // WebKit considers meta refreshes, and setting document.location (regardless + // of when called) as client redirects (possibly among others). + // + // This function is intended to continue progress feedback while a + // client-side redirect is pending. Watch out: WebKit seems to call us twice + // for client redirects, resulting in two calls of this function. + virtual void WillPerformClientRedirect(WebView* webview, + WebFrame* frame, + const GURL& src_url, + const GURL& dest_url, + unsigned int delay_seconds, + unsigned int fire_date) { + } + + // Notifies the delegate that a pending client-side redirect has been + // cancelled (for example, if the frame changes before the timeout) or has + // completed successfully. A client-side redirect is the result of setting + // document.location, for example, as opposed to a server side redirect + // which is the result of HTTP headers (see DidReceiveServerRedirect). + // + // On success, this will be called when the provisional load that the client + // side redirect initiated is committed. + // + // See the implementation of FrameLoader::clientRedirectCancelledOrFinished. + virtual void DidCancelClientRedirect(WebView* webview, + WebFrame* frame) { + } + + // Notifies the delegate that the load about to be committed for the specified + // webview and frame was due to a client redirect originating from source URL. + virtual void DidCompleteClientRedirect(WebView* webview, + WebFrame* frame, + const GURL& source) { + } + // + // @method webView:willCloseFrame: + // @abstract Notifies the delegate that a frame will be closed + // @param webView The WebView sending the message + // @param frame The frame that will be closed + // @discussion This method is called right before WebKit is done with the frame + // and the objects that it contains. + // - (void)webView:(WebView *)sender willCloseFrame:(WebFrame *)frame; + virtual void WillCloseFrame(WebView* webview, WebFrame* frame) { + } + + // ResourceLoadDelegate ---------------------------------------------------- + + // Associates the given identifier with the initial resource request. + // Resource load callbacks will use the identifier throughout the life of the + // request. + virtual void AssignIdentifierToRequest(WebView* webview, + uint32 identifier, + const WebRequest& request) { + } + + // Notifies the delegate that a request is about to be sent out, giving the + // delegate the opportunity to modify the request. Note that request is + // writable here, and changes to the URL, for example, will change the request + // to be made. + virtual void WillSendRequest(WebView* webview, + uint32 identifier, + WebRequest* request) { + } + + // Notifies the delegate that a subresource load has succeeded. + virtual void DidFinishLoading(WebView* webview, uint32 identifier) { + } + + // Notifies the delegate that a subresource load has failed, and why. + virtual void DidFailLoadingWithError(WebView* webview, + uint32 identifier, + const WebError& error) { + } + + // ChromeClient ------------------------------------------------------------ + + // Appends a line to the application's error console. The message contains + // an error description or other information, the line_no provides a line + // number (e.g. for a JavaScript error report), and the source_id contains + // a URL or other description of the source of the message. + virtual void AddMessageToConsole(WebView* webview, + const std::wstring& message, + unsigned int line_no, + const std::wstring& source_id) { + } + + // Notification of possible password forms to be filled/submitted by + // the password manager + virtual void OnPasswordFormsSeen(WebView* webview, + const std::vector<PasswordForm>& forms) { + } + + // + virtual void OnUnloadListenerChanged(WebView* webview, WebFrame* webframe) { + } + + // UIDelegate -------------------------------------------------------------- + + // Displays a JavaScript alert panel associated with the given view. Clients + // should visually indicate that this panel comes from JavaScript. The panel + // should have a single OK button. + virtual void RunJavaScriptAlert(WebView* webview, + const std::wstring& message) { + } + + // Displays a JavaScript confirm panel associated with the given view. + // Clients should visually indicate that this panel comes + // from JavaScript. The panel should have two buttons, e.g. "OK" and + // "Cancel". Returns true if the user hit OK, or false if the user hit Cancel. + virtual bool RunJavaScriptConfirm(WebView* webview, + const std::wstring& message) { + return false; + } + + // Displays a JavaScript text input panel associated with the given view. + // Clients should visually indicate that this panel comes from JavaScript. + // The panel should have two buttons, e.g. "OK" and "Cancel", and an area to + // type text. The default_value should appear as the initial text in the + // panel when it is shown. If the user hit OK, returns true and fills result + // with the text in the box. The value of result is undefined if the user + // hit Cancel. + virtual bool RunJavaScriptPrompt(WebView* webview, + const std::wstring& message, + const std::wstring& default_value, + std::wstring* result) { + return false; + } + + // Displays a "before unload" confirm panel associated with the given view. + // The panel should have two buttons, e.g. "OK" and "Cancel", where OK means + // that the navigation should continue, and Cancel means that the navigation + // should be cancelled, leaving the user on the current page. Returns true + // if the user hit OK, or false if the user hit Cancel. + virtual bool RunBeforeUnloadConfirm(WebView* webview, + const std::wstring& message) { + return true; // OK, continue to navigate away + } + + // Tells the client that we're hovering over a link with a given URL, + // if the node is not a link, the URL will be an empty GURL. + virtual void UpdateTargetURL(WebView* webview, + const GURL& url) { + } + + // Called to display a file chooser prompt. The prompt should be pre- + // populated with the given initial_filename string. The WebViewDelegate + // will own the WebFileChooserCallback object and is responsible for + // freeing it. + virtual void RunFileChooser(const std::wstring& initial_filename, + WebFileChooserCallback* file_chooser) { + delete file_chooser; + } + + // @abstract Shows a context menu with commands relevant to a specific + // element on the current page. + // @param webview The WebView sending the delegate method. + // @param type The type of node(s) the context menu is being invoked on + // @param x The x position of the mouse pointer (relative to the webview) + // @param y The y position of the mouse pointer (relative to the webview) + // @param link_url The absolute URL of the link that contains the node the + // mouse right clicked on + // @param image_url The absolute URL of the image that the mouse right + // clicked on + // @param page_url The URL of the page the mouse right clicked on + // @param frame_url The URL of the subframe the mouse right clicked on + // @param selection_text The raw text of the selection that the mouse right + // clicked on + // @param misspelled_word The editable (possibily) misspelled word + // in the Editor on which dictionary lookup for suggestions will be done. + // @param edit_flags Which edit operations the renderer believes are available + virtual void ShowContextMenu(WebView* webview, + ContextNode::Type type, + int x, + int y, + const GURL& link_url, + const GURL& image_url, + const GURL& page_url, + const GURL& frame_url, + const std::wstring& selection_text, + const std::wstring& misspelled_word, + int edit_flags) { + } + + // Starts a drag session with the supplied contextual information. + // webview: The WebView sending the delegate method. + // drop_data: a WebDropData struct which should contain all the necessary + // information for dragging data out of the webview. + virtual void StartDragging(WebView* webview, const WebDropData& drop_data) { } + + // Returns the focus to the client. + // reverse: Whether the focus should go to the previous (if true) or the next + // focusable element. + virtual void TakeFocus(WebView* webview, bool reverse) { + } + + // Displays JS out-of-memory warning in the infobar + virtual void JSOutOfMemory() { + } + + // EditorDelegate ---------------------------------------------------------- + + // These methods exist primarily to allow a specialized executable to record + // edit events for testing purposes. Most embedders are not expected to + // override them. In fact, by default these editor delegate methods aren't + // even called by the EditorClient, for performance reasons. To enable them, + // call WebView::SetUseEditorDelegate(true) for each WebView. + + virtual bool ShouldBeginEditing(WebView* webview, std::wstring range) { + return true; + } + + virtual bool ShouldEndEditing(WebView* webview, std::wstring range) { + return true; + } + + virtual bool ShouldInsertNode(WebView* webview, + std::wstring node, + std::wstring range, + std::wstring action) { + return true; + } + + virtual bool ShouldInsertText(WebView* webview, + std::wstring text, + std::wstring range, + std::wstring action) { + return true; + } + + virtual bool ShouldChangeSelectedRange(WebView* webview, + std::wstring fromRange, + std::wstring toRange, + std::wstring affinity, + bool stillSelecting) { + return true; + } + + virtual bool ShouldDeleteRange(WebView* webview, std::wstring range) { + return true; + } + + virtual bool ShouldApplyStyle(WebView* webview, + std::wstring style, + std::wstring range) { + return true; + } + + virtual bool SmartInsertDeleteEnabled() { + return false; + } + virtual void DidBeginEditing() { } + virtual void DidChangeSelection() { } + virtual void DidChangeContents() { } + virtual void DidEndEditing() { } + + // Notification that a user metric has occurred. + virtual void UserMetricsRecordAction(const std::wstring& action) { } + virtual void UserMetricsRecordComputedAction(const std::wstring& action) { + UserMetricsRecordAction(action); + } + + // ------------------------------------------------------------------------- + + // Notification that a request to download an image has completed. |errored| + // indicates if there was a network error. The image is empty if there was + // a network error, the contents of the page couldn't by converted to an + // image, or the response from the host was not 200. + // NOTE: image is empty if the response didn't contain image data. + virtual void DidDownloadImage(int id, + const GURL& image_url, + bool errored, + const SkBitmap& image) { + } + + enum ErrorPageType { + DNS_ERROR, + HTTP_404 + }; + // If providing an alternate error page (like link doctor), returns the URL + // to fetch instead. If an invalid url is returned, just fall back on local + // error pages. |error_name| tells the delegate what type of error page we + // want (e.g., 404 vs dns errors). + virtual GURL GetAlternateErrorPageURL(const GURL& failedURL, + ErrorPageType error_type) { + return GURL(); + } + + // History Related --------------------------------------------------------- + + // Returns the session history entry at a distance |offset| relative to the + // current entry. Returns NULL on failure. + virtual WebHistoryItem* GetHistoryEntryAtOffset(int offset) { + return NULL; + } + + // Asynchronously navigates to the history entry at the given offset. + virtual void GoToEntryAtOffsetAsync(int offset) { + } + + // Returns how many entries are in the back and forward lists, respectively. + virtual int GetHistoryBackListCount() { + return 0; + } + virtual int GetHistoryForwardListCount() { + return 0; + } + + // Notification that the form state of an element in the document, scroll + // position, or possibly something else has changed that affects session + // history (HistoryItem). This function will be called frequently, so the + // implementor should not perform intensive operations in this notification. + virtual void OnNavStateChanged(WebView* webview) { } + + // ------------------------------------------------------------------------- + + // Tell the delegate the tooltip text for the current mouse position. + virtual void SetTooltipText(WebView* webview, + const std::wstring& tooltip_text) { } + + // Downloading ------------------------------------------------------------- + + virtual void DownloadUrl(const GURL& url, const GURL& referrer) { } + + // Editor Client ----------------------------------------------------------- + + // Returns true if the word is spelled correctly. The word may begin or end + // with whitespace or punctuation, so the implementor should be sure to handle + // these cases. + // + // If the word is misspelled (returns false), the given first and last + // indices (inclusive) will be filled with the offsets of the boundary of the + // word within the given buffer. The out pointers must be specified. If the + // word is correctly spelled (returns true), they will be set to (0,0). + virtual void SpellCheck(const std::wstring& word, int& misspell_location, + int& misspell_length) { + misspell_location = misspell_length = 0; + } + + // Changes the state of the input method editor. + virtual void SetInputMethodState(bool enabled) { } + + // Asks the user to print the page or a specific frame. Called in response to + // a window.print() call. + virtual void ScriptedPrint(WebFrame* frame) { } + + virtual void WebInspectorOpened(int num_resources) { } + + WebViewDelegate() { } + virtual ~WebViewDelegate() { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebViewDelegate); +}; + +#endif // WEBKIT_GLUE_WEBVIEW_DELEGATE_H__ diff --git a/webkit/glue/webview_impl.cc b/webkit/glue/webview_impl.cc new file mode 100644 index 0000000..69b6cd2 --- /dev/null +++ b/webkit/glue/webview_impl.cc @@ -0,0 +1,1468 @@ +/* +* Copyright 2007 Google Inc. All Rights Reserved. +* +* Portions Copyright (C) 2006 Apple Computer, Inc. All rights reserved. +* +* ***** BEGIN LICENSE BLOCK ***** +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. 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. +* +* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. +* +* ***** END LICENSE BLOCK ***** +* +*/ + +#include "config.h" + +#pragma warning(push, 0) +#include "Cursor.h" +#include "Document.h" +#include "DragController.h" +#include "DragData.h" +#include "Editor.h" +#include "EventHandler.h" +#include "FocusController.h" +#include "FontDescription.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "Image.h" +#include "InspectorController.h" +#include "IntRect.h" +#include "KeyboardEvent.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "Pasteboard.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#include "PluginInfoStore.h" +#include "RenderThemeWin.h" +#include "ResourceHandle.h" +#include "SelectionController.h" +#include "Settings.h" +#include "TypingCommand.h" +#include "event_conversion.h" +#pragma warning(pop) +#undef LOG + +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "webkit/glue/chrome_client_impl.h" +#include "webkit/glue/context_menu_client_impl.h" +#include "webkit/glue/dragclient_impl.h" +#include "webkit/glue/editor_client_impl.h" +#include "webkit/glue/event_conversion.h" +#include "webkit/glue/glue_serialize.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/image_resource_fetcher.h" +#include "webkit/glue/inspector_client_impl.h" +#include "webkit/glue/searchable_form_data.h" +#include "webkit/glue/webdropdata.h" +#include "webkit/glue/webhistoryitem_impl.h" +#include "webkit/glue/webinputevent.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webpreferences.h" +#include "webkit/glue/webview_delegate.h" +#include "webkit/glue/webview_impl.h" +#include "webkit/glue/webwidget_impl.h" +#include "webkit/port/platform/graphics/PlatformContextSkia.h" + +// Get rid of WTF's pow define so we can use std::pow. +#undef pow + +using namespace WebCore; + +// Change the text zoom level by kTextSizeMultiplierRatio each time the user +// zooms text in or out (ie., change by 20%). The min and max values limit +// text zoom to half and 3x the original text size. These three values match +// those in Apple's port in WebKit/WebKit/WebView/WebView.mm +static const double kTextSizeMultiplierRatio = 1.2; +static const double kMinTextSizeMultiplier = 0.5; +static const double kMaxTextSizeMultiplier = 3.0; + +// The webcore drag operation type when something is trying to be dropped on +// the webview. These values are taken from Apple's windows port. +static const WebCore::DragOperation kDropTargetOperation = + static_cast<WebCore::DragOperation>(DragOperationCopy | DragOperationLink); + +// WebView ---------------------------------------------------------------- + +/*static*/ +WebView* WebView::Create(WebViewDelegate* delegate, + const WebPreferences& prefs) { + WebViewImpl* instance = new WebViewImpl(); + instance->AddRef(); + instance->SetPreferences(prefs); + instance->main_frame_->InitMainFrame(instance); + // Set the delegate after initializing the main frame, to avoid trying to + // respond to notifications before we're fully initialized. + instance->delegate_ = delegate; + // Restrict the access to the local file system + // (see WebView.mm WebView::_commonInitializationWithFrameName). + FrameLoader::setRestrictAccessToLocal(true); + return instance; +} + +WebViewImpl::WebViewImpl() + : delegate_(NULL), + pending_history_item_(NULL), +#ifndef NDEBUG + new_navigation_loader_(NULL), +#endif + observed_new_navigation_(false), + text_zoom_level_(0), + context_menu_allowed_(false), + doing_drag_and_drop_(false), + suppress_next_keypress_event_(false), + window_open_disposition_(IGNORE_ACTION), + ime_accept_events_(true) { + // set to impossible point so we always get the first mouse pos + last_mouse_position_.SetPoint(-1, -1); + + // the page will take ownership of the various clients + page_.reset(new Page(new ChromeClientImpl(this), + new ContextMenuClientImpl(this), + new EditorClientImpl(this), + new DragClientImpl(this), + new WebInspectorClient(this))); + + page_->backForwardList()->setClient(this); + + // The group name identifies a namespace of pages. I'm not sure how it's + // intended to be used, but keeping all pages in the same group works for us. + page_->setGroupName("default"); + + // This is created with a refcount of 1, and we assign it to a RefPtr, + // giving a refcount of 2. The ref is done on behalf of + // FrameWin/FrameLoaderWin which references the WebFrame via the + // FrameWinClient/FrameLoaderClient interfaces. See the comment at the + // top of webframe_impl.cc + main_frame_ = new WebFrameImpl(); +} + +WebViewImpl::~WebViewImpl() { + DCHECK(main_frame_ == NULL); + DCHECK(page_ == NULL); + ReleaseFocusReferences(); + for (std::set<ImageResourceFetcher*>::iterator i = image_fetchers_.begin(); + i != image_fetchers_.end(); ++i) { + delete *i; + } +} + +void WebViewImpl::SetUseEditorDelegate(bool value) { + ASSERT(page_ != 0); // The macro doesn't like (!page_) with a scoped_ptr. + ASSERT(page_->editorClient()); + EditorClientImpl* editor_client = + static_cast<EditorClientImpl*>(page_->editorClient()); + editor_client->SetUseEditorDelegate(value); +} + +void WebViewImpl::SetTabKeyCyclesThroughElements(bool value) { + if (page_ != NULL) { + page_->setTabKeyCyclesThroughElements(value); + } +} + +void WebViewImpl::MouseMove(const WebMouseEvent& event) { + if (!main_frame_->frameview()) + return; + + last_mouse_position_.SetPoint(event.x, event.y); + + // We call mouseMoved here instead of handleMouseMovedEvent because we need + // our ChromeClientImpl to receive changes to the mouse position and + // tooltip text, and mouseMoved handles all of that. + main_frame_->frameview()->frame()->eventHandler()->mouseMoved( + MakePlatformMouseEvent(main_frame_->frameview(), event)); +} + +void WebViewImpl::MouseLeave(const WebMouseEvent& event) { + // This event gets sent as the main frame is closing. In that case, just + // ignore it. + if (!main_frame_ || !main_frame_->frameview()) + return; + + delegate_->UpdateTargetURL(this, GURL()); + + main_frame_->frameview()->frame()->eventHandler()->handleMouseMoveEvent( + MakePlatformMouseEvent(main_frame_->frameview(), event)); +} + +void WebViewImpl::MouseDown(const WebMouseEvent& event) { + if (!main_frame_->frameview()) + return; + + last_mouse_down_point_ = gfx::Point(event.x, event.y); + main_frame_->frame()->eventHandler()->handleMousePressEvent( + MakePlatformMouseEvent(main_frame_->frameview(), event)); +} + +void WebViewImpl::MouseUp(const WebMouseEvent& event) { + if (event.button == WebMouseEvent::BUTTON_RIGHT) { + page_->contextMenuController()->clearContextMenu(); + + MakePlatformMouseEvent pme(main_frame_->frameview(), event); + + // Find the right target frame. See issue 1186900. + IntPoint doc_point( + main_frame_->frame()->view()->windowToContents(pme.pos())); + HitTestResult result = + main_frame_->frame()->eventHandler()->hitTestResultAtPoint(doc_point, + false); + Frame* target_frame; + if (result.innerNonSharedNode()) + target_frame = result.innerNonSharedNode()->document()->frame(); + else + target_frame = page_->focusController()->focusedOrMainFrame(); + + target_frame->view()->setCursor(pointerCursor()); + + context_menu_allowed_ = true; + target_frame->eventHandler()->sendContextMenuEvent(pme); + context_menu_allowed_ = false; + // Actually showing the context menu is handled by the ContextMenuClient + // implementation... + } + + if (!main_frame_->frameview()) + return; + + MouseCaptureLost(); + main_frame_->frameview()->frame()->eventHandler()->handleMouseReleaseEvent( + MakePlatformMouseEvent(main_frame_->frameview(), event)); +} + +void WebViewImpl::MouseWheel(const WebMouseWheelEvent& event) { + main_frame_->frame()->eventHandler()->handleWheelEvent( + MakePlatformWheelEvent(main_frame_->frameview(), event)); +} + +bool WebViewImpl::KeyEvent(const WebKeyboardEvent& event) { + DCHECK((event.type == WebInputEvent::KEY_DOWN) || + (event.type == WebInputEvent::KEY_UP)); + + // Please refer to the comments explaining the suppress_next_keypress_event_ + // member. + // The suppress_next_keypress_event_ is set if the KeyDown is handled by + // Webkit. A keyDown event is typically associated with a keyPress(char) + // event and a keyUp event. We reset this flag here as this is a new keyDown + // event. + suppress_next_keypress_event_ = false; + + Frame* frame = GetFocusedWebCoreFrame(); + if (!frame) + return false; + + EventHandler* handler = frame->eventHandler(); + if (!handler) + return KeyEventDefault(event); + + if (((event.modifiers == 0) && (event.key_code == VK_APPS)) || + ((event.modifiers == WebInputEvent::SHIFT_KEY) && + (event.key_code == VK_F10))) { + SendContextMenuEvent(event); + return true; + } + + MakePlatformKeyboardEvent evt(event); + + if (WebInputEvent::KEY_DOWN == event.type) { + MakePlatformKeyboardEvent evt_rawkeydown = evt; + evt_rawkeydown.SetKeyType(WebCore::PlatformKeyboardEvent::RawKeyDown); + if (handler->keyEvent(evt_rawkeydown) && !evt_rawkeydown.isSystemKey()) { + suppress_next_keypress_event_ = true; + return true; + } + } else { + if (handler->keyEvent(evt)) { + return true; + } + } + + return KeyEventDefault(event); +} + +bool WebViewImpl::CharEvent(const WebKeyboardEvent& event) { + DCHECK(event.type == WebInputEvent::CHAR); + + // Please refer to the comments explaining the suppress_next_keypress_event_ + // member. + // The suppress_next_keypress_event_ is set if the KeyDown is handled by + // Webkit. A keyDown event is typically associated with a keyPress(char) + // event and a keyUp event. We reset this flag here as it only applies + // to the current keyPress event. + if (suppress_next_keypress_event_) { + suppress_next_keypress_event_ = false; + return true; + } + + Frame* frame = GetFocusedWebCoreFrame(); + if (!frame) + return false; + + EventHandler* handler = frame->eventHandler(); + if (!handler) + return KeyEventDefault(event); + + MakePlatformKeyboardEvent evt(event); + if (!evt.IsCharacterKey()) + return true; + + // Safari 3.1 does not pass off WM_SYSCHAR messages to the + // eventHandler::keyEvent. We mimic this behavior. + if (evt.isSystemKey()) + return handler->handleAccessKey(evt); + + if (!handler->keyEvent(evt)) + return KeyEventDefault(event); + + return true; +} + +/* +* The WebViewImpl::SendContextMenuEvent function is based on the Webkit +* function +* bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in +* webkit\webkit\win\WebView.cpp. The only significant change in this +* function is the code to convert from a Keyboard event to the Right +* Mouse button up event. +*/ +bool WebViewImpl::SendContextMenuEvent(const WebKeyboardEvent& event) { + static const int kContextMenuMargin = 1; + FrameView* view = page()->mainFrame()->view(); + if (!view) + return false; + + POINT coords = {-1, -1}; + int right_aligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT); + IntPoint location; + + // The context menu event was generated from the keyboard, so show the + // context menu by the current selection. + Position start = + page()->mainFrame()->selectionController()->selection().start(); + Position end = page()->mainFrame()->selectionController()->selection().end(); + + if (!start.node() || !end.node()) { + location = + IntPoint(right_aligned ? view->contentsWidth() - kContextMenuMargin + : kContextMenuMargin, kContextMenuMargin); + } else { + RenderObject* renderer = start.node()->renderer(); + if (!renderer) + return false; + // Calculate the rect of the first line of the selection (cribbed from + // -[WebCoreFrameBridge firstRectForDOMRange:]). + int extra_width_to_EOL = 0; + IntRect start_caret_rect = renderer->caretRect(start.offset(), DOWNSTREAM, + &extra_width_to_EOL); + IntRect end_caret_rect = renderer->caretRect(end.offset(), UPSTREAM); + IntRect first_rect; + if (start_caret_rect.y() == end_caret_rect.y()) { + first_rect = IntRect(std::min(start_caret_rect.x(), end_caret_rect.x()), + start_caret_rect.y(), + abs(end_caret_rect.x() - start_caret_rect.x()), + max(start_caret_rect.height(), + end_caret_rect.height())); + } else { + first_rect = IntRect(start_caret_rect.x(), start_caret_rect.y(), + start_caret_rect.width() + extra_width_to_EOL, + start_caret_rect.height()); + } + location = IntPoint(right_aligned ? first_rect.right() : + first_rect.x(), first_rect.bottom()); + } + + location = view->contentsToWindow(location); + // FIXME: The IntSize(0, -1) is a hack to get the hit-testing to result in + // the selected element. Ideally we'd have the position of a context menu + // event be separate from its target node. + coords = location + IntSize(0, -1); + + // The contextMenuController() holds onto the last context menu that was + // popped up on the page until a new one is created. We need to clear + // this menu before propagating the event through the DOM so that we can + // detect if we create a new menu for this event, since we won't create + // a new menu if the DOM swallows the event and the defaultEventHandler does + // not run. + page()->contextMenuController()->clearContextMenu(); + + Frame* focused_frame = page()->focusController()->focusedOrMainFrame(); + focused_frame->view()->setCursor(pointerCursor()); + WebMouseEvent mouse_event; + mouse_event.button = WebMouseEvent::BUTTON_RIGHT; + mouse_event.x = coords.x; + mouse_event.y = coords.y; + mouse_event.type = WebInputEvent::MOUSE_UP; + + MakePlatformMouseEvent platform_event(view, mouse_event); + + context_menu_allowed_ = true; + bool handled = + focused_frame->eventHandler()->sendContextMenuEvent(platform_event); + context_menu_allowed_ = false; + return handled; +} + +bool WebViewImpl::KeyEventDefault(const WebKeyboardEvent& event) { + Frame* frame = GetFocusedWebCoreFrame(); + if (!frame) + return false; + + switch (event.type) { + case WebInputEvent::CHAR: { + if (event.key_code == VK_SPACE) { + int key_code = ((event.modifiers & WebInputEvent::SHIFT_KEY) ? + VK_PRIOR : VK_NEXT); + return ScrollViewWithKeyboard(key_code); + } + break; + } + + case WebInputEvent::KEY_DOWN: { + if (event.modifiers == WebInputEvent::CTRL_KEY) { + switch (event.key_code) { + case 'A': + GetFocusedFrame()->SelectAll(); + return true; + case VK_INSERT: + case 'C': + GetFocusedFrame()->Copy(); + return true; + // Match FF behavior in the sense that Ctrl+home/end are the only Ctrl + // key combinations which affect scrolling. Safari is buggy in the + // sense that it scrolls the page for all Ctrl+scrolling key + // combinations. For e.g. Ctrl+pgup/pgdn/up/down, etc. + case VK_HOME: + case VK_END: + break; + default: + return false; + } + } + if (!event.system_key) { + return ScrollViewWithKeyboard(event.key_code); + } + break; + } + + default: + break; + } + return false; +} + +bool WebViewImpl::ScrollViewWithKeyboard(int key_code) { + Frame* frame = GetFocusedWebCoreFrame(); + if (!frame) + return false; + + ScrollDirection scroll_direction; + ScrollGranularity scroll_granularity; + + switch (key_code) { + case VK_LEFT: + scroll_direction = ScrollLeft; + scroll_granularity = ScrollByLine; + break; + case VK_RIGHT: + scroll_direction = ScrollRight; + scroll_granularity = ScrollByLine; + break; + case VK_UP: + scroll_direction = ScrollUp; + scroll_granularity = ScrollByLine; + break; + case VK_DOWN: + scroll_direction = ScrollDown; + scroll_granularity = ScrollByLine; + break; + case VK_HOME: + scroll_direction = ScrollUp; + scroll_granularity = ScrollByDocument; + break; + case VK_END: + scroll_direction = ScrollDown; + scroll_granularity = ScrollByDocument; + break; + case VK_PRIOR: // page up + scroll_direction = ScrollUp; + scroll_granularity = ScrollByPage; + break; + case VK_NEXT: // page down + scroll_direction = ScrollDown; + scroll_granularity = ScrollByPage; + break; + default: + return false; + } + + bool scroll_handled = + frame->eventHandler()->scrollOverflow(scroll_direction, + scroll_granularity); + Frame* current_frame = frame; + while (!scroll_handled && current_frame) { + scroll_handled = current_frame->view()->scroll(scroll_direction, + scroll_granularity); + current_frame = current_frame->tree()->parent(); + } + return scroll_handled; +} + +Frame* WebViewImpl::GetFocusedWebCoreFrame() { + if (!main_frame_ || !main_frame_->frame()) + return NULL; + + return + main_frame_->frame()->page()->focusController()->focusedOrMainFrame(); +} + +// static +WebViewImpl* WebViewImpl::FromPage(WebCore::Page* page) { + return WebFrameImpl::FromFrame(page->mainFrame())->webview_impl(); +} + +// WebView -------------------------------------------------------------------- + +bool WebViewImpl::ShouldClose() { + // TODO(creis): 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 = page_->focusController()->focusedOrMainFrame(); + if (!frame) + return true; + + return frame->shouldClose(); +} + +void WebViewImpl::Close() { + // Do this first to prevent reentrant notifications from being sent to the + // initiator of the close. + delegate_ = NULL; + + // Initiate shutdown for the entire frameset. + if (main_frame_) { + // This will cause a lot of notifications to be sent. + main_frame_->frame()->loader()->frameDetached(); + main_frame_ = NULL; + } + + page_.reset(); +} + +WebViewDelegate* WebViewImpl::GetDelegate() { + return delegate_; +} + +WebFrame* WebViewImpl::GetMainFrame() { + return main_frame_.get(); +} + +WebFrame* WebViewImpl::GetFocusedFrame() { + Frame* frame = GetFocusedWebCoreFrame(); + return frame ? WebFrameImpl::FromFrame(frame) : NULL; +} + +void WebViewImpl::SetFocusedFrame(WebFrame* frame) { + if (!frame) { + // Clears the focused frame if any. + Frame* frame = GetFocusedWebCoreFrame(); + if (frame) + frame->selectionController()->setFocused(false); + return; + } + WebFrameImpl* frame_impl = static_cast<WebFrameImpl*>(frame); + WebCore::Frame* webcore_frame = frame_impl->frame(); + webcore_frame->page()->focusController()->setFocusedFrame(webcore_frame); +} + +WebFrame* WebViewImpl::GetFrameWithName(const std::wstring& name) { + String name_str = webkit_glue::StdWStringToString(name); + Frame* frame = main_frame_->frame()->tree()->find(name_str); + return frame ? WebFrameImpl::FromFrame(frame) : NULL; +} + +WebFrame* WebViewImpl::GetPreviousFrameBefore(WebFrame* frame, bool wrap) { + WebFrameImpl* frame_impl = static_cast<WebFrameImpl*>(frame); + WebCore::Frame* previous = + frame_impl->frame()->tree()->traversePreviousWithWrap(wrap); + return previous ? WebFrameImpl::FromFrame(previous) : NULL; +} + +WebFrame* WebViewImpl::GetNextFrameAfter(WebFrame* frame, bool wrap) { + WebFrameImpl* frame_impl = static_cast<WebFrameImpl*>(frame); + WebCore::Frame* next = + frame_impl->frame()->tree()->traverseNextWithWrap(wrap); + return next ? WebFrameImpl::FromFrame(next) : NULL; +} + +void WebViewImpl::Resize(const gfx::Size& new_size) { + if (size_ == new_size) + return; + size_ = new_size; + + if (main_frame_->frameview()) { + main_frame_->frameview()->resize(size_.width(), size_.height()); + main_frame_->frame()->sendResizeEvent(); + } + + if (delegate_) { + gfx::Rect damaged_rect(0, 0, size_.width(), size_.height()); + delegate_->DidInvalidateRect(this, damaged_rect); + } +} + +void WebViewImpl::Layout() { + if (main_frame_) { + // In order for our child HWNDs (NativeWindowWidgets) to update properly, + // they need to be told that we are updating the screen. The problem is + // that the native widgets need to recalculate their clip region and not + // overlap any of our non-native widgets. To force the resizing, call + // setFrameGeometry(). This will be a quick operation for most frames, but + // the NativeWindowWidgets will update a proper clipping region. + FrameView* frameview = main_frame_->frameview(); + if (frameview) + frameview->setFrameGeometry(frameview->frameGeometry()); + + // setFrameGeometry may have the side-effect of causing existing page + // layout to be invalidated, so layout needs to be called last. + + main_frame_->Layout(); + } +} + +void WebViewImpl::Paint(gfx::PlatformCanvas* canvas, const gfx::Rect& rect) { + if (main_frame_) + main_frame_->Paint(canvas, rect); +} + +// TODO(eseidel): g_current_input_event should be removed once +// ChromeClient:show() can get the current-event information from WebCore. +/* static */ +const WebInputEvent* WebViewImpl::g_current_input_event = NULL; + +bool WebViewImpl::HandleInputEvent(const WebInputEvent* input_event) { + // If we've started a drag and drop operation, ignore input events until + // we're done. + if (doing_drag_and_drop_) + return true; + + // TODO(eseidel): Remove g_current_input_event. + // This only exists to allow ChromeClient::show() to know which mouse button + // triggered a window.open event. + // Safari must perform a similar hack, ours is in our WebKit glue layer + // theirs is in the application. This should go when WebCore can be fixed + // to pass more event information to ChromeClient::show() + g_current_input_event = input_event; + + bool handled = true; + + // TODO(jcampan): WebKit seems to always return false on mouse events + // processing methods. For now we'll assume it has processed them (as we are + // only interested in whether keyboard events are processed). + switch (input_event->type) { + case WebInputEvent::MOUSE_MOVE: + MouseMove(*static_cast<const WebMouseEvent*>(input_event)); + break; + + case WebInputEvent::MOUSE_LEAVE: + MouseLeave(*static_cast<const WebMouseEvent*>(input_event)); + break; + + case WebInputEvent::MOUSE_WHEEL: + MouseWheel(*static_cast<const WebMouseWheelEvent*>(input_event)); + break; + + case WebInputEvent::MOUSE_DOWN: + case WebInputEvent::MOUSE_DOUBLE_CLICK: + MouseDown(*static_cast<const WebMouseEvent*>(input_event)); + break; + + case WebInputEvent::MOUSE_UP: + MouseUp(*static_cast<const WebMouseEvent*>(input_event)); + break; + + case WebInputEvent::KEY_DOWN: + case WebInputEvent::KEY_UP: + handled = KeyEvent(*static_cast<const WebKeyboardEvent*>(input_event)); + break; + + case WebInputEvent::CHAR: + handled = CharEvent(*static_cast<const WebKeyboardEvent*>(input_event)); + break; + default: + handled = false; + } + + g_current_input_event = NULL; + + return handled; +} + +void WebViewImpl::MouseCaptureLost() { +} + +// TODO(darin): these navigation methods should be killed + +void WebViewImpl::StopLoading() { + main_frame_->StopLoading(); +} + +void WebViewImpl::SetBackForwardListSize(int size) { + page_->backForwardList()->setCapacity(size); +} + +void WebViewImpl::SetFocus(bool enable) { + if (enable) { + // Getting the focused frame will have the side-effect of setting the main + // frame as the focused frame if it is not already focused. Otherwise, if + // there is already a focused frame, then this does nothing. + GetFocusedFrame(); + if (main_frame_ && main_frame_->frame()) { + Frame* frame = main_frame_->frame(); + if (!frame->selectionController()->isFocusedAndActive()) { + // No one has focus yet, try to restore focus. + RestoreFocus(); + frame->page()->focusController()->setActive(true); + } + Frame* focused_frame = + frame->page()->focusController()->focusedOrMainFrame(); + frame->selectionController()->setFocused(frame == focused_frame); + } + ime_accept_events_ = true; + } else { + // Clear out who last had focus. If someone has focus, the refs will be + // updated below. + ReleaseFocusReferences(); + + // Clear focus on the currently focused frame if any. + + if (!main_frame_) + return; + + Frame* frame = main_frame_->frame(); + if (!frame) + return; + + RefPtr<Frame> focused = frame->page()->focusController()->focusedFrame(); + if (focused.get()) { + // Update the focus refs, this way we can give focus back appropriately. + // It's entirely possible to have a focused document, but not a focused + // node. + RefPtr<Document> document = focused->document(); + last_focused_frame_ = focused; + if (document.get()) { + RefPtr<Node> focused_node = document->focusedNode(); + if (focused_node.get()) { + // To workaround bug #792423, we do not blur the focused node. This + // should be reenabled when we merge a WebKit that has the fix for + // http://bugs.webkit.org/show_bug.cgi?id=16928. + // last_focused_node_ = focused_node; + // document->setFocusedNode(NULL); + } + } + frame->page()->focusController()->setFocusedFrame(0); + // Finish an ongoing composition to delete the composition node. + Editor* editor = focused->editor(); + if (editor && editor->hasComposition()) + editor->confirmComposition(); + ime_accept_events_ = false; + } + // Make sure the main frame doesn't think it has focus. + if (frame != focused.get()) + frame->selectionController()->setFocused(false); + } +} + +// TODO(jcampan): http://b/issue?id=1157486 this is needed to work-around +// issues caused by the fix for bug #792423 and should be removed when that +// bug is fixed. +void WebViewImpl::StoreFocusForFrame(WebFrame* frame) { + DCHECK(frame); + + // We only want to store focus info if we are the focused frame and if we have + // not stored it already. + WebCore::Frame* webcore_frame = static_cast<WebFrameImpl*>(frame)->frame(); + if (last_focused_frame_.get() != webcore_frame || last_focused_node_.get()) + return; + + // Clear out who last had focus. If someone has focus, the refs will be + // updated below. + ReleaseFocusReferences(); + + last_focused_frame_ = webcore_frame; + RefPtr<Document> document = last_focused_frame_->document(); + if (document.get()) { + RefPtr<Node> focused_node = document->focusedNode(); + if (focused_node.get()) { + last_focused_node_ = focused_node; + document->setFocusedNode(NULL); + } + } +} + +void WebViewImpl::ImeSetComposition(int string_type, int cursor_position, + int target_start, int target_end, + int string_length, + const wchar_t *string_data) { + Frame* focused = GetFocusedWebCoreFrame(); + if (!focused || !ime_accept_events_) { + return; + } + Editor* editor = focused->editor(); + if (!editor) + return; + if (!editor->canEdit()) { + // The input focus has been moved to another WebWidget object. + // We should use this |editor| object only to complete the ongoing + // composition. + if (!editor->hasComposition()) + return; + } + + if (string_type == 0) { + // A browser process sent an IPC message which does not contain a valid + // string, which means an ongoing composition has been canceled. + // If the ongoing composition has been canceled, replace the ongoing + // composition string with an empty string and complete it. + // TODO(hbono): Need to add a new function to cancel the ongoing + // composition to WebCore::Editor? + WebCore::String empty_string; + editor->confirmComposition(empty_string); + } else { + // A browser process sent an IPC message which contains a string to be + // displayed in this Editor object. + // To display the given string, set the given string to the + // m_compositionNode member of this Editor object and display it. + // NOTE: An empty string (often sent by Chinese IMEs and Korean IMEs) + // causes a panic in Editor::setComposition(), which deactivates the + // m_frame.m_sel member of this Editor object, i.e. we can never display + // composition strings in the m_compositionNode member. + // (I have not been able to find good methods for re-activating it.) + // Therefore, I have to prevent from calling Editor::setComposition() + // with its first argument an empty string. + if (string_length > 0) { + if (target_start < 0) target_start = 0; + if (target_end < 0) target_end = string_length; + WebCore::String composition_string(string_data, string_length); + // Create custom underlines. + // To emphasize the selection, the selected region uses a solid black + // for its underline while other regions uses a pale gray for theirs. + WTF::Vector<WebCore::CompositionUnderline> underlines(3); + underlines[0].startOffset = 0; + underlines[0].endOffset = target_start; + underlines[0].thick = true; + underlines[0].color.setRGB(0xd3, 0xd3, 0xd3); + underlines[1].startOffset = target_start; + underlines[1].endOffset = target_end; + underlines[1].thick = true; + underlines[1].color.setRGB(0x00, 0x00, 0x00); + underlines[2].startOffset = target_end; + underlines[2].endOffset = string_length; + underlines[2].thick = true; + underlines[2].color.setRGB(0xd3, 0xd3, 0xd3); + // When we use custom underlines, WebKit ("InlineTextBox.cpp" Line 282) + // prevents from writing a text in between 'selectionStart' and + // 'selectionEnd' somehow. + // Therefore, we use the 'cursor_position' for these arguments so that + // there are not any characters in the above region. + editor->setComposition(composition_string, underlines, + cursor_position, cursor_position); + } + // The given string is a result string, which means the ongoing + // composition has been completed. I have to call the + // Editor::confirmCompletion() and complete this composition. + if (string_type == GCS_RESULTSTR) { + editor->confirmComposition(); + } + } +} + +bool WebViewImpl::ImeUpdateStatus(bool* enable_ime, const void **id, + int* x, int* y) { + // Initialize the return values so that we can disable the IME attached + // to a browser process when an error occurs while retrieving information + // of the focused edit control. + *enable_ime = false; + *id = NULL; + *x = -1; + *y = -1; + // Store the position of the bottom-left corner of the caret. + // This process consists of the following four steps: + // 1. Retrieve the selection controller of the focused frame; + // 2. Retrieve the caret rectangle from the controller; + // 3. Convert the rectangle, which is relative to the parent view, to the + // one relative to the client window, and; + // 4. Store the position of its bottom-left corner. + const Frame* focused = GetFocusedWebCoreFrame(); + if (!focused) + return false; + const Editor* editor = focused->editor(); + if (!editor || !editor->canEdit()) + return false; + const SelectionController* controller = focused->selectionController(); + if (!controller) + return false; + const Node* node = controller->start().node(); + if (!node) + return false; + const FrameView* view = node->document()->view(); + if (!view) + return false; + IntRect rect = view->contentsToWindow(controller->caretRect()); + *x = rect.x(); + *y = rect.bottom(); + return true; +} + +void WebViewImpl::RestoreFocus() { + if (last_focused_frame_.get()) { + if (last_focused_frame_->page()) { + // last_focused_frame_ can be detached from the frame tree, thus, + // its page can be null. + last_focused_frame_->page()->focusController()->setFocusedFrame( + last_focused_frame_.get()); + } + if (last_focused_node_.get()) { + // last_focused_node_ may be null, make sure it's valid before trying to + // focus it. + static_cast<Element*>(last_focused_node_.get())->focus(); + } + // And clear out the refs. + ReleaseFocusReferences(); + } +} + +void WebViewImpl::SetInitialFocus(bool reverse) { + if (page_.get()) { + // So RestoreFocus does not focus anything when it is called. + ReleaseFocusReferences(); + + // Since we don't have a keyboard event, we'll create one. + WebKeyboardEvent keyboard_event; + keyboard_event.type = WebInputEvent::KEY_DOWN; + if (reverse) + keyboard_event.modifiers = WebInputEvent::SHIFT_KEY; + keyboard_event.key_code = VK_TAB; + keyboard_event.key_data = L'\t'; + MakePlatformKeyboardEvent platform_event(keyboard_event); + // We have to set the key type explicitly to avoid an assert in the + // KeyboardEvent constructor. + platform_event.SetKeyType(PlatformKeyboardEvent::RawKeyDown); + KeyboardEvent webkit_event(platform_event, NULL); + page()->focusController()->setInitialFocus( + reverse ? WebCore::FocusDirectionBackward : + WebCore::FocusDirectionForward, + &webkit_event); + } +} + +bool WebViewImpl::FocusedFrameNeedsSpellchecking() { + const WebCore::Frame* frame = GetFocusedWebCoreFrame(); + if (!frame) + return false; + const WebCore::Document* document = frame->document(); + if (!document) + return false; + const WebCore::Node* node = document->focusedNode(); + if (!node) + return false; + const WebCore::RenderObject* renderer = node->renderer(); + if (!renderer) + return false; + // We should also retrieve the contenteditable attribute of this element to + // determine if this element needs spell-checking. + const WebCore::EUserModify user_modify = renderer->style()->userModify(); + return renderer->isTextArea() || + user_modify == WebCore::READ_WRITE || + user_modify == WebCore::READ_WRITE_PLAINTEXT_ONLY; +} + +// Releases references used to restore focus. +void WebViewImpl::ReleaseFocusReferences() { + if (last_focused_frame_.get()) { + last_focused_frame_.release(); + last_focused_node_.release(); + } +} + +bool WebViewImpl::DownloadImage(int id, const GURL& image_url, int image_size) { + if (!main_frame_ || !main_frame_->frame()) + return false; + image_fetchers_.insert( + new ImageResourceFetcher(this, id, image_url, image_size)); + return true; +} + +void WebViewImpl::SetPreferences(const WebPreferences& preferences) { + if (!page_.get()) + return; + + // Keep a local copy of the preferences struct for GetPreferences. + webprefs_ = preferences; + + Settings* settings = page_->settings(); + + settings->setStandardFontFamily(webkit_glue::StdWStringToString( + preferences.standard_font_family)); + settings->setFixedFontFamily(webkit_glue::StdWStringToString( + preferences.fixed_font_family)); + settings->setSerifFontFamily(webkit_glue::StdWStringToString( + preferences.serif_font_family)); + settings->setSansSerifFontFamily(webkit_glue::StdWStringToString( + preferences.sans_serif_font_family)); + settings->setCursiveFontFamily(webkit_glue::StdWStringToString( + preferences.cursive_font_family)); + settings->setFantasyFontFamily(webkit_glue::StdWStringToString( + preferences.fantasy_font_family)); + settings->setDefaultFontSize(preferences.default_font_size); + settings->setDefaultFixedFontSize(preferences.default_fixed_font_size); + settings->setMinimumFontSize(preferences.minimum_font_size); + settings->setMinimumLogicalFontSize(preferences.minimum_logical_font_size); + settings->setDefaultTextEncodingName(webkit_glue::StdWStringToString( + preferences.default_encoding)); + settings->setJavaScriptEnabled(preferences.javascript_enabled); + settings->setJavaScriptCanOpenWindowsAutomatically( + preferences.javascript_can_open_windows_automatically); + settings->setLoadsImagesAutomatically( + preferences.loads_images_automatically); + settings->setPluginsEnabled(preferences.plugins_enabled); + settings->setDOMPasteAllowed(preferences.dom_paste_enabled); + settings->setDeveloperExtrasEnabled(preferences.developer_extras_enabled); + settings->setShrinksStandaloneImagesToFit( + preferences.shrinks_standalone_images_to_fit); + settings->setUsesUniversalDetector(preferences.uses_universal_detector); + settings->setTextAreasAreResizable(preferences.text_areas_are_resizable); + if (preferences.user_style_sheet_enabled) { + settings->setUserStyleSheetLocation(webkit_glue::GURLToKURL( + preferences.user_style_sheet_location)); + } else { + settings->setUserStyleSheetLocation(KURL()); + } + + // This setting affects the behavior of links in an editable region: + // clicking the link should select it rather than navigate to it. + // Safari uses the same default. It is unlikley an embedder would want to + // change this, since it would break existing rich text editors. + settings->setEditableLinkBehavior(WebCore::EditableLinkNeverLive); + + settings->setUsesDashboardBackwardCompatibilityMode( + preferences.dashboard_compatibility_mode); + settings->setFontRenderingMode(NormalRenderingMode); + settings->setJavaEnabled(preferences.java_enabled); + + // RenderTheme is a singleton that needs to know the default font size to + // draw some form controls. We let it know each time the size changes. + WebCore::RenderThemeWin::setDefaultFontSize(preferences.default_font_size); + + // Used to make sure if the frameview needs layout, layout is triggered + // during Document::updateLayout(). TODO(tc): See bug 1199269 for more + // details. + FrameView* frameview = main_frame()->frameview(); + if (frameview && frameview->needsLayout()) + frameview->setNeedsLayout(); +} + +const WebPreferences& WebViewImpl::GetPreferences() { + return webprefs_; +} + +// Set the encoding of the current main frame to the one selected by +// a user in the encoding menu. +void WebViewImpl::SetPageEncoding(const std::wstring& encoding_name) { + if (!main_frame_) + return; + + if (!encoding_name.empty()) { + // only change override encoding, don't change default encoding + String new_encoding_name(encoding_name.data()); + main_frame_->frame()->loader()->reloadAllowingStaleData(new_encoding_name); + } +} + +// Return the canonical encoding name of current main webframe in webview. +std::wstring WebViewImpl::GetMainFrameEncodingName() { + if (!main_frame_) + return std::wstring(L""); + + String encoding_name = main_frame_->frame()->loader()->encoding(); + return std::wstring(encoding_name.charactersWithNullTermination()); +} + +void WebViewImpl::MakeTextLarger() { + Frame* frame = main_frame()->frame(); + double multiplier = std::min(std::pow(kTextSizeMultiplierRatio, + text_zoom_level_ + 1), + kMaxTextSizeMultiplier); + int zoom_factor = static_cast<int>(100.0 * multiplier); + if (zoom_factor != frame->zoomFactor()) { + ++text_zoom_level_; + frame->setZoomFactor(zoom_factor); + } +} + +void WebViewImpl::MakeTextSmaller() { + Frame* frame = main_frame()->frame(); + double multiplier = std::max(std::pow(kTextSizeMultiplierRatio, + text_zoom_level_ - 1), + kMinTextSizeMultiplier); + int zoom_factor = static_cast<int>(100.0 * multiplier); + if (zoom_factor != frame->zoomFactor()) { + --text_zoom_level_; + frame->setZoomFactor(zoom_factor); + } +} + +void WebViewImpl::MakeTextStandardSize() { + text_zoom_level_ = 0; + main_frame()->frame()->setZoomFactor(100); +} + +void WebViewImpl::CopyImageAt(int x, int y) { + IntPoint point = IntPoint(x, y); + + Frame* frame = main_frame_->frame(); + if (!frame) + return; + + HitTestResult result = + frame->eventHandler()->hitTestResultAtPoint(point, false); + + if (result.absoluteImageURL().isEmpty()) { + // There isn't actually an image at these coordinates. Might be because + // the window scrolled while the context menu was open or because the page + // changed itself between when we thought there was an image here and when + // we actually tried to retreive the image. + // + // TODO: implement a cache of the most recent HitTestResult to avoid having + // to do two hit tests. + return; + } + + frame->editor()->copyImage(result); +} + +void WebViewImpl::InspectElement(int x, int y) { + if (x == -1 || y == -1) { + page_->inspectorController()->inspect(NULL); + } else { + IntPoint point = IntPoint(x, y); + HitTestResult result(point); + + if (Frame* frame = main_frame_->frame()) + result = frame->eventHandler()->hitTestResultAtPoint(point, false); + + if (!result.innerNonSharedNode()) + return; + + page_->inspectorController()->inspect(result.innerNonSharedNode()); + } +} + +void WebViewImpl::ShowJavaScriptConsole() { + page_->inspectorController()->showConsole(); +} + +void WebViewImpl::DragSourceEndedAt( + int client_x, int client_y, int screen_x, int screen_y) { + PlatformMouseEvent pme(IntPoint(client_x, client_y), + IntPoint(screen_x, screen_y), + NoButton, MouseEventMoved, 0, false, false, false, + false, 0); + main_frame_->frame()->eventHandler()->dragSourceEndedAt(pme, + DragOperationCopy); +} + +void WebViewImpl::DragSourceMovedTo( + int client_x, int client_y, int screen_x, int screen_y) { + PlatformMouseEvent pme(IntPoint(client_x, client_y), + IntPoint(screen_x, screen_y), + LeftButton, MouseEventMoved, 0, false, false, false, + false, 0); + main_frame_->frame()->eventHandler()->dragSourceMovedTo(pme); +} + +void WebViewImpl::DragSourceSystemDragEnded() { + page_->dragController()->dragEnded(); + DCHECK(doing_drag_and_drop_); + doing_drag_and_drop_ = false; +} + +bool WebViewImpl::DragTargetDragEnter(const WebDropData& drop_data, + int client_x, int client_y, int screen_x, int screen_y) { + DCHECK(!current_drop_data_.get()); + + // Copy drop_data into current_drop_data_. + WebDropData* drop_data_copy = new WebDropData; + *drop_data_copy = drop_data; + current_drop_data_.reset(drop_data_copy); + + DragData drag_data(reinterpret_cast<DragDataRef>(current_drop_data_.get()), + IntPoint(client_x, client_y), IntPoint(screen_x, screen_y), + kDropTargetOperation); + DragOperation effect = page_->dragController()->dragEntered(&drag_data); + return effect != DragOperationNone; +} + +bool WebViewImpl::DragTargetDragOver( + int client_x, int client_y, int screen_x, int screen_y) { + DCHECK(current_drop_data_.get()); + DragData drag_data(reinterpret_cast<DragDataRef>(current_drop_data_.get()), + IntPoint(client_x, client_y), IntPoint(screen_x, screen_y), + kDropTargetOperation); + DragOperation effect = page_->dragController()->dragUpdated(&drag_data); + return effect != DragOperationNone; +} + +void WebViewImpl::DragTargetDragLeave() { + DCHECK(current_drop_data_.get()); + DragData drag_data(reinterpret_cast<DragDataRef>(current_drop_data_.get()), + IntPoint(), IntPoint(), DragOperationNone); + page_->dragController()->dragExited(&drag_data); + current_drop_data_.reset(NULL); +} + +void WebViewImpl::DragTargetDrop( + int client_x, int client_y, int screen_x, int screen_y) { + DCHECK(current_drop_data_.get()); + DragData drag_data(reinterpret_cast<DragDataRef>(current_drop_data_.get()), + IntPoint(client_x, client_y), IntPoint(screen_x, screen_y), + kDropTargetOperation); + page_->dragController()->performDrag(&drag_data); + current_drop_data_.reset(NULL); +} + +SearchableFormData* WebViewImpl::CreateSearchableFormDataForFocusedNode() { + if (!main_frame_) + return NULL; + + Frame* frame = main_frame_->frame(); + if (!frame) + return NULL; + + if (RefPtr<Frame> focused = + frame->page()->focusController()->focusedFrame()) { + RefPtr<Document> document = focused->document(); + if (document.get()) { + RefPtr<Node> focused_node = document->focusedNode(); + if (focused_node.get() && + focused_node->nodeType() == Node::ELEMENT_NODE) { + return SearchableFormData::Create( + static_cast<Element*>(focused_node.get())); + } + } + } + return NULL; +} + +void WebViewImpl::DidCommitLoad(bool* is_new_navigation) { + if (is_new_navigation) + *is_new_navigation = observed_new_navigation_; + +#ifndef NDEBUG + DCHECK(!observed_new_navigation_ || + main_frame_->frame()->loader()->documentLoader() == new_navigation_loader_); + new_navigation_loader_ = NULL; +#endif + observed_new_navigation_ = false; +} + +void WebViewImpl::StartDragging(const WebDropData& drop_data) { + if (delegate_) { + DCHECK(!doing_drag_and_drop_); + doing_drag_and_drop_ = true; + delegate_->StartDragging(this, drop_data); + } +} + +const WebCore::Node* WebViewImpl::getInspectedNode(WebCore::Frame* frame) { + DCHECK(frame); + WebFrameImpl* webframe_impl = WebFrameImpl::FromFrame(frame); + if (!webframe_impl) + return NULL; + + return webframe_impl->inspected_node(); +} + +void WebViewImpl::ImageResourceDownloadDone(ImageResourceFetcher* fetcher, + bool errored, + const SkBitmap& image) { + if (delegate_) { + delegate_->DidDownloadImage(fetcher->id(), fetcher->image_url(), errored, + image); + } + DeleteImageResourceFetcher(fetcher); +} + +//----------------------------------------------------------------------------- +// WebCore::WidgetClientWin + +HWND WebViewImpl::containingWindow() { + return delegate_ ? delegate_->GetContainingWindow(this) : NULL; +} + +void WebViewImpl::invalidateRect(const IntRect& damaged_rect) { + if (delegate_) + delegate_->DidInvalidateRect(this, gfx::Rect(damaged_rect.x(), + damaged_rect.y(), + damaged_rect.width(), + damaged_rect.height())); +} + +void WebViewImpl::scrollRect(int dx, int dy, const IntRect& clip_rect) { + if (delegate_) + delegate_->DidScrollRect(this, dx, dy, gfx::Rect(clip_rect.x(), + clip_rect.y(), + clip_rect.width(), + clip_rect.height())); +} + +void WebViewImpl::popupOpened(WebCore::Widget* widget, + const WebCore::IntRect& bounds) { + if (!delegate_) + return; + + WebWidgetImpl* webwidget = + static_cast<WebWidgetImpl*>(delegate_->CreatePopupWidget(this)); + webwidget->Init(widget, gfx::Rect(bounds.x(), bounds.y(), + bounds.width(), bounds.height())); +} + +void WebViewImpl::popupClosed(WebCore::Widget* widget) { + NOTREACHED() << "popupClosed called on a non-popup"; +} + +void WebViewImpl::setCursor(const WebCore::Cursor& cursor) { + if (delegate_) + delegate_->SetCursor(this, cursor.impl()); +} + +void WebViewImpl::setFocus() { + if (delegate_) + delegate_->Focus(this); +} + +const SkBitmap* WebViewImpl::getPreloadedResourceBitmap(int resource_id) { + if (!delegate_) + return NULL; + + return delegate_->GetPreloadedResourceBitmap(resource_id); +} + +void WebViewImpl::onScrollPositionChanged(WebCore::Widget* widget) { + // Scroll position changes should be reflected in the session history. + if (delegate_) + delegate_->OnNavStateChanged(this); +} + +const WTF::Vector<RefPtr<WebCore::Range> >* WebViewImpl::getTickmarks( + WebCore::Frame* frame) { + DCHECK(frame); + WebFrameImpl* webframe_impl = WebFrameImpl::FromFrame(frame); + if (!webframe_impl) + return NULL; + + return &webframe_impl->tickmarks(); +} + +size_t WebViewImpl::getActiveTickmarkIndex(WebCore::Frame* frame) { + DCHECK(frame); + WebFrameImpl* webframe_impl = WebFrameImpl::FromFrame(frame); + if (!webframe_impl) + return kNoTickmark; + + // The mainframe can tell us if we are the frame with the active tick-mark. + if (webframe_impl != main_frame_->active_tickmark_frame()) + return kNoTickmark; + + return webframe_impl->active_tickmark_index(); +} + +//----------------------------------------------------------------------------- +// WebCore::BackForwardListClient + +void WebViewImpl::didAddHistoryItem(WebCore::HistoryItem* item) { + // If WebCore adds a new HistoryItem, it means this is a new navigation + // (ie, not a reload or back/forward). + observed_new_navigation_ = true; +#ifndef NDEBUG + new_navigation_loader_ = main_frame_->frame()->loader()->documentLoader(); +#endif +} + +void WebViewImpl::willGoToHistoryItem(WebCore::HistoryItem* item) { + if (pending_history_item_) { + if (item == pending_history_item_->GetHistoryItem()) { + // Let the main frame know this HistoryItem is loading, so it can cache + // any ExtraData when the DataSource is created. + main_frame_->set_currently_loading_history_item(pending_history_item_); + pending_history_item_ = 0; + } + } +} + +WebCore::HistoryItem* WebViewImpl::itemAtIndex(int index) { + if (!delegate_) + return NULL; + + WebHistoryItem* item = delegate_->GetHistoryEntryAtOffset(index); + if (!item) + return NULL; + + // If someone has asked for a history item, we probably want to navigate to + // it soon. Keep track of it until willGoToHistoryItem is called. + pending_history_item_ = static_cast<WebHistoryItemImpl*>(item); + return pending_history_item_->GetHistoryItem(); +} + +void WebViewImpl::goToItemAtIndexAsync(int index) { + if (delegate_) + delegate_->GoToEntryAtOffsetAsync(index); +} + +int WebViewImpl::backListCount() { + if (!delegate_) + return 0; + + return delegate_->GetHistoryBackListCount(); +} + +int WebViewImpl::forwardListCount() { + if (!delegate_) + return 0; + + return delegate_->GetHistoryForwardListCount(); +} + +void WebViewImpl::DeleteImageResourceFetcher(ImageResourceFetcher* fetcher) { + DCHECK(image_fetchers_.find(fetcher) != image_fetchers_.end()); + image_fetchers_.erase(fetcher); + + // We're in the callback from the ImageResourceFetcher, best to delay + // deletion. + MessageLoop::current()->DeleteSoon(FROM_HERE, fetcher); +} diff --git a/webkit/glue/webview_impl.h b/webkit/glue/webview_impl.h new file mode 100644 index 0000000..e22c49f --- /dev/null +++ b/webkit/glue/webview_impl.h @@ -0,0 +1,331 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBVIEW_IMPL_H__ +#define WEBKIT_GLUE_WEBVIEW_IMPL_H__ + +#include <set> + +#include "base/basictypes.h" +#include "base/gfx/point.h" +#include "base/gfx/size.h" +#include "webkit/glue/webdropdata.h" +#include "webkit/glue/webframe_impl.h" +#include "webkit/glue/webpreferences.h" +#include "webkit/glue/webview.h" + +#pragma warning(push, 0) +#include "webkit/port/history/BackForwardList.h" +#include "webkit/port/platform/WidgetClientWin.h" +#pragma warning(pop) + +namespace WebCore { + class Frame; + class HistoryItem; + class KeyboardEvent; + class Page; + class PlatformKeyboardEvent; + class Range; + class Widget; +} + +class ImageResourceFetcher; +class SearchableFormData; +struct WebDropData; +class WebHistoryItemImpl; +class WebKeyboardEvent; +class WebMouseEvent; +class WebMouseWheelEvent; +class WebViewDelegate; + +class WebViewImpl : public WebView, + public WebCore::WidgetClientWin, + public WebCore::BackForwardListClient { + public: + // WebView + virtual bool ShouldClose(); + virtual void Close(); + virtual WebViewDelegate* GetDelegate(); + virtual void SetUseEditorDelegate(bool value); + virtual void SetTabKeyCyclesThroughElements(bool value); + virtual WebFrame* GetMainFrame(); + virtual WebFrame* GetFocusedFrame(); + virtual void SetFocusedFrame(WebFrame* frame); + virtual WebFrame* GetFrameWithName(const std::wstring& name); + virtual WebFrame* GetPreviousFrameBefore(WebFrame* frame, bool wrap); + virtual WebFrame* GetNextFrameAfter(WebFrame* frame, bool wrap); + virtual void Resize(const gfx::Size& new_size); + virtual gfx::Size GetSize() { return size(); } + virtual void Layout(); + virtual void Paint(gfx::PlatformCanvas* canvas, const gfx::Rect& rect); + virtual bool HandleInputEvent(const WebInputEvent* input_event); + virtual void MouseCaptureLost(); + virtual void SetFocus(bool enable); + virtual void StoreFocusForFrame(WebFrame* frame); + virtual void ImeSetComposition(int string_type, int cursor_position, + int target_start, int target_end, + int string_length, + const wchar_t *string_data); + virtual bool ImeUpdateStatus(bool* enable_ime, const void** id, + int* x, int* y); + virtual void StopLoading(); + virtual void SetBackForwardListSize(int size); + virtual void RestoreFocus(); + virtual void SetInitialFocus(bool reverse); + virtual bool FocusedFrameNeedsSpellchecking(); + virtual bool DownloadImage(int id, const GURL& image_url, int image_size); + virtual void SetPreferences(const WebPreferences& preferences); + virtual const WebPreferences& GetPreferences(); + virtual void SetPageEncoding(const std::wstring& encoding_name); + virtual std::wstring GetMainFrameEncodingName(); + virtual void MakeTextLarger(); + virtual void MakeTextSmaller(); + virtual void MakeTextStandardSize(); + virtual void CopyImageAt(int x, int y); + virtual void InspectElement(int x, int y); + virtual void ShowJavaScriptConsole(); + virtual void DragSourceEndedAt( + int client_x, int client_y, int screen_x, int screen_y); + virtual void DragSourceMovedTo( + int client_x, int client_y, int screen_x, int screen_y); + virtual void DragSourceSystemDragEnded(); + virtual bool DragTargetDragEnter(const WebDropData& drop_data, + int client_x, int client_y, int screen_x, int screen_y); + virtual bool DragTargetDragOver( + int client_x, int client_y, int screen_x, int screen_y); + virtual void DragTargetDragLeave(); + virtual void DragTargetDrop( + int client_x, int client_y, int screen_x, int screen_y); + + // WebViewImpl + + const gfx::Size& size() const { return size_; } + + const gfx::Point& last_mouse_down_point() const { + return last_mouse_down_point_; + } + + WebCore::Frame* GetFocusedWebCoreFrame(); + + static WebViewImpl* FromPage(WebCore::Page* page); + + WebFrameImpl* main_frame() { + return main_frame_; + } + + WebViewDelegate* delegate() { + return delegate_.get(); + } + + // Returns the page object associated with this view. This may be NULL when + // the page is shutting down, but will be valid all other times. + WebCore::Page* page() const { + return page_.get(); + } + + WebHistoryItemImpl* pending_history_item() const { + return pending_history_item_; + } + + void MouseMove(const WebMouseEvent& mouse_event); + void MouseLeave(const WebMouseEvent& mouse_event); + void MouseDown(const WebMouseEvent& mouse_event); + void MouseUp(const WebMouseEvent& mouse_event); + void MouseDoubleClick(const WebMouseEvent& mouse_event); + void MouseWheel(const WebMouseWheelEvent& wheel_event); + bool KeyEvent(const WebKeyboardEvent& key_event); + bool CharEvent(const WebKeyboardEvent& key_event); + + // Handles context menu events orignated via the the keyboard. These + // include the VK_APPS virtual key and the Shift+F10 combine. + // Code is based on the Webkit function + // bool WebView::handleContextMenuEvent(WPARAM wParam, LPARAM lParam) in + // webkit\webkit\win\WebView.cpp. The only significant change in this + // function is the code to convert from a Keyboard event to the Right + // Mouse button down event. + bool SendContextMenuEvent(const WebKeyboardEvent& event); + + // Releases references used to restore focus. + void ReleaseFocusReferences(); + + // Notifies the WebView that a load has been committed. + // is_new_navigation will be true if a new session history item should be + // created for that load. + void DidCommitLoad(bool* is_new_navigation); + + bool context_menu_allowed() const { + return context_menu_allowed_; + } + + // Set the disposition for how this webview is to be initially shown. + void set_window_open_disposition(WindowOpenDisposition disp) { + window_open_disposition_ = disp; + } + WindowOpenDisposition window_open_disposition() const { + return window_open_disposition_; + } + + // Start a system drag and drop operation. + void StartDragging(const WebDropData& drop_data); + + virtual const WebCore::Node* getInspectedNode(WebCore::Frame* frame); + + // ImageResourceFetcher callback. + void ImageResourceDownloadDone(ImageResourceFetcher* fetcher, + bool errored, + const SkBitmap& image); + + protected: + friend class WebView; // So WebView::Create can call our constructor + + WebViewImpl(); + ~WebViewImpl(); + + void ModifySelection(UINT message, + WebCore::Frame* frame, + const WebCore::PlatformKeyboardEvent& e); + + // WebCore::WidgetClientWin + virtual HWND containingWindow(); + virtual void invalidateRect(const WebCore::IntRect& damaged_rect); + virtual void scrollRect(int dx, int dy, const WebCore::IntRect& clip_rect); + virtual void popupOpened(WebCore::Widget* widget, + const WebCore::IntRect& bounds); + virtual void popupClosed(WebCore::Widget* widget); + virtual void setCursor(const WebCore::Cursor& cursor); + virtual void setFocus(); + virtual const SkBitmap* getPreloadedResourceBitmap(int resource_id); + virtual void onScrollPositionChanged(WebCore::Widget* widget); + virtual const WTF::Vector<RefPtr<WebCore::Range> >* getTickmarks( + WebCore::Frame* frame); + virtual size_t getActiveTickmarkIndex(WebCore::Frame* frame); + + // WebCore::BackForwardListClient + virtual void didAddHistoryItem(WebCore::HistoryItem* item); + virtual void willGoToHistoryItem(WebCore::HistoryItem* item); + virtual WebCore::HistoryItem* itemAtIndex(int index); + virtual void goToItemAtIndexAsync(int index); + virtual int backListCount(); + virtual int forwardListCount(); + + // Creates and returns a new SearchableFormData for the focused node. + // It's up to the caller to free the returned SearchableFormData. + // This returns NULL if the focused node is NULL, or not in a valid form. + SearchableFormData* CreateSearchableFormDataForFocusedNode(); + + scoped_refptr<WebViewDelegate> delegate_; + gfx::Size size_; + + scoped_refptr<WebFrameImpl> main_frame_; + gfx::Point last_mouse_position_; + // Reference to the Frame that last had focus. This is set once when + // we lose focus, and used when focus is gained to reinstall focus to + // the correct element. + RefPtr<WebCore::Frame> last_focused_frame_; + // Reference to the node that last had focus. + RefPtr<WebCore::Node> last_focused_node_; + scoped_ptr<WebCore::Page> page_; + + // The last history item that was accessed via itemAtIndex(). We keep track + // of this until willGoToHistoryItem() is called, so we can track the + // navigation. + scoped_refptr<WebHistoryItemImpl> pending_history_item_; + + // This flag is set when a new navigation is detected. It is used to satisfy + // the corresponding argument to WebViewDelegate::DidCommitLoadForFrame. + bool observed_new_navigation_; +#ifndef NDEBUG + // Used to assert that the new navigation we observed is the same navigation + // when we make use of observed_new_navigation_. + const WebCore::DocumentLoader* new_navigation_loader_; +#endif + + // A copy of the WebPreferences object we receive from the browser. + WebPreferences webprefs_; + + // A copy of the web drop data object we received from the browser. + scoped_ptr<WebDropData> current_drop_data_; + + private: + // Returns true if the event was actually processed. + bool KeyEventDefault(const WebKeyboardEvent& event); + + // Returns true if the view was scrolled. + bool ScrollViewWithKeyboard(int key_code); + + // Removes fetcher from the set of pending image fetchers and deletes it. + // This is invoked after the download is completed (or fails). + void DeleteImageResourceFetcher(ImageResourceFetcher* fetcher); + + // ImageResourceFetchers schedule via DownloadImage. + std::set<ImageResourceFetcher*> image_fetchers_; + + // The point relative to the client area where the mouse was last pressed + // down. This is used by the drag client to determine what was under the + // mouse when the drag was initiated. We need to track this here in + // WebViewImpl since DragClient::startDrag does not pass the position the + // mouse was at when the drag was initiated, only the current point, which + // can be misleading as it is usually not over the element the user actually + // dragged by the time a drag is initiated. + gfx::Point last_mouse_down_point_; + + // Keeps track of the current text zoom level. 0 means no zoom, positive + // values mean larger text, negative numbers mean smaller text. + int text_zoom_level_; + + bool context_menu_allowed_; + + bool doing_drag_and_drop_; + + // Webkit expects keyPress events to be suppressed if the associated keyDown + // event was handled. Safari implements this behavior by peeking out the + // associated WM_CHAR event if the keydown was handled. We emulate + // this behavior by setting this flag if the keyDown was handled. + bool suppress_next_keypress_event_; + + // The disposition for how this webview is to be initially shown. + WindowOpenDisposition window_open_disposition_; + + // Represents whether or not this object should process incoming IME events. + bool ime_accept_events_; + + // HACK: current_input_event is for ChromeClientImpl::show(), until we can fix + // WebKit to pass enough information up into ChromeClient::show() so we can + // decide if the window.open event was caused by a middle-mouse click +public: + static const WebInputEvent* current_input_event() { + return g_current_input_event; + } +private: + static const WebInputEvent* g_current_input_event; + + DISALLOW_EVIL_CONSTRUCTORS(WebViewImpl); +}; + +#endif // WEBKIT_GLUE_WEBVIEW_IMPL_H__ diff --git a/webkit/glue/webwidget.h b/webkit/glue/webwidget.h new file mode 100644 index 0000000..1f5f74c --- /dev/null +++ b/webkit/glue/webwidget.h @@ -0,0 +1,100 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBWIDGET_H__ +#define WEBKIT_GLUE_WEBWIDGET_H__ + +#include "base/ref_counted.h" + +namespace gfx { +class PlatformCanvas; +class Rect; +class Size; +} + +class WebInputEvent; +class WebWidgetDelegate; + +class WebWidget : public base::RefCounted<WebWidget> { + public: + WebWidget() {} + virtual ~WebWidget() {} + + // This method creates a WebWidget that is initially invisible and positioned + // according to the given bounds relative to the specified parent window. + // The caller is responsible for showing the WebWidget's view window (see + // GetViewWindow) once it is ready to have the WebWidget appear on the screen. + static WebWidget* Create(WebWidgetDelegate* delegate); + + // This method closes the WebWidget. + virtual void Close() = 0; + + // Called to resize the WebWidget. + virtual void Resize(const gfx::Size& new_size) = 0; + + // Returns the current size of the WebWidget. + virtual gfx::Size GetSize() = 0; + + // Called to layout the WebWidget. This MUST be called before Paint, and it + // may result in calls to WebWidgetDelegate::DidInvalidateRect. + virtual void Layout() = 0; + + // Called to paint the specified region of the WebWidget onto the given canvas. + // You MUST call Layout before calling this method. It is okay to call Paint + // multiple times once Layout has been called, assuming no other changes are + // made to the WebWidget (e.g., once events are processed, it should be assumed + // that another call to Layout is warranted before painting again). + virtual void Paint(gfx::PlatformCanvas* canvas, const gfx::Rect& rect) = 0; + + // Called to inform the WebWidget of an input event. + // Returns true if the event has been processed, false otherwise. + virtual bool HandleInputEvent(const WebInputEvent* input_event) = 0; + + // Called to inform the WebWidget that mouse capture was lost. + virtual void MouseCaptureLost() = 0; + + // Called to inform the WebWidget that it has gained or lost keyboard focus. + virtual void SetFocus(bool enable) = 0; + + // Called to inform the webwidget of a composition event from IMM + // (Input Method Manager). + virtual void ImeSetComposition(int string_type, int cursor_position, + int target_start, int target_end, + int string_length, + const wchar_t *string_data) = 0; + + // Retrieve the status of this widget required by IME APIs. + virtual bool ImeUpdateStatus(bool* enable_ime, const void** node, + int* x, int* y) = 0; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebWidget); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBWIDGET_H__ diff --git a/webkit/glue/webwidget_delegate.h b/webkit/glue/webwidget_delegate.h new file mode 100644 index 0000000..d68c38f --- /dev/null +++ b/webkit/glue/webwidget_delegate.h @@ -0,0 +1,111 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBWIDGET_DELEGATE_H__ +#define WEBKIT_GLUE_WEBWIDGET_DELEGATE_H__ + +typedef struct HWND__* HWND; + +namespace gfx { + class Point; + class Rect; +} + +enum WindowOpenDisposition; +class WebWidget; +class WebCursor; +struct WebPluginGeometry; + +class WebWidgetDelegate { + public: + // Returns the HWND in which the WebWidget is embedded. + virtual HWND GetContainingWindow(WebWidget* webwidget) = 0; + + // Called when a region of the WebWidget needs to be re-painted. + virtual void DidInvalidateRect(WebWidget* webwidget, const gfx::Rect& rect) = 0; + + // Called when a region of the WebWidget, given by clip_rect, should be + // scrolled by the specified dx and dy amounts. + virtual void DidScrollRect(WebWidget* webwidget, int dx, int dy, + const gfx::Rect& clip_rect) = 0; + + // This method is called to instruct the window containing the WebWidget to + // show itself as the topmost window. This method is only used after a + // successful call to CreateWebWidget. |disposition| indicates how this new + // window should be displayed, but generally only means something for WebViews. + virtual void Show(WebWidget* webwidget, WindowOpenDisposition disposition) = 0; + + // This method is called to instruct the window containing the WebWidget to + // close. Note: This method should just be the trigger that causes the + // WebWidget to eventually close. It should not actually be destroyed until + // after this call returns. + virtual void CloseWidgetSoon(WebWidget* webwidget) = 0; + + // This method is called to focus the window containing the WebWidget so + // that it receives keyboard events. + virtual void Focus(WebWidget* webwidget) = 0; + + // This method is called to unfocus the window containing the WebWidget so that + // it no longer receives keyboard events. + virtual void Blur(WebWidget* webwidget) = 0; + + virtual void SetCursor(WebWidget* webwidget, + const WebCursor& cursor) = 0; + // Returns the location (x,y) of the WebWidget in screen coordinates. + virtual void GetWindowLocation(WebWidget* webwidget, gfx::Point* origin) = 0; + + // This method is called to re-position the WebWidget on the screen. The given + // rect is in screen coordinates. The implementation may choose to ignore + // this call or modify the given rect. This method may be called before Show + // has been called. + // TODO(darin): this is more of a request; does this need to take effect + // synchronously? + virtual void SetWindowRect(WebWidget* webwidget, const gfx::Rect& rect) = 0; + + // Keeps track of the necessary window move for a plugin window that resulted + // from a scroll operation. That way, all plugin windows can be moved at the + // same time as each other and the page. + virtual void DidMove(WebWidget* webwidget, const WebPluginGeometry& move) = 0; + + // Suppress input events to other windows, and do not return until the widget + // is closed. This is used to support |window.showModalDialog|. + virtual void RunModal(WebWidget* webwidget) = 0; + + // Owners depend on the delegates living as long as they do, so we ref them. + virtual void AddRef() = 0; + virtual void Release() = 0; + + WebWidgetDelegate() { } + virtual ~WebWidgetDelegate() { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebWidgetDelegate); +}; + +#endif // #ifndef WEBKIT_GLUE_WEBWIDGET_DELEGATE_H__ diff --git a/webkit/glue/webwidget_impl.cc b/webkit/glue/webwidget_impl.cc new file mode 100644 index 0000000..010d9f0 --- /dev/null +++ b/webkit/glue/webwidget_impl.cc @@ -0,0 +1,275 @@ +// Copyright 2008, 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" + +#pragma warning(push, 0) +#include "Cursor.h" +#include "FramelessScrollView.h" +#include "FrameView.h" +#include "IntRect.h" +#include "PlatformKeyboardEvent.h" +#include "PlatformMouseEvent.h" +#include "PlatformWheelEvent.h" +#pragma warning(pop) + +#undef LOG +#include "base/gfx/platform_canvas.h" +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "webkit/glue/event_conversion.h" +#include "webkit/glue/webinputevent.h" +#include "webkit/glue/webwidget_delegate.h" +#include "webkit/glue/webwidget_impl.h" +#include "graphics/SkiaUtils.h" + +using namespace WebCore; + +// WebWidget ---------------------------------------------------------------- + +/*static*/ +WebWidget* WebWidget::Create(WebWidgetDelegate* delegate) { + WebWidgetImpl* instance = new WebWidgetImpl(delegate); + instance->AddRef(); + return instance; +} + +WebWidgetImpl::WebWidgetImpl(WebWidgetDelegate* delegate) + : delegate_(delegate), + widget_(NULL) { + // set to impossible point so we always get the first mouse pos + last_mouse_position_.SetPoint(-1, -1); +} + +WebWidgetImpl::~WebWidgetImpl() { + if (widget_) { + widget_->setClient(NULL); + } +} + +void WebWidgetImpl::Init(WebCore::Widget* widget, const gfx::Rect& bounds) { + DCHECK(widget->isFrameView()); + widget_ = static_cast<FramelessScrollView*>(widget); + + widget_->setClient(this); + + if (delegate_) { + delegate_->SetWindowRect(this, bounds); + delegate_->Show(this, WindowOpenDisposition()); + } +} + +void WebWidgetImpl::MouseMove(const WebMouseEvent& event) { + // don't send mouse move messages if the mouse hasn't moved. + if (event.x != last_mouse_position_.x() || + event.y != last_mouse_position_.y()) { + last_mouse_position_.SetPoint(event.x, event.y); + + widget_->handleMouseMoveEvent(MakePlatformMouseEvent(widget_, event)); + } +} + +void WebWidgetImpl::MouseLeave(const WebMouseEvent& event) { + widget_->handleMouseMoveEvent(MakePlatformMouseEvent(widget_, event)); +} + +void WebWidgetImpl::MouseDown(const WebMouseEvent& event) { + widget_->handleMouseDownEvent(MakePlatformMouseEvent(widget_, event)); +} + +void WebWidgetImpl::MouseUp(const WebMouseEvent& event) { + MouseCaptureLost(); + widget_->handleMouseReleaseEvent(MakePlatformMouseEvent(widget_, event)); +} + +void WebWidgetImpl::MouseWheel(const WebMouseWheelEvent& event) { + widget_->handleWheelEvent(MakePlatformWheelEvent(widget_, event)); +} + +bool WebWidgetImpl::KeyEvent(const WebKeyboardEvent& event) { + return widget_->handleKeyEvent(MakePlatformKeyboardEvent(event)); +} + +// WebWidget ------------------------------------------------------------------- + +void WebWidgetImpl::Close() { + if (widget_) + widget_->hide(); + + delegate_ = NULL; +} + +void WebWidgetImpl::Resize(const gfx::Size& new_size) { + if (size_ == new_size) + return; + size_ = new_size; + + if (widget_) { + IntRect new_geometry(0, 0, size_.width(), size_.height()); + widget_->setFrameGeometry(new_geometry); + } + + if (delegate_) { + gfx::Rect damaged_rect(0, 0, size_.width(), size_.height()); + delegate_->DidInvalidateRect(this, damaged_rect); + } +} + +void WebWidgetImpl::Layout() { +} + +void WebWidgetImpl::Paint(gfx::PlatformCanvas* canvas, const gfx::Rect& rect) { + if (!widget_) + return; + + if (!rect.IsEmpty()) { + PlatformContextSkia context(canvas); + + // PlatformGraphicsContext is actually a pointer to PlatformContextSkia. + GraphicsContext gc(reinterpret_cast<PlatformGraphicsContext*>(&context)); + IntRect dirty_rect(rect.x(), rect.y(), rect.width(), rect.height()); + + widget_->paint(&gc, dirty_rect); + } +} + +bool WebWidgetImpl::HandleInputEvent(const WebInputEvent* input_event) { + if (!widget_) + return false; + + // TODO (jcampan): WebKit seems to always return false on mouse events + // methods. For now we'll assume it has processed them (as we are only + // interested in whether keyboard events are processed). + switch (input_event->type) { + case WebInputEvent::MOUSE_MOVE: + MouseMove(*static_cast<const WebMouseEvent*>(input_event)); + return true; + + case WebInputEvent::MOUSE_LEAVE: + MouseLeave(*static_cast<const WebMouseEvent*>(input_event)); + return true; + + case WebInputEvent::MOUSE_WHEEL: + MouseWheel(*static_cast<const WebMouseWheelEvent*>(input_event)); + return true; + + case WebInputEvent::MOUSE_DOWN: + case WebInputEvent::MOUSE_DOUBLE_CLICK: + MouseDown(*static_cast<const WebMouseEvent*>(input_event)); + return true; + + case WebInputEvent::MOUSE_UP: + MouseUp(*static_cast<const WebMouseEvent*>(input_event)); + return true; + + case WebInputEvent::KEY_DOWN: + case WebInputEvent::KEY_UP: + return KeyEvent(*static_cast<const WebKeyboardEvent*>(input_event)); + } + return false; +} + +void WebWidgetImpl::MouseCaptureLost() { +} + +void WebWidgetImpl::SetFocus(bool enable) { +} + +void WebWidgetImpl::ImeSetComposition(int string_type, int cursor_position, + int target_start, int target_end, + int string_length, + const wchar_t *string_data) { +} + +bool WebWidgetImpl::ImeUpdateStatus(bool* enable_ime, const void** id, + int* x, int* y) { + return false; +} + +const SkBitmap* WebWidgetImpl::getPreloadedResourceBitmap(int resource_id) { + return NULL; +} + +const WTF::Vector<RefPtr<WebCore::Range> >* WebWidgetImpl::getTickmarks( + WebCore::Frame* frame) { + return NULL; +} + +size_t WebWidgetImpl::getActiveTickmarkIndex(WebCore::Frame* frame) { + return kNoTickmark; +} + +void WebWidgetImpl::onScrollPositionChanged(Widget* widget) { +} + +//----------------------------------------------------------------------------- +// WebCore::WidgetClientWin + +HWND WebWidgetImpl::containingWindow() { + return delegate_ ? delegate_->GetContainingWindow(this) : NULL; +} + +void WebWidgetImpl::invalidateRect(const IntRect& damaged_rect) { + if (delegate_) + delegate_->DidInvalidateRect(this, gfx::Rect(damaged_rect.x(), + damaged_rect.y(), + damaged_rect.width(), + damaged_rect.height())); +} + +void WebWidgetImpl::scrollRect(int dx, int dy, const IntRect& clip_rect) { + if (delegate_) + delegate_->DidScrollRect(this, dx, dy, gfx::Rect(clip_rect.x(), + clip_rect.y(), + clip_rect.width(), + clip_rect.height())); +} + +void WebWidgetImpl::popupOpened(WebCore::Widget* widget, + const WebCore::IntRect& bounds) { + NOTREACHED() << "popupOpened called on a popup"; +} + +void WebWidgetImpl::popupClosed(WebCore::Widget* widget) { + DCHECK(widget == widget_); + if (widget_) { + widget_->setClient(NULL); + widget_ = NULL; + } + delegate_->CloseWidgetSoon(this); +} + +void WebWidgetImpl::setCursor(const WebCore::Cursor& cursor) { + if (delegate_) + delegate_->SetCursor(this, cursor.impl()); +} + +void WebWidgetImpl::setFocus() { + delegate_->Focus(this); +} diff --git a/webkit/glue/webwidget_impl.h b/webkit/glue/webwidget_impl.h new file mode 100644 index 0000000..e885c1d --- /dev/null +++ b/webkit/glue/webwidget_impl.h @@ -0,0 +1,126 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WEBWIDGET_IMPL_H__ +#define WEBKIT_GLUE_WEBWIDGET_IMPL_H__ + +#include "base/basictypes.h" +#include "base/gfx/point.h" +#include "base/gfx/size.h" +#include "webkit/glue/webwidget.h" + +#pragma warning(push, 0) +#include "WidgetClientWin.h" +#pragma warning(pop) + +namespace WebCore { + class Frame; + class FramelessScrollView; + class KeyboardEvent; + class Page; + class PlatformKeyboardEvent; + class Range; + class Widget; +} + +class WebKeyboardEvent; +class WebMouseEvent; +class WebMouseWheelEvent; +class WebWidgetDelegate; + +class WebWidgetImpl : public WebWidget, public WebCore::WidgetClientWin { + public: + // WebWidget + virtual void Close(); + virtual void Resize(const gfx::Size& new_size); + virtual gfx::Size GetSize() { return size(); } + virtual void Layout(); + virtual void Paint(gfx::PlatformCanvas* canvas, const gfx::Rect& rect); + virtual bool HandleInputEvent(const WebInputEvent* input_event); + virtual void MouseCaptureLost(); + virtual void SetFocus(bool enable); + virtual void ImeSetComposition(int string_type, int cursor_position, + int target_start, int target_end, + int string_length, + const wchar_t *string_data); + virtual bool ImeUpdateStatus(bool* enable_ime, const void** id, + int* x, int* y); + + // WebWidgetImpl + void Init(WebCore::Widget* widget, const gfx::Rect& bounds); + + const gfx::Size& size() const { return size_; } + + WebWidgetDelegate* delegate() { + return delegate_; + } + + void MouseMove(const WebMouseEvent& mouse_event); + void MouseLeave(const WebMouseEvent& mouse_event); + void MouseDown(const WebMouseEvent& mouse_event); + void MouseUp(const WebMouseEvent& mouse_event); + void MouseDoubleClick(const WebMouseEvent& mouse_event); + void MouseWheel(const WebMouseWheelEvent& wheel_event); + bool KeyEvent(const WebKeyboardEvent& key_event); + + protected: + friend class WebWidget; // So WebWidget::Create can call our constructor + + WebWidgetImpl(WebWidgetDelegate* delegate); + ~WebWidgetImpl(); + + // WebCore::WidgetClientWin + virtual HWND containingWindow(); + virtual void invalidateRect(const WebCore::IntRect& damaged_rect); + virtual void scrollRect(int dx, int dy, const WebCore::IntRect& clip_rect); + virtual void popupOpened(WebCore::Widget* widget, + const WebCore::IntRect& bounds); + virtual void popupClosed(WebCore::Widget* widget); + virtual void setCursor(const WebCore::Cursor& cursor); + virtual void setFocus(); + virtual const SkBitmap* getPreloadedResourceBitmap(int resource_id); + virtual void onScrollPositionChanged(WebCore::Widget* widget); + virtual const WTF::Vector<RefPtr<WebCore::Range> >* getTickmarks( + WebCore::Frame* frame); + virtual size_t getActiveTickmarkIndex(WebCore::Frame* frame); + + WebWidgetDelegate* delegate_; + gfx::Size size_; + + gfx::Point last_mouse_position_; + + // This is a non-owning ref. The popup will notify us via popupClosed() + // before it is destroyed. + WebCore::FramelessScrollView* widget_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(WebWidgetImpl); +}; + +#endif // WEBKIT_GLUE_WEBWIDGET_IMPL_H__ diff --git a/webkit/glue/window_open_disposition.h b/webkit/glue/window_open_disposition.h new file mode 100644 index 0000000..1bf6801 --- /dev/null +++ b/webkit/glue/window_open_disposition.h @@ -0,0 +1,45 @@ +// Copyright 2008, 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 WEBKIT_GLUE_WINDOW_OPEN_DISPOSITION_H__ +#define WEBKIT_GLUE_WINDOW_OPEN_DISPOSITION_H__ + +enum WindowOpenDisposition { + SUPPRESS_OPEN, + CURRENT_TAB, + NEW_FOREGROUND_TAB, + NEW_BACKGROUND_TAB, + NEW_POPUP, + NEW_WINDOW, + SAVE_TO_DISK, + OFF_THE_RECORD, + IGNORE_ACTION +}; + +#endif // WEBKIT_GLUE_WINDOW_OPEN_DISPOSITION_H__ |