diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/common | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
178 files changed, 38120 insertions, 0 deletions
diff --git a/chrome/common/SConscript b/chrome/common/SConscript new file mode 100644 index 0000000..b84a29b3 --- /dev/null +++ b/chrome/common/SConscript @@ -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.
+
+Import('env', 'env_test')
+
+env = env.Clone()
+
+env.Prepend(
+ CPPPATH = [
+ env.Dir('../app/resources'),
+ env.Dir('#/tools/build/win'),
+ env.Dir('#/..'),
+ ],
+ CPPDEFINES = [
+ 'U_STATIC_IMPLEMENTATION',
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ '/Wp64',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+env.Append(
+ CPPPATH = [
+ 'third_party/wtl/include',
+ '$NPAPI_DIR',
+ '$LIBXML_DIR/scons/include',
+ '$LIBXML_DIR/include',
+
+ 'app',
+ '$WEBKIT_DIR/build/localized_strings',
+
+ '$SKIA_DIR/include',
+ '$SKIA_DIR/include/corecg',
+ '$SKIA_DIR/platform',
+ '$LIBPNG_DIR',
+ '$ZLIB_DIR',
+ env.Dir('#../breakpad/src'),
+ '$LIBJPEG_DIR',
+ '$ICU38_DIR/public/common',
+ '$ICU38_DIR/public/i18n',
+ ],
+)
+
+input_files = [
+ 'animation.cc',
+ 'child_process.cc',
+ 'chrome_constants.cc',
+ 'chrome_counters.cc',
+ 'chrome_paths.cc',
+ 'chrome_plugin_lib.cc',
+ 'chrome_plugin_util.cc',
+ 'chrome_process_filter.cc',
+ 'chrome_switches.cc',
+ 'classfactory.cc',
+ 'clipboard_service.cc',
+ 'debug_flags.cc',
+ 'drag_drop_types.cc',
+ 'env_util.cc',
+ 'env_vars.cc',
+ 'gfx/chrome_canvas.cc',
+ 'gfx/chrome_font.cc',
+ 'gfx/color_utils.cc',
+ 'gfx/emf.cc',
+ 'gfx/icon_util.cc',
+ 'gfx/path.cc',
+ 'gfx/url_elider.cc',
+ 'ipc_channel.cc',
+ 'ipc_channel_proxy.cc',
+ 'ipc_logging.cc',
+ 'ipc_message.cc',
+ 'ipc_sync_channel.cc',
+ 'ipc_sync_message.cc',
+ 'jpeg_codec.cc',
+ 'json_value_serializer.cc',
+ 'jstemplate_builder.cc',
+ 'l10n_util.cc',
+ 'libxml_utils.cc',
+ 'logging_chrome.cc',
+ 'message_router.cc',
+ 'net/cookie_monster_sqlite.cc',
+ 'net/url_request_intercept_job.cc',
+ 'notification_service.cc',
+ 'os_exchange_data.cc',
+ 'plugin_messages.cc',
+ 'pref_names.cc',
+ 'pref_service.cc',
+ 'process_watcher.cc',
+ 'rand_util.cc',
+ 'render_messages.cc',
+ 'resource_bundle.cc',
+ 'resource_dispatcher.cc',
+ 'security_filter_peer.cc',
+ 'slide_animation.cc',
+ 'sqlite_compiled_statement.cc',
+ 'sqlite_utils.cc',
+ 'task_queue.cc',
+ 'throb_animation.cc',
+ 'thumbnail_score.cc',
+ 'time_format.cc',
+ 'visitedlink_common.cc',
+ 'win_safe_util.cc',
+ 'win_util.cc',
+ 'worker_thread_ticker.cc',
+]
+
+env.StaticLibrary('common', input_files)
+
+
+env_test = env_test.Clone()
+
+env_test.Append(
+ CPPPATH = [
+ '$SKIA_DIR/include',
+ '$SKIA_DIR/include/corecg',
+ '$SKIA_DIR/platform',
+ '#/..',
+ '$GTEST_DIR/include',
+ ],
+ LINKFLAGS = [
+ '/INCREMENTAL',
+
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+
+ '/DEBUG',
+ '/MACHINE:X86',
+ '/FIXED:No',
+ ],
+ LIBS = [
+ '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',
+ ],
+)
+
+ipc_tests_files = [
+ '$BASE_DIR/perftimer$OBJSUFFIX',
+ 'ipc_fuzzing_tests.cc',
+ 'ipc_tests.cc',
+]
+
+libs = [
+ '$TESTING_DIR/gtest.lib',
+ '$ICU38_DIR/icuuc.lib',
+ 'common.lib',
+ '$BASE_DIR/base.lib',
+]
+
+ipc_tests = env_test.Program('ipc_tests.exe', ipc_tests_files + libs)
+
+i = env_test.Install('$TARGET_ROOT', ipc_tests)
+Alias('chrome', i)
diff --git a/chrome/common/animation.cc b/chrome/common/animation.cc new file mode 100644 index 0000000..585f1b7 --- /dev/null +++ b/chrome/common/animation.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 "base/message_loop.h" +#include "chrome/common/animation.h" + +Animation::Animation(int frame_rate, + AnimationDelegate* delegate) + : animating_(false), + frame_rate_(frame_rate), + timer_interval_(CalculateInterval(frame_rate)), + duration_(0), + iteration_count_(0), + current_iteration_(0), + state_(0.0), + delegate_(delegate), + timer_(TimeDelta::FromMilliseconds(timer_interval_)) { + timer_.set_unowned_task(this); +} + +Animation::Animation(int duration, + int frame_rate, + AnimationDelegate* delegate) + : animating_(false), + frame_rate_(frame_rate), + timer_interval_(CalculateInterval(frame_rate)), + duration_(0), + iteration_count_(0), + current_iteration_(0), + state_(0.0), + delegate_(delegate), + timer_(TimeDelta::FromMilliseconds(timer_interval_)) { + timer_.set_unowned_task(this); + + SetDuration(duration); +} + +Animation::~Animation() { +} + +void Animation::Reset() { + current_iteration_ = 0; +} + +double Animation::GetCurrentValue() const { + // Default is linear relationship, subclass to adapt. + return state_; +} + +void Animation::Start() { + if (!animating_) { + timer_.Start(); + + animating_ = true; + if (delegate_) + delegate_->AnimationStarted(this); + } +} + +void Animation::Stop() { + if (animating_) { + timer_.Stop(); + + animating_ = false; + if (delegate_) { + if (state_ >= 1.0) + delegate_->AnimationEnded(this); + else + delegate_->AnimationCanceled(this); + } + } +} + +void Animation::End() { + if (animating_) { + timer_.Stop(); + + animating_ = false; + AnimateToState(1.0); + if (delegate_) + delegate_->AnimationEnded(this); + } +} + +bool Animation::IsAnimating() { + return animating_; +} + +void Animation::Run() { + state_ = static_cast<double>(++current_iteration_) / iteration_count_; + + if (state_ >= 1.0) + state_ = 1.0; + + AnimateToState(state_); + if (delegate_) + delegate_->AnimationProgressed(this); + + if (state_ == 1.0) + Stop(); +} + +void Animation::SetDuration(int duration) { + duration_ = duration; + if (duration_ < timer_interval_) + duration_ = timer_interval_; + iteration_count_ = duration_ / timer_interval_; + + // Changing the number of iterations forces us to reset the + // animation to the first iteration. + current_iteration_ = 0; +} + +int Animation::CalculateInterval(int frame_rate) { + int timer_interval = 1000 / frame_rate; + if (timer_interval < 10) + timer_interval = 10; + return timer_interval; +} diff --git a/chrome/common/animation.h b/chrome/common/animation.h new file mode 100644 index 0000000..17db439 --- /dev/null +++ b/chrome/common/animation.h @@ -0,0 +1,144 @@ +// 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. +// Inspired by NSAnimation + +#ifndef CHROME_COMMON_ANIMATION_H__ +#define CHROME_COMMON_ANIMATION_H__ + +#include "base/task.h" +#include "base/timer.h" + +class Animation; + +// AnimationDelegate +// +// Implement this interface when you want to receive notifications about the +// state of an animation. +// +class AnimationDelegate { + public: + // Called when an animation has started. + virtual void AnimationStarted(const Animation* animation) { + } + + // Called when an animation has completed. + virtual void AnimationEnded(const Animation* animation) { + } + + // Called when an animation has progressed. + virtual void AnimationProgressed(const Animation* animation) { + } + + // Called when an animation has been canceled. + virtual void AnimationCanceled(const Animation* animation) { + } +}; + +// Animation +// +// This class provides a basic implementation of an object that uses a timer +// to increment its state over the specified time and frame-rate. To +// actually do something useful with this you need to subclass it and override +// AnimateToState and optionally GetCurrentValue to update your state. +// +// The Animation notifies a delegate when events of interest occur. +// +// The practice is to instantiate a subclass and call Init and any other +// initialization specific to the subclass, and then call |Start|. The +// animation uses the current thread's message loop. +// +class Animation : public Task { + public: + // Initializes everything except the duration. + // + // Caller must make sure to call SetDuration() if they use this + // constructor; it is preferable to use the full one, but sometimes + // duration can change between calls to Start() and we need to + // expose this interface. + Animation(int frame_rate, AnimationDelegate* delegate); + + // Initializes all fields. + Animation(int duration, int frame_rate, AnimationDelegate* delegate); + virtual ~Animation(); + + // Reset state so that the animation can be started again. + virtual void Reset(); + + // Called when the animation progresses. Subclasses override this to + // efficiently update their state. + virtual void AnimateToState(double state) = 0; + + // Gets the value for the current state, according to the animation + // curve in use. This class provides only for a linear relationship, + // however subclasses can override this to provide others. + virtual double GetCurrentValue() const; + + // Start the animation. + void Start(); + + // Stop the animation. + void Stop(); + + // Skip to the end of the current animation. + void End(); + + // Return whether this animation is animating. + bool IsAnimating(); + + // The animation's Task::Run implementation + virtual void Run(); + + // Changes the length of the animation. This resets the current + // state of the animation to the beginning. + void SetDuration(int duration); + + protected: + // Calculates the timer interval from the constructor list. + int CalculateInterval(int frame_rate); + + // Whether or not we are currently animating. + bool animating_; + + int frame_rate_; + int timer_interval_; + int duration_; + + // For determining state. + int iteration_count_; + int current_iteration_; + double state_; + + AnimationDelegate* delegate_; + + RepeatingTimer timer_; + + DISALLOW_EVIL_CONSTRUCTORS(Animation); +}; + +#endif // CHROME_COMMON_ANIMATION_H__ diff --git a/chrome/common/animation_unittest.cc b/chrome/common/animation_unittest.cc new file mode 100644 index 0000000..a35f5c0 --- /dev/null +++ b/chrome/common/animation_unittest.cc @@ -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. + +#include "base/logging.h" +#include "base/message_loop.h" +#include "chrome/common/animation.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace std; + +namespace { + class AnimationTest: public testing::Test { + }; +}; + +/////////////////////////////////////////////////////////////////////////////// +// RunAnimation + +class RunAnimation : public Animation { + public: + RunAnimation(int frame_rate, AnimationDelegate* delegate) + : Animation(frame_rate, delegate) { + } + + virtual void AnimateToState(double state) { + EXPECT_LE(0.0, state); + EXPECT_GE(1.0, state); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// CancelAnimation + +class CancelAnimation : public Animation { + public: + CancelAnimation(int duration, int frame_rate, AnimationDelegate* delegate) + : Animation(duration, frame_rate, delegate) { + } + + virtual void AnimateToState(double state) { + if (state >= 0.5) + Stop(); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// LinearCase + +class TestAnimationDelegate : public AnimationDelegate { + public: + TestAnimationDelegate() : + canceled_(false), + finished_(false) { + } + + virtual void AnimationStarted(const Animation* animation) { + } + + virtual void AnimationEnded(const Animation* animation) { + finished_ = true; + MessageLoop::current()->Quit(); + } + + virtual void AnimationCanceled(const Animation* animation) { + finished_ = true; + canceled_ = true; + MessageLoop::current()->Quit(); + } + + bool finished() { + return finished_; + } + + bool canceled() { + return canceled_; + } + + private: + bool finished_; + bool canceled_; +}; + +TEST(AnimationTest, RunCase) { + TestAnimationDelegate ad; + RunAnimation a1(150, &ad); + a1.SetDuration(2000); + a1.Start(); + MessageLoop::current()->Run(); + + EXPECT_TRUE(ad.finished()); + EXPECT_FALSE(ad.canceled()); +} + +TEST(AnimationTest, CancelCase) { + TestAnimationDelegate ad; + CancelAnimation a2(2000, 150, &ad); + a2.Start(); + MessageLoop::current()->Run(); + + EXPECT_TRUE(ad.finished()); + EXPECT_TRUE(ad.canceled()); +} diff --git a/chrome/common/bzip2_unittest.cc b/chrome/common/bzip2_unittest.cc new file mode 100644 index 0000000..977f42fc --- /dev/null +++ b/chrome/common/bzip2_unittest.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. + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/bzip2/bzlib.h" + +namespace { + class Bzip2Test : public testing::Test { + }; +}; + +// This test does a simple round trip to test that the bzip2 library is +// present and working. +TEST(Bzip2Test, Roundtrip) { + char input[] = "Test Data, More Test Data, Even More Data of Test"; + char output[256]; + + memset(output, 0, arraysize(output)); + + bz_stream stream; + stream.bzalloc = NULL; + stream.bzfree = NULL; + stream.opaque = NULL; + int result = BZ2_bzCompressInit(&stream, + 9, // 900k block size + 0, // quiet + 0); // default work factor + ASSERT_EQ(BZ_OK, result); + + stream.next_in = input; + stream.avail_in = arraysize(input) - 1; + stream.next_out = output; + stream.avail_out = arraysize(output); + do { + result = BZ2_bzCompress(&stream, BZ_FINISH); + } while (result == BZ_FINISH_OK); + ASSERT_EQ(BZ_STREAM_END, result); + result = BZ2_bzCompressEnd(&stream); + ASSERT_EQ(BZ_OK, result); + int written = stream.total_out_lo32; + + // Make sure we wrote something; otherwise not sure what to expect + ASSERT_GT(written, 0); + + // Now decompress and check that we got the same thing. + result = BZ2_bzDecompressInit(&stream, 0, 0); + ASSERT_EQ(BZ_OK, result); + char output2[256]; + memset(output2, 0, arraysize(output2)); + + stream.next_in = output; + stream.avail_in = written; + stream.next_out = output2; + stream.avail_out = arraysize(output2); + + do { + result = BZ2_bzDecompress(&stream); + } while (result == BZ_OK); + ASSERT_EQ(result, BZ_STREAM_END); + result = BZ2_bzDecompressEnd(&stream); + ASSERT_EQ(result, BZ_OK); + + EXPECT_EQ(arraysize(input) - 1, stream.total_out_lo32); + EXPECT_STREQ(input, output2); +} diff --git a/chrome/common/child_process.cc b/chrome/common/child_process.cc new file mode 100644 index 0000000..9c4310a --- /dev/null +++ b/chrome/common/child_process.cc @@ -0,0 +1,114 @@ +// 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 "chrome/common/child_process.h" + +#include "base/basictypes.h" + +ChildProcess* ChildProcess::child_process_; +MessageLoop* ChildProcess::main_thread_loop_; +LONG ChildProcess::ref_count_; +HANDLE ChildProcess::shutdown_event_; + + +ChildProcess::ChildProcess() { + DCHECK(!child_process_); +} + +ChildProcess::~ChildProcess() { + DCHECK(child_process_ == this); +} + +// Called on any thread +void ChildProcess::AddRefProcess() { + InterlockedIncrement(&ref_count_); +} + +// Called on any thread +void ChildProcess::ReleaseProcess() { + DCHECK(ref_count_ > 0); + DCHECK(child_process_); + if (InterlockedDecrement(&ref_count_) == 0) + child_process_->OnFinalRelease(); +} + +void ChildProcess::OnFinalRelease() { + DCHECK(main_thread_loop_); + main_thread_loop_->Quit(); +} + +HANDLE ChildProcess::GetShutDownEvent() { + return shutdown_event_; +} + +// On error, it is OK to leave the global pointers, as long as the only +// non-NULL pointers are valid. GlobalCleanup will always get called, which +// will delete any non-NULL services. +bool ChildProcess::GlobalInit(const std::wstring &channel_name, + ChildProcessFactoryInterface *factory) { + // OK to be called multiple times. + if (main_thread_loop_) + return true; + + if (channel_name.empty()) { + NOTREACHED() << "Unable to get the channel name"; + return false; + } + + // Remember the current message loop, so we can communicate with this thread + // again when we need to shutdown (see ReleaseProcess). + main_thread_loop_ = MessageLoop::current(); + + // An event that will be signalled when we shutdown. + shutdown_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); + + child_process_ = factory->Create(channel_name); + return true; +} + +void ChildProcess::GlobalCleanup() { + // Signal this event before destroying the child process. That way all + // background threads. + // For example, in the renderer the RenderThread instances will be able to + // notice shutdown before the render process begins waiting for them to exit. + SetEvent(shutdown_event_); + + // Destroy the child process first to force all background threads to + // terminate before we bring down other resources. (We null pointers + // just in case.) + child_process_->Cleanup(); + delete child_process_; + child_process_ = NULL; + + main_thread_loop_ = NULL; + + CloseHandle(shutdown_event_); + shutdown_event_ = NULL; +} diff --git a/chrome/common/child_process.h b/chrome/common/child_process.h new file mode 100644 index 0000000..ca88d25 --- /dev/null +++ b/chrome/common/child_process.h @@ -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. + +#ifndef CHROME_COMMON_CHILD_PROCESS_H__ +#define CHROME_COMMON_CHILD_PROCESS_H__ + +#include <string> +#include <vector> +#include "base/basictypes.h" +#include "base/message_loop.h" + + +class ChildProcess; + +class ChildProcessFactoryInterface { + public: + virtual ChildProcess* Create(const std::wstring& channel_name) = 0; +}; + +template<class T> +class ChildProcessFactory : public ChildProcessFactoryInterface { + virtual ChildProcess* Create(const std::wstring& channel_name) { + return new T(channel_name); + } +}; + +// Base class for child processes of the browser process (i.e. renderer and +// plugin host). This is a singleton object for each child process. +class ChildProcess { + public: + + // initializes/cleansup the global variables, services, and libraries + // Derived classes need to implement a static GlobalInit, that calls + // into ChildProcess::GlobalInit with a class factory +//static bool GlobalInit(const std::wstring& channel_name); + static void GlobalCleanup(); + + // These are used for ref-counting the child process. The process shuts + // itself down when the ref count reaches 0. These functions may be called + // on any thread. + // For example, in the renderer process, generally each tab managed by this + // process will hold a reference to the process, and release when closed. + static void AddRefProcess(); + static void ReleaseProcess(); + + // A global event object that is signalled when the main thread's message + // loop exits. This gives background threads a way to observe the main + // thread shutting down. This can be useful when a background thread is + // waiting for some information from the browser process. If the browser + // process goes away prematurely, the background thread can at least notice + // the child processes's main thread exiting to determine that it should give + // up waiting. + // For example, see the renderer code used to implement + // webkit_glue::GetCookies. + static HANDLE GetShutDownEvent(); + + // You must call Init after creating this object before it will be valid + ChildProcess(); + virtual ~ChildProcess(); + + protected: + static bool GlobalInit(const std::wstring& channel_name, + ChildProcessFactoryInterface* factory); + + static int GetProcessRefcount() { return static_cast<int>(ref_count_);} + + // The singleton instance for this process. + static ChildProcess* child_process_; + + static MessageLoop* main_thread_loop_; + + // Derived classes can override this to alter the behavior when the ref count + // reaches 0. The default implementation calls Quit on the main message loop + // which causes the process to shutdown. Note, this can be called on any + // thread. (See ReleaseProcess) + virtual void OnFinalRelease(); + + private: + // Derived classes can override this to handle any cleanup, called by + // GlobalCleanup. + virtual void Cleanup() {} + + static LONG ref_count_; + static HANDLE shutdown_event_; + + DISALLOW_EVIL_CONSTRUCTORS(ChildProcess); +}; + +#endif // CHROME_COMMON_CHILD_PROCESS_H__ diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc new file mode 100644 index 0000000..9d29cb08 --- /dev/null +++ b/chrome/common/chrome_constants.cc @@ -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. + +#include "chrome/common/chrome_constants.h" + +namespace chrome { +// The following should not be used for UI strings; they are meant +// for system strings only. UI changes should be made in the GRD. +const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe"; +const wchar_t kBrowserAppName[] = L"Chrome"; +const wchar_t kMessageWindowClass[] = L"Chrome_MessageWindow"; +const wchar_t kExternalTabWindowClass[] = L"Chrome_ExternalTabContainer"; +const wchar_t kCrashReportLog[] = L"Reported Crashes.txt"; +const wchar_t kTestingInterfaceDLL[] = L"testing_interface.dll"; +const wchar_t kNotSignedInProfile[] = L"Default"; +const wchar_t kNotSignedInID[] = L"not-signed-in"; +const wchar_t kStatsFilename[] = L"ChromeStats"; +const wchar_t kBrowserResourcesDll[] = L"chrome.dll"; + +// filenames +const wchar_t kArchivedHistoryFilename[] = L"Archived History"; +const wchar_t kCacheDirname[] = L"Cache"; +const wchar_t kChromePluginDataDirname[] = L"Plugin Data"; +const wchar_t kCookieFilename[] = L"Cookies"; +const wchar_t kHistoryFilename[] = L"History"; +const wchar_t kLocalStateFilename[] = L"Local State"; +const wchar_t kPreferencesFilename[] = L"Preferences"; +const wchar_t kSafeBrowsingFilename[] = L"Safe Browsing"; +const wchar_t kThumbnailsFilename[] = L"Thumbnails"; +const wchar_t kUserDataDirname[] = L"User Data"; +const wchar_t kWebDataFilename[] = L"Web Data"; + +// Note, this shouldn't go above 64. See bug 535234. +const unsigned int kMaxRendererProcessCount = 20; +const int kStatsMaxThreads = 32; +const int kStatsMaxCounters = 300; + +// We don't enable record mode in the released product because users could +// potentially be tricked into running a product in record mode without +// knowing it. Enable in debug builds. Playback mode is allowed always, +// because it is useful for testing and not hazardous by itself. +#ifdef DEBUG +const bool kRecordModeEnabled = true; +#else +const bool kRecordModeEnabled = false; +#endif +} diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h new file mode 100644 index 0000000..77c8198 --- /dev/null +++ b/chrome/common/chrome_constants.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. + +// A handful of resource-like constants related to the Chrome application. + +#ifndef CHROME_COMMON_CHROME_CONSTANTS_H__ +#define CHROME_COMMON_CHROME_CONSTANTS_H__ + +namespace chrome { + +extern const wchar_t kBrowserProcessExecutableName[]; +extern const wchar_t kBrowserAppName[]; +extern const wchar_t kMessageWindowClass[]; +extern const wchar_t kExternalTabWindowClass[]; +extern const wchar_t kCrashReportLog[]; +extern const wchar_t kTestingInterfaceDLL[]; +extern const wchar_t kNotSignedInProfile[]; +extern const wchar_t kNotSignedInID[]; +extern const wchar_t kStatsFilename[]; +extern const wchar_t kBrowserResourcesDll[]; + +// filenames +extern const wchar_t kArchivedHistoryFilename[]; +extern const wchar_t kCacheDirname[]; +extern const wchar_t kChromePluginDataDirname[]; +extern const wchar_t kCookieFilename[]; +extern const wchar_t kHistoryFilename[]; +extern const wchar_t kLocalStateFilename[]; +extern const wchar_t kPreferencesFilename[]; +extern const wchar_t kSafeBrowsingFilename[]; +extern const wchar_t kThumbnailsFilename[]; +extern const wchar_t kUserDataDirname[]; +extern const wchar_t kWebDataFilename[]; + +extern const unsigned int kMaxRendererProcessCount; +extern const int kStatsMaxThreads; +extern const int kStatsMaxCounters; + +extern const bool kRecordModeEnabled; +} + +#endif // CHROME_COMMON_CHROME_CONSTANTS_H__ diff --git a/chrome/common/chrome_counters.cc b/chrome/common/chrome_counters.cc new file mode 100644 index 0000000..1474027 --- /dev/null +++ b/chrome/common/chrome_counters.cc @@ -0,0 +1,80 @@ +// 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 "chrome/common/chrome_counters.h" + +namespace chrome { + +// Note: We use the construct-on-first-use pattern here, because we don't +// want to fight with any static initializer ordering problems later. +// The downside of this is that the objects don't ever get cleaned up. +// But they are small and this is okay. + +// Note: Because these are constructed on-first-use, there is a slight +// race condition - two threads could initialize the same counter. +// If this happened, the stats table would still work just fine; +// we'd leak the extraneous StatsCounter object once, and that +// would be it. But these are small objects, so this is ok. + +StatsCounter& Counters::ipc_send_counter() { + static StatsCounter* ctr = new StatsCounter(L"IPC.SendMsgCount"); + return *ctr; +} + +StatsCounterTimer& Counters::chrome_main() { + static StatsCounterTimer* ctr = new StatsCounterTimer(L"Chrome.Init"); + return *ctr; +} + +StatsCounterTimer& Counters::renderer_main() { + static StatsCounterTimer* ctr = new StatsCounterTimer(L"Chrome.RendererInit"); + return *ctr; +} + +StatsCounterTimer& Counters::spellcheck_init() { + static StatsCounterTimer* ctr = new StatsCounterTimer(L"SpellCheck.Init"); + return *ctr; +} + +StatsRate& Counters::spellcheck_lookup() { + static StatsRate* ctr = new StatsRate(L"SpellCheck.Lookup"); + return *ctr; +} + +StatsCounterTimer& Counters::plugin_load() { + static StatsCounterTimer* ctr = new StatsCounterTimer(L"ChromePlugin.Load"); + return *ctr; +} + +StatsRate& Counters::plugin_intercept() { + static StatsRate* ctr = new StatsRate(L"ChromePlugin.Intercept"); + return *ctr; +} + +} // namespace chrome diff --git a/chrome/common/chrome_counters.h b/chrome/common/chrome_counters.h new file mode 100644 index 0000000..85f9c5d --- /dev/null +++ b/chrome/common/chrome_counters.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. + +// Counters used within the browser. + +#ifndef CHROME_COMMON_CHROME_COUNTERS_H__ +#define CHROME_COMMON_CHROME_COUNTERS_H__ + +#include "base/stats_counters.h" + +namespace chrome { + +class Counters { + public: + // The number of messages sent on IPC channels. + static StatsCounter& ipc_send_counter(); + + // The amount of time spent in chrome initialization. + static StatsCounterTimer& chrome_main(); + + // The amount of time spent in renderer initialization. + static StatsCounterTimer& renderer_main(); + + // Time spent in spellchecker initialization. + static StatsCounterTimer& spellcheck_init(); + + // Time/Count of spellcheck lookups. + static StatsRate& spellcheck_lookup(); + + // Time spent loading the Chrome plugins. + static StatsCounterTimer& plugin_load(); + + // Time/Count of plugin network interception. + static StatsRate& plugin_intercept(); + +}; + +} // namespace chrome + +#endif // CHROME_COMMON_CHROME_COUNTERS_H__ diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc new file mode 100644 index 0000000..9d3e2eb --- /dev/null +++ b/chrome/common/chrome_paths.cc @@ -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. + +#include <windows.h> +#include <shellapi.h> +#include <shlobj.h> + +#include "chrome/common/chrome_paths.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" + +namespace chrome { + +bool GetUserDirectory(int directory_type, std::wstring* result) { + wchar_t path_buf[MAX_PATH]; + if (FAILED(SHGetFolderPath(NULL, directory_type, NULL, + SHGFP_TYPE_CURRENT, path_buf))) + return false; + result->assign(path_buf); + return true; +} + +// Gets the default user data directory, regardless of whether +// DIR_USER_DATA has been overridden by a command-line option. +bool GetDefaultUserDataDirectory(std::wstring* result) { + if (!PathService::Get(base::DIR_LOCAL_APP_DATA, result)) + return false; + file_util::AppendToPath(result, L"Google"); + file_util::AppendToPath(result, chrome::kBrowserAppName); + file_util::AppendToPath(result, chrome::kUserDataDirname); + return true; +} + +bool GetGearsPluginPathFromCommandLine(std::wstring *path) { +#ifndef NDEBUG + // for debugging, support a cmd line based override + CommandLine command_line; + *path = command_line.GetSwitchValue(switches::kGearsPluginPathOverride); + return !path->empty(); +#else + return false; +#endif +} + +bool PathProvider(int key, std::wstring* result) { + // Some keys are just aliases... + switch (key) { + case chrome::DIR_APP: + return PathService::Get(base::DIR_MODULE, result); + case chrome::DIR_LOGS: +#ifndef NDEBUG + return PathService::Get(chrome::DIR_USER_DATA, result); +#else + return PathService::Get(base::DIR_EXE, result); +#endif + case chrome::FILE_RESOURCE_MODULE: + return PathService::Get(base::FILE_MODULE, result); + } + + // We need to go compute the value. It would be nice to support paths with + // names longer than MAX_PATH, but the system functions don't seem to be + // designed for it either, with the exception of GetTempPath (but other + // things will surely break if the temp path is too long, so we don't bother + // handling it. + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + + // Assume that we will need to create the directory if it does not already + // exist. This flag can be set to true to prevent checking. + bool exists = false; + + std::wstring cur; + switch (key) { + case chrome::DIR_USER_DATA: + if (!GetDefaultUserDataDirectory(&cur)) + return false; + break; + case chrome::DIR_USER_DOCUMENTS: + if (!GetUserDirectory(CSIDL_MYDOCUMENTS, &cur)) + return false; + break; + case chrome::DIR_CRASH_DUMPS: + // The crash reports are always stored relative to the default user data + // directory. This avoids the problem of having to re-initialize the + // exception handler after parsing command line options, which may + // override the location of the app's profile directory. + if (!GetDefaultUserDataDirectory(&cur)) + return false; + file_util::AppendToPath(&cur, L"Crash Reports"); + break; + case chrome::DIR_DEFAULT_DOWNLOAD: + if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + cur = system_buffer; + exists = true; + break; + case chrome::DIR_RESOURCES: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::AppendToPath(&cur, L"resources"); + break; + case chrome::DIR_INSPECTOR: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::AppendToPath(&cur, L"Resources"); + file_util::AppendToPath(&cur, L"Inspector"); + exists = true; + break; + case chrome::DIR_THEMES: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::AppendToPath(&cur, L"themes"); + break; + case chrome::DIR_LOCALES: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::AppendToPath(&cur, L"locales"); + break; + case chrome::DIR_APP_DICTIONARIES: + if (!PathService::Get(base::DIR_EXE, &cur)) + return false; + file_util::AppendToPath(&cur, L"Dictionaries"); + break; + case chrome::FILE_LOCAL_STATE: + if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) + return false; + file_util::AppendToPath(&cur, chrome::kLocalStateFilename); + exists = true; // don't trigger directory creation code + break; + case chrome::FILE_RECORDED_SCRIPT: + if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) + return false; + file_util::AppendToPath(&cur, L"script.log"); + exists = true; + break; + case chrome::FILE_GEARS_PLUGIN: + if (!GetGearsPluginPathFromCommandLine(&cur)) { + if (!PathService::Get(base::DIR_EXE, &cur)) + return false; + file_util::AppendToPath(&cur, L"plugins"); + file_util::AppendToPath(&cur, L"gears"); + file_util::AppendToPath(&cur, L"gears.dll"); + } + exists = true; + break; + // The following are only valid in the development environment, and + // will fail if executed from an installed executable (because the + // generated path won't exist). + case chrome::DIR_TEST_DATA: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::UpOneDirectory(&cur); + file_util::AppendToPath(&cur, L"test"); + file_util::AppendToPath(&cur, L"data"); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + exists = true; + break; + case chrome::DIR_TEST_TOOLS: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::UpOneDirectory(&cur); + file_util::AppendToPath(&cur, L"tools"); + file_util::AppendToPath(&cur, L"test"); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + exists = true; + break; + case chrome::FILE_PYTHON_RUNTIME: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::UpOneDirectory(&cur); // chrome + file_util::UpOneDirectory(&cur); + file_util::AppendToPath(&cur, L"third_party"); + file_util::AppendToPath(&cur, L"python_24"); + file_util::AppendToPath(&cur, L"python.exe"); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + exists = true; + break; + case chrome::FILE_TEST_SERVER: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + file_util::UpOneDirectory(&cur); + file_util::AppendToPath(&cur, L"tools"); + file_util::AppendToPath(&cur, L"test"); + file_util::AppendToPath(&cur, L"testserver"); + file_util::AppendToPath(&cur, L"testserver.py"); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + exists = true; + break; + default: + return false; + } + + if (!exists && !file_util::PathExists(cur) && !file_util::CreateDirectory(cur)) + return false; + + result->swap(cur); + return true; +} + +// This cannot be done as a static initializer sadly since Visual Studio will +// eliminate this object file if there is no direct entry point into it. +void RegisterPathProvider() { + PathService::RegisterProvider(PathProvider, PATH_START, PATH_END); +} + +} // namespace chrome diff --git a/chrome/common/chrome_paths.h b/chrome/common/chrome_paths.h new file mode 100644 index 0000000..fcf57e4 --- /dev/null +++ b/chrome/common/chrome_paths.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 CHROME_COMMON_CHROME_PATHS_H__ +#define CHROME_COMMON_CHROME_PATHS_H__ + +// This file declares path keys for the chrome module. These can be used with +// the PathService to access various special directories and files. + +namespace chrome { + +enum { + PATH_START = 1000, + + DIR_APP = PATH_START, // directory where dlls and data reside + DIR_LOGS, // directory where logs should be written + DIR_USER_DATA, // directory where user data can be written + DIR_CRASH_DUMPS, // directory where crash dumps are written + DIR_DEFAULT_DOWNLOAD, // directory where downloads go by default + DIR_RESOURCES, // directory where application resources are stored + DIR_INSPECTOR, // directory where web inspector is located + DIR_THEMES, // directory where theme dll files are stored + DIR_LOCALES, // directory where locale resources are stored + DIR_APP_DICTIONARIES, // directory where the global dictionaries are + DIR_USER_DOCUMENTS, // directory for a user's "My Documents" + FILE_RESOURCE_MODULE, // full path and filename of the module that contains + // embedded resources (version, strings, images, etc.) + FILE_LOCAL_STATE, // path and filename to the file in which machine/ + // installation-specific state is saved + FILE_RECORDED_SCRIPT, // full path to the script.log file that contains + // recorded browser events for playback. + FILE_GEARS_PLUGIN, // full path to the gears.dll plugin file. + + // Valid only in development environment; TODO(darin): move these + DIR_TEST_DATA, // directory where unit test data resides + DIR_TEST_TOOLS, // directory where unit test tools reside + FILE_TEST_SERVER, // simple HTTP server for testing the network stack + FILE_PYTHON_RUNTIME, // Python runtime, used for running the test server + + PATH_END +}; + +// Call once to register the provider for the path keys defined above. +void RegisterPathProvider(); + +} // namespace chrome + +#endif // CHROME_COMMON_CHROME_PATHS_H__ diff --git a/chrome/common/chrome_plugin_api.h b/chrome/common/chrome_plugin_api.h new file mode 100644 index 0000000..1dd08727 --- /dev/null +++ b/chrome/common/chrome_plugin_api.h @@ -0,0 +1,507 @@ +// 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 header specifies the Chrome Plugin API. It is based heavily on NPAPI. +// The key difference is that Chrome plugins can be loaded for the lifetime +// of the browser, and are not tied to a specific web page. +// +// NOTE: This API is not final and may change or go away at any point. +// +// All strings in the API are UTF8-encoded unless otherwise noted. + +#ifndef CHROME_COMMON_CHROME_PLUGIN_API_H__ +#define CHROME_COMMON_CHROME_PLUGIN_API_H__ + +#include "base/basictypes.h" + +#ifndef STDCALL +#ifdef WIN32 +#define STDCALL __stdcall +#else +#define STDCALL +#endif // WIN32 +#endif // STDCALL + +#ifdef __cplusplus +extern "C" { +#endif + +// The current version of the API, used by the 'version' field of CPPluginFuncs +// and CPBrowserFuncs. +#define CP_MAJOR_VERSION 0 +#define CP_MINOR_VERSION 5 +#define CP_VERSION ((CP_MAJOR_VERSION << 8) | (CP_MINOR_VERSION)) + +#define CP_GET_MAJOR_VERSION(version) ((version & 0xff00) >> 8) +#define CP_GET_MINOR_VERSION(version) (version & 0x00ff) + +typedef unsigned char CPBool; + +// Chrome plugins can be loaded into different process types. +typedef enum { + CP_PROCESS_BROWSER = 0, + CP_PROCESS_PLUGIN, +} CPProcessType; + +// Return codes. Error values are negative. +typedef enum { + // No error + CPERR_SUCCESS = 0, + + // (network) An asynchronous IO operation is not complete + CPERR_IO_PENDING = -1, + + // Generic failure + CPERR_FAILURE = -2, + + // The API versions used by plugin and host are incompatible + CPERR_INVALID_VERSION = -3, + + // The operation was cancelled + CPERR_CANCELLED = -4, + + // An invalid parameter was passed + CPERR_INVALID_PARAMETER = -5, +} CPError; + +// Types of response info metadata to query using CPP_GetResponseInfo. +typedef enum { + // HTTP status code. + CPRESPONSEINFO_HTTP_STATUS = 0, + + // Raw headers from the server, including the status line. Headers should + // be delimited by "\0", and end with "\0\0" (a blank line). + CPRESPONSEINFO_HTTP_RAW_HEADERS = 1, +} CPResponseInfoType; + +// An identifier for the plugin used by the browser. +typedef struct _CPID_t { + int unused; +} CPID_t; +typedef struct _CPID_t* CPID; + +// An identifier that encapsulates the browsing context needed by various APIs. +// This includes information about what tab a request was made from, and what +// profile is active. Note that this ID is global to all processes, so it can +// be passed from one process to another. The value 0 is reserved for an +// undefined context. +typedef uint32 CPBrowsingContext; + +// Types of context info to query using CPB_GetBrowsingContextInfo. +typedef enum { + // The data directory for the profile associated with this context as a + // pointer to a null-terminated string. The plugin can save persistent data + // to this directory. The returned pointer should be freed using CPB_Free. + CPBROWSINGCONTEXT_DATA_DIR_PTR = 0, + + // The locale language code used for the browser UI. The returned pointer + // should be freed using CPB_Free. + CPBROWSINGCONTEXT_UI_LOCALE_PTR = 1, +} CPBrowsingContextInfoType; + +// A network request object. +typedef struct _CPRequest { + void* pdata; // plugin private data + const char* url; // the URL being requested + const char* method; // the request method as an uppercase string (ex: "GET") + CPBrowsingContext context; // context in which this request was made +} CPRequest; + +typedef enum { + CPREQUESTLOAD_NORMAL = 0, + + // This is "normal reload", meaning an if-none-match/if-modified-since query + CPREQUESTLOAD_VALIDATE_CACHE = 1 << 0, + + // This is "shift-reload", meaning a "pragma: no-cache" end-to-end fetch + CPREQUESTLOAD_BYPASS_CACHE = 1 << 1, + + // This is a back/forward style navigation where the cached content should + // be preferred over any protocol specific cache validation. + CPREQUESTLOAD_PREFERRING_CACHE = 1 << 2, + + // This is a navigation that will fail if it cannot serve the requested + // resource from the cache (or some equivalent local store). + CPREQUESTLOAD_ONLY_FROM_CACHE = 1 << 3, + + // This is a navigation that will not use the cache at all. It does not + // impact the HTTP request headers. + CPREQUESTLOAD_DISABLE_CACHE = 1 << 4, + + // This navigation should not be intercepted by plugins. + CPREQUESTLOAD_DISABLE_INTERCEPT = 1 << 5, + + // This request should be loaded synchronously. What this means is that + // CPR_StartRequest and CPR_Read will never return CPERR_IO_PENDING - they + // will block until a response is available, and return success or failure. + CPREQUESTLOAD_SYNCHRONOUS = 1 << 20, +} CPRequestLoadFlags; + +// +// Functions provided by plugin to host. +// + +// Called when the browser is unloading the plugin. +typedef CPError (STDCALL *CPP_ShutdownFunc)(void); + +// Returns true if the plugin is interested in handling this request. +typedef CPBool (STDCALL *CPP_ShouldInterceptRequestFunc)(CPRequest* request); + +// Called when an HTML dialog was closed. json_retval is the JSON string +// containing the return value sent back by the dialog (using Chrome's +// JavaScript DOM bindings). +typedef void (STDCALL *CPP_HtmlDialogClosedFunc)( + void* plugin_context, const char* json_retval); + +// Asks the plugin to handle the given command. 'command_data' is command- +// specific data used for some builtin commands: see gears_api.h for +// possible types. It is only valid for the duration of this call. +typedef CPError (STDCALL *CPP_HandleCommandFunc)( + CPBrowsingContext context, int command, void* command_data); + +// +// Functions provided by host to plugin. +// + +// Asks the host to handle the given command. 'command_data' is +// command-specific data used for some builtin commands: see gears_api.h for +// possible types. It is only valid for the duration of this call. +typedef CPError (STDCALL *CPB_HandleCommandFunc)( + CPID id, CPBrowsingContext context, int command, void* command_data); + +// Asks the browser to enable/disable request interception for this plugin for +// the given schemes. 'schemes' is an array of strings containing the scheme +// names the plugin wishes to handle; case is ignored. If 'schemes' is NULL or +// empty, request interception is disabled for this plugin. Multiple calls to +// this function will add to the existing set of enabled schemes. The browser +// should call the plugin's CPP_ShouldInterceptRequestFunc for any network +// requests it makes that match a given scheme. The browser may choose not to +// allow the plugin to intercept certain protocols. +typedef void (STDCALL *CPB_EnableRequestInterceptFunc)( + CPID id, const char** schemes, uint32 num_schemes); + +// Asks the browser to create a request object for the given method/url. +// Returns CPERR_SUCCESS and puts the new object into the 'request' field on +// success, or an error code on failure. The plugin must call CPR_EndRequest +// to clean up the request object when it is done with it. +typedef CPError (STDCALL *CPB_CreateRequestFunc)( + CPID id, CPBrowsingContext context, const char* method, const char* url, + CPRequest** request); + +// Queries the browser's cookie store for cookies set for the given URL. +// Sets 'cookies' to an allocated string containing the cookies as +// semicolon-delimited "name=value" pairs on success, NULL on failure. +// The memory should be freed using CPB_Free when done. +typedef CPError (STDCALL *CPB_GetCookiesFunc)( + CPID id, CPBrowsingContext context, const char* url, char** cookies); + +// Allocates memory for the given size using the browser's allocator. Call +// CPB_Free when done. +typedef void* (STDCALL *CPB_AllocFunc)(uint32 size); + +// Frees a pointer allocated by CPB_Alloc. +typedef void (STDCALL *CPB_FreeFunc)(void* memory); + + +// Sets a flag that influences when the plugin process created to host +// the plugin is shutdown. Generally, the plugin process is terminated +// when no more plugin instances exist, this is the default behavior. +// If keep_alive is non-zero, the process will not be terminated when +// the instance count goes to zero. Note: a non-zero keep_alive value +// does not prevent the plugin process from being terminated upon +// overall browser shutdown. +typedef void (STDCALL *CPB_SetKeepProcessAliveFunc)(CPID id, + CPBool keep_alive); + +// Asks the browser to show an HTML dialog to the user. The dialog contents +// should be loaded from the given URL. The 'json_arguments' is a JSON string +// that the dialog can fetch using Chrome's JavaScript DOM bindings. This call +// will block until the dialog is closed. On success, 'json_retval' will contain +// the JSON string sent back by the dialog (using Chrome's JavaScript DOM +// bindings), and CPERR_SUCCESS is returned. 'json_retval' should be freed +// using CPB_Free when done. +typedef CPError (STDCALL *CPB_ShowHtmlDialogModalFunc)( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, char** json_retval); + +// Similar to CPB_ShowHtmlDialogModalFunc, but does not block. When the dialog +// is closed, CPP_HtmlDialogClosed is called with the JSON return value and the +// given 'plugin_context', which may be used by the plugin to associate other +// data with the dialog. +typedef CPError (STDCALL *CPB_ShowHtmlDialogFunc)( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, void* plugin_context); + +// Get the browsing context associated with the given NPAPI instance. +typedef CPBrowsingContext (STDCALL *CPB_GetBrowsingContextFromNPPFunc)( + struct _NPP* npp); + +// Queries for some meta data associated with the given browsing context. See +// CPBrowsingContextInfoType for possible queries. If buf_size is too small to +// contain the entire data, the return value will indicate the size required. +// Otherwise, the return value is a CPError or CPERR_SUCCESS. +typedef int (STDCALL *CPB_GetBrowsingContextInfoFunc)( + CPID id, CPBrowsingContext context, CPBrowsingContextInfoType type, + void* buf, uint32 buf_size); + +// Given an URL string, returns the string of command-line arguments that should +// be passed to start the browser at the given URL. 'arguments' should be freed +// using CPB_Free when done. +typedef CPError (STDCALL *CPB_GetCommandLineArgumentsFunc)( + CPID id, CPBrowsingContext context, const char* url, char** arguments); + +// Asks the browser to let the plugin handle the given UI command. When the +// command is invoked, the browser will call CPP_HandleCommand. 'command' +// should be an integer identifier. Currently only builtin commands are +// supported, but in the future we may want to let plugins add custom menu +// commands with their own descriptions. +typedef CPError (STDCALL *CPB_AddUICommandFunc)(CPID id, int command); + +// +// Functions related to making network requests. +// Both the host and plugin will implement their own versions of these. +// + +// Starts the request. Returns CPERR_SUCCESS if the request could be started +// immediately, at which point the response info is available to be read. +// Returns CPERR_IO_PENDING if an asynchronous operation was started, and the +// caller should wait for CPRR_StartCompleted to be called before reading the +// response info. Returns an error code on failure. +typedef CPError (STDCALL *CPR_StartRequestFunc)(CPRequest* request); + +// Stops or cancels the request. The caller should not access the request +// object after this call. If an asynchronous IO operation is pending, the +// operation is aborted and the caller will not receive a callback for it. +typedef void (STDCALL *CPR_EndRequestFunc)(CPRequest* request, CPError reason); + +// Sets the additional request headers to append to the standard headers that +// would normally be made with this request. Headers should be \r\n-delimited, +// with no terminating \r\n. Extra headers are not checked against the standard +// headers for duplicates. Must be called before CPRR_StartCompletedFunc. +// Plugins should avoid setting the following headers: User-Agent, +// Content-Length. +typedef void (STDCALL *CPR_SetExtraRequestHeadersFunc)(CPRequest* request, + const char* headers); + +// Sets the load flags for this request. 'flags' is a bitwise-OR of +// CPRequestLoadFlags. Must be called before CPRR_StartCompletedFunc. +typedef void (STDCALL *CPR_SetRequestLoadFlagsFunc)(CPRequest* request, + uint32 flags); + +// Appends binary data to the request body of a POST or PUT request. The caller +// should set the "Content-Type" header to the appropriate mime type using +// CPR_SetExtraRequestHeadersFunc. This can be called multiple times to append +// a sequence of data segments to upload. Must be called before +// CPR_StartRequestFunc. +typedef void (STDCALL *CPR_AppendDataToUploadFunc)( + CPRequest* request, const char* bytes, int bytes_len); + +// Appends the contents of a file to the request body of a POST or PUT request. +// 'offset' and 'length' can be used to append a subset of the file. Pass zero +// for 'length' and 'offset' to upload the entire file. 'offset' +// indicates where the data to upload begins in the file. 'length' indicates +// how much of the file to upload. A 'length' of zero is interpretted as to +// end-of-file. If 'length' and 'offset' indicate a range beyond end of file, +// the amount sent is clipped at eof. +// See CPR_AppendDataToUploadFunc for additional usage information. +// (added in v0.4) +typedef CPError (STDCALL *CPR_AppendFileToUploadFunc)( + CPRequest* request, const char* filepath, uint64 offset, uint64 length); + +// Queries for some response meta data. See CPResponseInfoType for possible +// queries. If buf_size is too small to contain the entire data, the return +// value will indicate the size required. Otherwise, the return value is a +// CPError or CPERR_SUCCESS. +typedef int (STDCALL *CPR_GetResponseInfoFunc)( + CPRequest* request, CPResponseInfoType type, + void* buf, uint32 buf_size); + +// Attempts to read a request's response data. The number of bytes read is +// returned; 0 indicates an EOF. CPERR_IO_PENDING is returned if an +// asynchronous operation was started, and CPRR_ReadCompletedFunc will be called +// when it completes; 'buf' must be available until the operation completes. +// Returns an error code on failure. +typedef int (STDCALL *CPR_ReadFunc)( + CPRequest* request, void* buf, uint32 buf_size); + +// +// Functions related to serving network requests. +// Both the host and plugin will implement their own versions of these. +// + +// Called upon a server-initiated redirect. The request will still hold the +// original URL, and 'new_url' will be the redirect destination. +typedef void (STDCALL *CPRR_ReceivedRedirectFunc)(CPRequest* request, + const char* new_url); + +// Called when an asynchronous CPR_StartRequest call has completed, once all +// redirects are followed. On success, 'result' holds CPERR_SUCCESS and the +// response info is available to be read via CPR_GetResponseInfo. On error, +// 'result' holds the error code. +typedef void (STDCALL *CPRR_StartCompletedFunc)(CPRequest* request, + CPError result); + +// Called when an asynchronous CPR_Read call has completed. On success, +// 'bytes_read' will hold the number of bytes read into the buffer that was +// passed to CPR_Read; 0 indicates an EOF, and the request object will be +// destroyed after the call completes. On failure, 'bytes_read' holds the error +// code. +typedef void (STDCALL *CPRR_ReadCompletedFunc)(CPRequest* request, + int bytes_read); + +// Called as upload progress is being made for async POST requests. +// (added in v0.5) +typedef void (STDCALL *CPRR_UploadProgressFunc)(CPRequest* request, + uint64 position, + uint64 size); + +// +// Functions to support the sending and receipt of messages between processes. +// + +// Returns true if the plugin process is running +typedef CPBool (STDCALL *CPB_IsPluginProcessRunningFunc)(CPID id); + +// Returns the type of the current process. +typedef CPProcessType (STDCALL *CPB_GetProcessTypeFunc)(CPID id); + +// Asks the browser to send raw data to the other process hosting an instance of +// this plugin. If needed, the plugin process will be started prior to sending +// the message. +typedef CPError (STDCALL *CPB_SendMessageFunc)(CPID id, + const void *data, + uint32 data_len); + +// Informs the plugin of raw data having been sent from another process. +typedef void (STDCALL *CPP_OnMessageFunc)(void *data, uint32 data_len); + +// Function table for issuing requests using via the other side's network stack. +// For the plugin, this functions deal with issuing requests through the +// browser. For the browser, these functions deal with allowing the plugin to +// intercept requests. +typedef struct _CPRequestFuncs { + uint16 size; + CPR_SetExtraRequestHeadersFunc set_extra_request_headers; + CPR_SetRequestLoadFlagsFunc set_request_load_flags; + CPR_AppendDataToUploadFunc append_data_to_upload; + CPR_StartRequestFunc start_request; + CPR_EndRequestFunc end_request; + CPR_GetResponseInfoFunc get_response_info; + CPR_ReadFunc read; + CPR_AppendFileToUploadFunc append_file_to_upload; +} CPRequestFuncs; + +// Function table for handling requests issued by the other side. For the +// plugin, these deal with serving requests that the plugin has intercepted. For +// the browser, these deal with serving requests that the plugin has issued +// through us. +typedef struct _CPResponseFuncs { + uint16 size; + CPRR_ReceivedRedirectFunc received_redirect; + CPRR_StartCompletedFunc start_completed; + CPRR_ReadCompletedFunc read_completed; + CPRR_UploadProgressFunc upload_progress; +} CPResponseFuncs; + +// Function table of CPP functions (functions provided by plugin to host). This +// structure is filled in by the plugin in the CP_Initialize call, except for +// the 'size' field, which is set by the browser. The version fields should be +// set to those that the plugin was compiled using. +typedef struct _CPPluginFuncs { + uint16 size; + uint16 version; + CPRequestFuncs* request_funcs; + CPResponseFuncs* response_funcs; + CPP_ShutdownFunc shutdown; + CPP_ShouldInterceptRequestFunc should_intercept_request; + CPP_OnMessageFunc on_message; + CPP_HtmlDialogClosedFunc html_dialog_closed; + CPP_HandleCommandFunc handle_command; +} CPPluginFuncs; + +// Function table CPB functions (functions provided by host to plugin). +// This structure is filled in by the browser and provided to the plugin. The +// plugin will likely want to save a copy of this structure to make calls +// back to the browser. +typedef struct _CPBrowserFuncs { + uint16 size; + uint16 version; + CPRequestFuncs* request_funcs; + CPResponseFuncs* response_funcs; + CPB_EnableRequestInterceptFunc enable_request_intercept; + CPB_CreateRequestFunc create_request; + CPB_GetCookiesFunc get_cookies; + CPB_AllocFunc alloc; + CPB_FreeFunc free; + CPB_SetKeepProcessAliveFunc set_keep_process_alive; + CPB_ShowHtmlDialogModalFunc show_html_dialog_modal; + CPB_ShowHtmlDialogFunc show_html_dialog; + CPB_IsPluginProcessRunningFunc is_plugin_process_running; + CPB_GetProcessTypeFunc get_process_type; + CPB_SendMessageFunc send_message; + CPB_GetBrowsingContextFromNPPFunc get_browsing_context_from_npp; + CPB_GetBrowsingContextInfoFunc get_browsing_context_info; + CPB_GetCommandLineArgumentsFunc get_command_line_arguments; + CPB_AddUICommandFunc add_ui_command; + CPB_HandleCommandFunc handle_command; +} CPBrowserFuncs; + + +// +// DLL exports +// + +// This export is optional. +// Prior to calling CP_Initialize, the browser may negotiate with the plugin +// regarding which version of the CPAPI to utilize. 'min_version' is the +// lowest version of the interface supported by the browser, 'max_version' is +// the highest supported version. The plugin can specify which version within +// the range should be used. This version will be reflected in the version field +// of the CPBrowserFuncs struct passed to CP_Initialize. If this function +// returns an error code, CP_Initialize will not be called. If function is not +// exported by the chrome plugin module, CP_Initiailize will be called with +// a version of the host's choosing. +typedef CPError (STDCALL *CP_VersionNegotiateFunc)( + uint16 min_version, uint16 max_version, uint16 *selected_version); + +// 'bfuncs' are the browser functions provided to the plugin. 'id' is the +// plugin identifier that the plugin should use when calling browser functions. +// The plugin should initialize 'pfuncs' with pointers to its own functions, +// or return an error code. +// All functions and entry points should be called on the same thread. The +// plugin should not attempt to call a browser function from a thread other +// than the one CP_InitializeFunc is called from. +typedef CPError (STDCALL *CP_InitializeFunc)( + CPID id, const CPBrowserFuncs* bfuncs, CPPluginFuncs* pfuncs); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CHROME_COMMON_CHROME_PLUGIN_API_H__ diff --git a/chrome/common/chrome_plugin_lib.cc b/chrome/common/chrome_plugin_lib.cc new file mode 100644 index 0000000..f1d85eb --- /dev/null +++ b/chrome/common/chrome_plugin_lib.cc @@ -0,0 +1,278 @@ +// 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 "chrome/common/chrome_plugin_lib.h" + +#include "base/command_line.h" +#include "base/histogram.h" +#include "base/path_service.h" +#include "base/perftimer.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "chrome/common/chrome_counters.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/chrome_paths.h" +#include "webkit/glue/plugins/plugin_list.h" + +const TCHAR ChromePluginLib::kRegistryChromePlugins[] = + _T("Software\\Google\\Chrome\\Plugins"); +static const TCHAR kRegistryLoadOnStartup[] = _T("LoadOnStartup"); +static const TCHAR kRegistryPath[] = _T("Path"); + +typedef stdext::hash_map<std::wstring, scoped_refptr<ChromePluginLib> > + PluginMap; + +// A map of all the instantiated plugins. +static PluginMap* g_loaded_libs; + +// The thread plugins are loaded and used in, lazily initialized upon +// the first creation call. +static DWORD g_plugin_thread_id = 0; + +static bool IsSingleProcessMode() { + // We don't support ChromePlugins in single-process mode. + CommandLine command_line; + return command_line.HasSwitch(switches::kSingleProcess); +} + +// static +ChromePluginLib* ChromePluginLib::Create(const std::wstring& filename, + const CPBrowserFuncs* bfuncs) { + // Keep a map of loaded plugins to ensure we only load each library once. + if (!g_loaded_libs) { + g_loaded_libs = new PluginMap(); + g_plugin_thread_id = ::GetCurrentThreadId(); + } + DCHECK(IsPluginThread()); + + // Lower case to match how PluginList::LoadPlugin stores the path. + std::wstring filename_lc = StringToLowerASCII(filename); + + PluginMap::const_iterator iter = g_loaded_libs->find(filename_lc); + if (iter != g_loaded_libs->end()) + return iter->second; + + scoped_refptr<ChromePluginLib> plugin(new ChromePluginLib(filename_lc)); + if (!plugin->CP_Initialize(bfuncs)) + return NULL; + + (*g_loaded_libs)[filename_lc] = plugin; + return plugin; +} + +// static +ChromePluginLib* ChromePluginLib::Find(const std::wstring& filename) { + if (g_loaded_libs) { + PluginMap::const_iterator iter = g_loaded_libs->find(filename); + if (iter != g_loaded_libs->end()) + return iter->second; + } + return NULL; +} + +// static +void ChromePluginLib::Destroy(const std::wstring& filename) { + DCHECK(g_loaded_libs); + PluginMap::iterator iter = g_loaded_libs->find(filename); + if (iter != g_loaded_libs->end()) { + iter->second->Unload(); + g_loaded_libs->erase(iter); + } +} + +// static +bool ChromePluginLib::IsPluginThread() { + return ::GetCurrentThreadId() == g_plugin_thread_id; +} + +// static +void ChromePluginLib::RegisterPluginsWithNPAPI() { + // We don't support ChromePlugins in single-process mode. + if (IsSingleProcessMode()) + return; + + std::wstring path; + if (!PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) + return; + // Note: we can only access the NPAPI list because the PluginService has done + // the locking for us. We should not touch it anywhere else. + NPAPI::PluginList::AddExtraPluginPath(path); +} + +static void LogPluginLoadTime(const TimeDelta &time) { + UMA_HISTOGRAM_TIMES(L"Gears.LoadTime", time); +} + +// static +void ChromePluginLib::LoadChromePlugins(const CPBrowserFuncs* bfuncs) { + static bool loaded = false; + if (loaded) + return; + loaded = true; + + // We don't support ChromePlugins in single-process mode. + if (IsSingleProcessMode()) + return; + + std::wstring path; + if (!PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) + return; + + PerfTimer timer; + ChromePluginLib::Create(path, bfuncs); + LogPluginLoadTime(timer.Elapsed()); + + // TODO(mpcomplete): disabled loading of plugins from the registry until we + // phase out registry keys from the gears installer. +#if 0 + for (RegistryKeyIterator iter(HKEY_CURRENT_USER, kRegistryChromePlugins); + iter.Valid(); ++iter) { + // Use the registry to gather plugin across the file system. + std::wstring reg_path = kRegistryChromePlugins; + reg_path.append(L"\\"); + reg_path.append(iter.Name()); + RegKey key(HKEY_CURRENT_USER, reg_path.c_str()); + + DWORD is_persistent; + if (key.ReadValueDW(kRegistryLoadOnStartup, &is_persistent) && + is_persistent) { + std::wstring path; + if (key.ReadValue(kRegistryPath, &path)) { + ChromePluginLib::Create(path, bfuncs); + } + } + } +#endif +} + +// static +void ChromePluginLib::UnloadAllPlugins() { + if (g_loaded_libs) { + PluginMap::iterator it; + for (PluginMap::iterator it = g_loaded_libs->begin(); + it != g_loaded_libs->end(); ++it) { + it->second->Unload(); + } + delete g_loaded_libs; + g_loaded_libs = NULL; + } +} + +ChromePluginLib::ChromePluginLib(const std::wstring& filename) + : filename_(filename), + module_(0), + initialized_(false), + CP_VersionNegotiate_(NULL), + CP_Initialize_(NULL) { + memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); +} + +ChromePluginLib::~ChromePluginLib() { +} + +bool ChromePluginLib::CP_Initialize(const CPBrowserFuncs* bfuncs) { + if (initialized_) + return true; + + if (!Load()) + return false; + + if (CP_VersionNegotiate_) { + uint16 selected_version = 0; + CPError rv = CP_VersionNegotiate_(CP_VERSION, CP_VERSION, + &selected_version); + if ( (rv != CPERR_SUCCESS) || (selected_version != CP_VERSION)) + return false; + } + + plugin_funcs_.size = sizeof(plugin_funcs_); + CPError rv = CP_Initialize_(cpid(), bfuncs, &plugin_funcs_); + initialized_ = (rv == CPERR_SUCCESS) && + (CP_GET_MAJOR_VERSION(plugin_funcs_.version) == CP_MAJOR_VERSION) && + (CP_GET_MINOR_VERSION(plugin_funcs_.version) <= CP_MINOR_VERSION); + + return initialized_; +} + +void ChromePluginLib::CP_Shutdown() { + DCHECK(initialized_); + functions().shutdown(); + initialized_ = false; + memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); +} + +int ChromePluginLib::CP_Test(void* param) { + DCHECK(initialized_); + if (!CP_Test_) + return -1; + return CP_Test_(param); +} + +bool ChromePluginLib::Load() { + DCHECK(module_ == 0); + module_ = LoadLibrary(filename_.c_str()); + if (module_ == 0) + return false; + + // required initialization function + CP_Initialize_ = reinterpret_cast<CP_InitializeFunc> + (GetProcAddress(module_, "CP_Initialize")); + + if (!CP_Initialize_) { + FreeLibrary(module_); + module_ = 0; + return false; + } + + // optional version negotiation function + CP_VersionNegotiate_ = reinterpret_cast<CP_VersionNegotiateFunc> + (GetProcAddress(module_, "CP_VersionNegotiate")); + + // optional test function + CP_Test_ = reinterpret_cast<CP_TestFunc> + (GetProcAddress(module_, "CP_Test")); + + return true; +} + +void ChromePluginLib::Unload() { + NotificationService::current()->Notify( + NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(this), + NotificationService::NoDetails()); + + if (initialized_) + CP_Shutdown(); + + if (module_) { + FreeLibrary(module_); + module_ = 0; + } +} diff --git a/chrome/common/chrome_plugin_lib.h b/chrome/common/chrome_plugin_lib.h new file mode 100644 index 0000000..287eb1e --- /dev/null +++ b/chrome/common/chrome_plugin_lib.h @@ -0,0 +1,127 @@ +// 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 CHROME_COMMON_CHROME_PLUGIN_LIB_H__ +#define CHROME_COMMON_CHROME_PLUGIN_LIB_H__ + +#include <hash_map> +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/common/chrome_plugin_api.h" + +// A ChromePluginLib is a single Chrome Plugin Library. +// This class is used in the browser process (IO thread), and the plugin process +// (plugin thread). It should not be accessed on other threads, because it +// issues a NOTIFY_CHROME_PLUGIN_UNLOADED notification. +class ChromePluginLib : public base::RefCounted<ChromePluginLib> { + public: + static ChromePluginLib* Create(const std::wstring& filename, + const CPBrowserFuncs* bfuncs); + static ChromePluginLib* Find(const std::wstring& filename); + static void Destroy(const std::wstring& filename); + static bool IsPluginThread(); + + static ChromePluginLib* FromCPID(CPID id) { + return reinterpret_cast<ChromePluginLib*>(id); + } + + // Adds Chrome plugins to the NPAPI plugin list. + static void RegisterPluginsWithNPAPI(); + + // Loads all the plugin dlls that are marked as "LoadOnStartup" in the + // registry. This should only be called in the browser process. + static void LoadChromePlugins(const CPBrowserFuncs* bfuncs); + + // Unloads all the loaded plugin dlls and cleans up the plugin map. + static void UnloadAllPlugins(); + + // Returns true if the plugin is currently loaded. + const bool is_loaded() const { return initialized_; } + + // Get the Plugin's function pointer table. + const CPPluginFuncs& functions() const { + DCHECK(initialized_); + DCHECK(IsPluginThread()); + return plugin_funcs_; + } + + CPID cpid() { return reinterpret_cast<CPID>(this); } + + const std::wstring& filename() { return filename_; } + + // Plugin API functions + + // Method to call a test function in the plugin, used for unit tests. + int CP_Test(void* param); + + // The registroy path to search for Chrome Plugins/ + static const TCHAR kRegistryChromePlugins[]; + + private: + friend class base::RefCounted<ChromePluginLib>; + + ChromePluginLib(const std::wstring& filename); + ~ChromePluginLib(); + + // Method to initialize a Plugin. + // Initialize can be safely called multiple times. + bool CP_Initialize(const CPBrowserFuncs* bfuncs); + + // Method to shutdown a Plugin. + void CP_Shutdown(); + + // 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(); + + std::wstring filename_; // the path to the DLL + HMODULE module_; // the opened DLL handle + bool initialized_; // is the plugin initialized + + // DLL exports, looked up by name. + CP_VersionNegotiateFunc CP_VersionNegotiate_; + CP_InitializeFunc CP_Initialize_; + + // Additional function pointers provided by the plugin. + CPPluginFuncs plugin_funcs_; + + // Used for unit tests. + typedef int (STDCALL *CP_TestFunc)(void*); + CP_TestFunc CP_Test_; + + DISALLOW_EVIL_CONSTRUCTORS(ChromePluginLib); +}; + +#endif // CHROME_COMMON_CHROME_PLUGIN_LIB_H__ diff --git a/chrome/common/chrome_plugin_unittest.cc b/chrome/common/chrome_plugin_unittest.cc new file mode 100644 index 0000000..2c1bfdf --- /dev/null +++ b/chrome/common/chrome_plugin_unittest.cc @@ -0,0 +1,297 @@ +// 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 exercising the Chrome Plugin API. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/browser/chrome_plugin_host.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/url_fetcher.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/test/chrome_plugin/test_chrome_plugin.h" +#include "net/url_request/url_request_test_job.h" +#include "net/url_request/url_request_unittest.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +const wchar_t kDocRoot[] = L"chrome/test/data"; +const wchar_t kPluginFilename[] = L"test_chrome_plugin.dll"; + +class ChromePluginTest : public testing::Test, public URLRequest::Delegate { + public: + ChromePluginTest() + : request_(NULL), + plugin_(NULL), + expected_payload_(NULL), + request_context_(new TestURLRequestContext()) { + } + + // Loads/unloads the chrome test plugin. + void LoadPlugin(); + void UnloadPlugin(); + + // Runs the test and expects the given payload as a response. If expectation + // is NULL, the request is expected to fail. + void RunTest(const GURL& url, const TestResponsePayload* expected_payload); + + // URLRequest::Delegate implementations + virtual void OnReceivedRedirect(URLRequest* request, + const GURL& new_url) { } + virtual void OnResponseStarted(URLRequest* request); + virtual void OnReadCompleted(URLRequest* request, int bytes_read); + + // Helper called when the URLRequest is done. + void OnURLRequestComplete(); + + // testing::Test + virtual void SetUp() { + LoadPlugin(); + URLRequest::RegisterProtocolFactory("test", &URLRequestTestJob::Factory); + + // We need to setup a default request context in order to issue HTTP + // requests. + DCHECK(!Profile::GetDefaultRequestContext()); + Profile::set_default_request_context(request_context_.get()); + } + virtual void TearDown() { + UnloadPlugin(); + URLRequest::RegisterProtocolFactory("test", NULL); + + Profile::set_default_request_context(NULL); + } + protected: + // Note: we use URLRequest (instead of URLFetcher) because this allows the + // request to be intercepted. + scoped_ptr<URLRequest> request_; + char response_buffer_[4096]; + std::string response_data_; + + ChromePluginLib* plugin_; + TestFuncParams::PluginFuncs test_funcs_; + const TestResponsePayload* expected_payload_; + scoped_refptr<URLRequestContext> request_context_; +}; + +static void STDCALL CPT_Complete(CPRequest* request, bool success, + const std::string& raw_headers, + const std::string& body) { + GURL url(request->url); + if (url == GURL(kChromeTestPluginPayloads[0].url)) { + // This URL should fail, because the plugin should not have intercepted it. + EXPECT_FALSE(success); + MessageLoop::current()->Quit(); + return; + } + + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_headers)); + EXPECT_TRUE(success); + EXPECT_EQ(200, headers->response_code()); + + if (url == URLRequestTestJob::test_url_1()) { + EXPECT_EQ(URLRequestTestJob::test_data_1(), body); + } else if (url == URLRequestTestJob::test_url_2()) { + EXPECT_EQ(URLRequestTestJob::test_data_2(), body); + } else if (url.spec().find("echo") != std::string::npos) { + EXPECT_EQ(kChromeTestPluginPostData, body); + } + + MessageLoop::current()->Quit(); +} + +static void STDCALL CPT_InvokeLater(TestFuncParams::CallbackFunc callback, + void* callback_data, int delay_ms) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(callback, callback_data), delay_ms); +} + +void ChromePluginTest::LoadPlugin() { + std::wstring path; + PathService::Get(base::DIR_EXE, &path); + file_util::AppendToPath(&path, kPluginFilename); + plugin_ = ChromePluginLib::Create(path, GetCPBrowserFuncsForBrowser()); + + // Exchange test APIs with the plugin. + TestFuncParams params; + params.bfuncs.test_complete = CPT_Complete; + params.bfuncs.invoke_later = CPT_InvokeLater; + EXPECT_EQ(CPERR_SUCCESS, plugin_->CP_Test(¶ms)); + test_funcs_ = params.pfuncs; + + EXPECT_TRUE(plugin_); +} + +void ChromePluginTest::UnloadPlugin() { + ChromePluginLib::UnloadAllPlugins(); + plugin_ = NULL; +} + +void ChromePluginTest::RunTest(const GURL& url, + const TestResponsePayload* expected_payload) { + expected_payload_ = expected_payload; + + response_data_.clear(); + request_.reset(new URLRequest(url, this)); + request_->set_context(new TestURLRequestContext()); + request_->Start(); + + MessageLoop::current()->Run(); +} + +void ChromePluginTest::OnResponseStarted(URLRequest* request) { + DCHECK(request == request_); + + int bytes_read = 0; + if (request_->status().is_success()) + request_->Read(response_buffer_, sizeof(response_buffer_), &bytes_read); + OnReadCompleted(request_.get(), bytes_read); +} + +void ChromePluginTest::OnReadCompleted(URLRequest* request, int bytes_read) { + DCHECK(request == request_); + + do { + if (!request_->status().is_success() || bytes_read <= 0) + break; + response_data_.append(response_buffer_, bytes_read); + } while (request_->Read(response_buffer_, sizeof(response_buffer_), + &bytes_read)); + + if (!request_->status().is_io_pending()) { + OnURLRequestComplete(); + } +} + +void ChromePluginTest::OnURLRequestComplete() { + if (expected_payload_) { + EXPECT_TRUE(request_->status().is_success()); + + EXPECT_EQ(expected_payload_->status, request_->GetResponseCode()); + if (expected_payload_->mime_type) { + std::string mime_type; + EXPECT_TRUE(request_->response_headers()->GetMimeType(&mime_type)); + EXPECT_EQ(expected_payload_->mime_type, mime_type); + } + if (expected_payload_->body) { + EXPECT_EQ(expected_payload_->body, response_data_); + } + } else { + EXPECT_FALSE(request_->status().is_success()); + } + + MessageLoop::current()->Quit(); + // If MessageLoop::current() != main_loop_, it will be shut down when the + // main loop returns and this thread subsequently goes out of scope. +} + +}; // namespace + +// Tests that the plugin can intercept URLs. +TEST_F(ChromePluginTest, DoesIntercept) { + for (int i = 0; i < arraysize(kChromeTestPluginPayloads); ++i) { + RunTest(GURL(kChromeTestPluginPayloads[i].url), + &kChromeTestPluginPayloads[i]); + } +} + +// Tests that non-intercepted URLs are handled normally. +TEST_F(ChromePluginTest, DoesNotIntercept) { + TestResponsePayload about_blank = { + "about:blank", + false, + -1, + NULL, + "" + }; + RunTest(GURL(about_blank.url), &about_blank); +} + +// Tests that unloading the plugin correctly unregisters URL interception. +TEST_F(ChromePluginTest, UnregisterIntercept) { + UnloadPlugin(); + + RunTest(GURL(kChromeTestPluginPayloads[0].url), NULL); +} + +static void ProcessAllPendingMessages() { + while (URLRequestTestJob::ProcessOnePendingMessage()); +} + +// Tests that the plugin can issue a GET request and receives the data when +// it comes back synchronously. +TEST_F(ChromePluginTest, CanMakeGETRequestSync) { + // test_url_1 has a synchronous response + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request( + "GET", URLRequestTestJob::test_url_1())); + + // Note: we must add this task after we make the request, so that + // URLRequestTestJob's StartAsync task is added and run first. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(&ProcessAllPendingMessages)); + MessageLoop::current()->Run(); +} + +// Tests that the plugin can issue a GET request and receives the data when +// it comes back asynchronously. +TEST_F(ChromePluginTest, CanMakeGETRequestAsync) { + // test_url_2 has an asynchronous response + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request( + "GET", URLRequestTestJob::test_url_2())); + + // Note: we must add this task after we make the request, so that + // URLRequestTestJob's StartAsync task is added and run first. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(&ProcessAllPendingMessages)); + MessageLoop::current()->Run(); +} + +// Tests that the plugin can issue a POST request. +TEST_F(ChromePluginTest, CanMakePOSTRequest) { + TestServer server(kDocRoot); + GURL url = server.TestServerPage("echo"); + + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request("POST", url)); + + // Note: we must add this task after we make the request, so that + // URLRequestTestJob's StartAsync task is added and run first. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(&ProcessAllPendingMessages)); + MessageLoop::current()->Run(); +} + +// Tests that the plugin does not intercept its own requests. +TEST_F(ChromePluginTest, DoesNotInterceptOwnRequest) { + const TestResponsePayload& payload = kChromeTestPluginPayloads[0]; + + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request( + "GET", GURL(payload.url))); + + MessageLoop::current()->Run(); +} diff --git a/chrome/common/chrome_plugin_util.cc b/chrome/common/chrome_plugin_util.cc new file mode 100644 index 0000000..39497af --- /dev/null +++ b/chrome/common/chrome_plugin_util.cc @@ -0,0 +1,191 @@ +// 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 "chrome/common/chrome_plugin_util.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/common/chrome_switches.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_headers.h" + +// +// ScopableCPRequest +// + +ScopableCPRequest::ScopableCPRequest(const char* u, const char* m, + CPBrowsingContext c) { + pdata = NULL; + data = NULL; + url = _strdup(u); + method = _strdup(m); + context = c; +} + +ScopableCPRequest::~ScopableCPRequest() { + pdata = NULL; + data = NULL; + free(const_cast<char*>(url)); + free(const_cast<char*>(method)); +} + +// +// PluginHelper +// + +// static +void PluginHelper::DestroyAllHelpersForPlugin(ChromePluginLib* plugin) { + NotificationService::current()->Notify( + NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(plugin), + NotificationService::NoDetails()); +} + +PluginHelper::PluginHelper(ChromePluginLib* plugin) : plugin_(plugin) { +#ifndef NDEBUG + message_loop_ = MessageLoop::current(); +#endif + NotificationService::current()->AddObserver( + this, NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(plugin_)); +} + +PluginHelper::~PluginHelper() { +#ifndef NDEBUG + DCHECK(MessageLoop::current() == message_loop_); +#endif + NotificationService::current()->RemoveObserver( + this, NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(plugin_)); +} + +void PluginHelper::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { +#ifndef NDEBUG + DCHECK(MessageLoop::current() == message_loop_); +#endif + DCHECK(type == NOTIFY_CHROME_PLUGIN_UNLOADED); + DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr()); + + delete this; +} + +// +// PluginResponseUtils +// + +uint32 PluginResponseUtils::CPLoadFlagsToNetFlags(uint32 flags) { + uint32 net_flags = 0; +#define HANDLE_FLAG(name) \ + if (flags & CPREQUEST##name) \ + net_flags |= net::name + + HANDLE_FLAG(LOAD_VALIDATE_CACHE); + HANDLE_FLAG(LOAD_BYPASS_CACHE); + HANDLE_FLAG(LOAD_PREFERRING_CACHE); + HANDLE_FLAG(LOAD_ONLY_FROM_CACHE); + HANDLE_FLAG(LOAD_DISABLE_CACHE); + HANDLE_FLAG(LOAD_DISABLE_INTERCEPT); + + net_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS; + + return net_flags; +} + +int PluginResponseUtils::GetResponseInfo( + const net::HttpResponseHeaders* response_headers, + CPResponseInfoType type, void* buf, uint32 buf_size) { + if (!response_headers) + return CPERR_FAILURE; + + switch (type) { + case CPRESPONSEINFO_HTTP_STATUS: + if (buf && buf_size) { + int status = response_headers->response_code(); + memcpy(buf, &status, std::min(buf_size, sizeof(int))); + } + break; + case CPRESPONSEINFO_HTTP_RAW_HEADERS: { + const std::string& headers = response_headers->raw_headers(); + if (buf_size < headers.size()+1) + return static_cast<int>(headers.size()+1); + if (buf) + memcpy(buf, headers.c_str(), headers.size()+1); + break; + } + default: + return CPERR_INVALID_VERSION; + } + + return CPERR_SUCCESS; +} + +CPError CPB_GetCommandLineArgumentsCommon(const char* url, + std::string* arguments) { + CommandLine cmd; + std::wstring arguments_w; + + // Use the same UserDataDir for new launches that we currently have set. + std::wstring user_data_dir = cmd.GetSwitchValue(switches::kUserDataDir); + if (!user_data_dir.empty()) { + // Make sure user_data_dir is an absolute path. + wchar_t user_data_dir_full[MAX_PATH]; + if (_wfullpath(user_data_dir_full, user_data_dir.c_str(), MAX_PATH) && + file_util::PathExists(user_data_dir_full)) { + CommandLine::AppendSwitchWithValue( + &arguments_w, switches::kUserDataDir, user_data_dir_full); + } + } + + // Use '--app=url' instead of just 'url' to launch the browser with minimal + // chrome. + // Note: Do not change this flag! Old Gears shortcuts will break if you do! + std::wstring url_w = UTF8ToWide(url); + CommandLine::AppendSwitchWithValue(&arguments_w, switches::kApp, url_w); + + *arguments = WideToUTF8(arguments_w); + + return CPERR_SUCCESS; +} + +// +// Host functions shared by browser and plugin processes +// + +void* STDCALL CPB_Alloc(uint32 size) { + return malloc(size); +} + +void STDCALL CPB_Free(void* memory) { + free(memory); +} diff --git a/chrome/common/chrome_plugin_util.h b/chrome/common/chrome_plugin_util.h new file mode 100644 index 0000000..652259b --- /dev/null +++ b/chrome/common/chrome_plugin_util.h @@ -0,0 +1,112 @@ +// 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 CHROME_COMMON_CHROME_PLUGIN_UTIL_H__ +#define CHROME_COMMON_CHROME_PLUGIN_UTIL_H__ + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "chrome/common/chrome_plugin_api.h" +#include "chrome/common/notification_service.h" + +class ChromePluginLib; +class MessageLoop; +namespace net{ +class HttpResponseHeaders; +} + +// A helper struct to ensure the CPRequest data is cleaned up when done. +// This class is reused for requests made by the browser (and intercepted by the +// plugin) as well as those made by the plugin. +struct ScopableCPRequest : public CPRequest { + template<class T> + static T GetData(CPRequest* request) { + return static_cast<T>(static_cast<ScopableCPRequest*>(request)->data); + } + + ScopableCPRequest(const char* url, const char* method, + CPBrowsingContext context); + ~ScopableCPRequest(); + + void* data; +}; + +// This is a base class for plugin-related objects that need to go away when +// the plugin unloads. This object also verifies that it is created and +// destroyed on the same thread. +class PluginHelper : public NotificationObserver { + public: + static void DestroyAllHelpersForPlugin(ChromePluginLib* plugin); + + PluginHelper(ChromePluginLib* plugin); + virtual ~PluginHelper(); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + protected: + scoped_refptr<ChromePluginLib> plugin_; +#ifndef NDEBUG + // We keep track of the message loop of the thread we were created on, so + // we can verify that all other methods are called on the same thread. + MessageLoop* message_loop_; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(PluginHelper); +}; + +// A class of utility functions for dealing with request responses. +class PluginResponseUtils { +public: + // Helper to convert request load flags from the plugin API to the net API + // versions. + static uint32 CPLoadFlagsToNetFlags(uint32 flags); + + // Common implementation of a CPR_GetResponseInfo call. + static int GetResponseInfo( + const net::HttpResponseHeaders* response_headers, + CPResponseInfoType type, void* buf, uint32 buf_size); +}; + +// Helper to allocate a string using the given CPB_Alloc function. +inline char* CPB_StringDup(CPB_AllocFunc alloc, const std::string& str) { + char* cstr = static_cast<char*>(alloc(static_cast<uint32>(str.length() + 1))); + memcpy(cstr, str.c_str(), str.length() + 1); // Include null terminator. + return cstr; +} + +CPError CPB_GetCommandLineArgumentsCommon(const char* url, + std::string* arguments); + +void* STDCALL CPB_Alloc(uint32 size); +void STDCALL CPB_Free(void* memory); + +#endif // CHROME_COMMON_CHROME_PLUGIN_UTIL_H__ diff --git a/chrome/common/chrome_process_filter.cc b/chrome/common/chrome_process_filter.cc new file mode 100644 index 0000000..28bee47 --- /dev/null +++ b/chrome/common/chrome_process_filter.cc @@ -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. + +#include <windows.h> + +#include "chrome/common/chrome_process_filter.h" + +#include "base/path_service.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" + +BrowserProcessFilter::BrowserProcessFilter() : browser_process_id_(0) { + // Find the message window (if any) for the current user data directory, + // and get its process ID. We'll only count browser processes that either + // have the same process ID or have that process ID as their parent. + + std::wstring user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + + HWND message_window = FindWindowEx(HWND_MESSAGE, NULL, + chrome::kMessageWindowClass, + user_data_dir.c_str()); + if (message_window) + GetWindowThreadProcessId(message_window, &browser_process_id_); +} + +bool BrowserProcessFilter::Includes(uint32 pid, + uint32 parent_pid) const { + return browser_process_id_ && (browser_process_id_ == pid || + browser_process_id_ == parent_pid); +} diff --git a/chrome/common/chrome_process_filter.h b/chrome/common/chrome_process_filter.h new file mode 100644 index 0000000..5f1e2cdc --- /dev/null +++ b/chrome/common/chrome_process_filter.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 CHROME_COMMON_CHROME_PROCESS_FILTER_H__ +#define CHROME_COMMON_CHROME_PROCESS_FILTER_H__ + +#include "base/process_util.h" + +// Filter all chrome browser processes that run with the current user data +// directory (chrome::DIR_USER_DATA). +class BrowserProcessFilter : public process_util::ProcessFilter { + public: + BrowserProcessFilter(); + + uint32 browser_process_id() const { return browser_process_id_; } + + virtual bool Includes(uint32 pid, uint32 parent_pid) const; + + private: + DWORD browser_process_id_; + + DISALLOW_EVIL_CONSTRUCTORS(BrowserProcessFilter); +}; + +#endif // CHROME_COMMON_CHROME_PROCESS_FILTER_H__ diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc new file mode 100644 index 0000000..2f47beb --- /dev/null +++ b/chrome/common/chrome_switches.cc @@ -0,0 +1,340 @@ +// 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 "chrome/common/chrome_switches.h" + +#include "base/base_switches.h" + +namespace switches { + +// Can't find the switch you are looking for? try looking in +// base/base_switches.cc instead. + +// Suppresses hang monitor dialogs in renderer processes. +const wchar_t kDisableHangMonitor[] = L"disable-hang-monitor"; + +// Completely disables UMA metrics system. +const wchar_t kDisableMetrics[] = L"disable-metrics"; + +// Disables only the sending of metrics reports. In contrast to +// kDisableMetrics, this executes all the code that a normal client would use +// for reporting, except the report is dropped rather than sent to the server. +// This is useful for finding issues in the metrics code during UI and +// performance tests. +const wchar_t kDisableMetricsReporting[] = L"disable-metrics-reporting"; + +// Causes the browser process to throw an assertion on startup. +const wchar_t kBrowserAssertTest[] = L"assert-test"; + +// Causes the renderer process to throw an assertion on launch. +const wchar_t kRendererAssertTest[] = L"renderer-assert-test"; + +// Causes the browser process to crash on startup. +const wchar_t kBrowserCrashTest[] = L"crash-test"; + +// Causes the renderer process to crash on launch. +const wchar_t kRendererCrashTest[] = L"renderer-crash-test"; + +// Causes the renderer process to display a dialog on launch. +const wchar_t kRendererStartupDialog[] = L"renderer-startup-dialog"; + +// Causes the plugin process to display a dialog on launch. +const wchar_t kPluginStartupDialog[] = L"plugin-startup-dialog"; + +// Causes the test shell process to display a dialog on launch. +const wchar_t kTestShellStartupDialog[] = L"testshell-startup-dialog"; + +// Specifies a command that should be used to launch the plugin process. Useful +// for running the plugin process through purify or quantify. Ex: +// --plugin-launcher="path\to\purify /Run=yes" +const wchar_t kPluginLauncher[] = L"plugin-launcher"; + +// The value of this switch tells the child process which +// IPC channel the browser expects to use to communicate with it. +const wchar_t kProcessChannelID[] = L"channel"; + +// The value of this switch tells the app to listen for and broadcast +// testing-related messages on IPC channel with the given ID. +const wchar_t kTestingChannelID[] = L"testing-channel"; + +// The value of this switch specifies which page will be displayed +// in newly-opened tabs. We need this for testing purposes so +// that the UI tests don't depend on what comes up for http://google.com. +const wchar_t kHomePage[] = L"homepage"; + +// When this switch is present, the browser will throw up a dialog box +// asking the user to start a renderer process independently rather +// than launching the renderer itself. (This is useful for debugging.) +const wchar_t kBrowserStartRenderersManually[] = L"start-renderers-manually"; + +// Causes the process to run as renderer instead of as browser. +const wchar_t kRendererProcess[] = L"renderer"; + +// Path to the exe to run for the renderer subprocess +const wchar_t kRendererPath[] = L"renderer-path"; + +// Causes the process to run as plugin host +const wchar_t kPluginProcess[] = L"plugin"; + +// Runs the renderer and plugins in the same process as the browser +const wchar_t kSingleProcess[] = L"single-process"; + +// Runs each set of script-connected tabs (i.e., a BrowsingInstance) in its own +// renderer process. We default to using a renderer process for each +// site instance (i.e., group of pages from the same registered domain with +// script connections to each other). +const wchar_t kProcessPerTab[] = L"process-per-tab"; + +// Runs a single process for each site (i.e., group of pages from the same +// registered domain) the user visits. We default to using a renderer process +// for each site instance (i.e., group of pages from the same registered +// domain with script connections to each other). +const wchar_t kProcessPerSite[] = L"process-per-site"; + +// Runs plugins inside the renderer process +const wchar_t kInProcessPlugins[] = L"in-process-plugins"; + +// Runs the renderer outside the sandbox. +const wchar_t kNoSandbox[] = L"no-sandbox"; + +// Runs the plugin processes inside the sandbox. +const wchar_t kSafePlugins[] = L"safe-plugins"; + +// Excludes these plugins from the plugin sandbox. +// This is a comma separated list of plugin dlls name and activex clsid. +const wchar_t kTrustedPlugins[] = L"trusted-plugins"; + +// Runs the security test for the sandbox. +const wchar_t kTestSandbox[] = L"test-sandbox"; + +// Specifies the user data directory, which is where the browser will look +// for all of its state. +const wchar_t kUserDataDir[] = L"user-data-dir"; + +// Specifies that the associated value should be launched in "application" mode. +const wchar_t kApp[] = L"app"; + +// Specifies the file that should be uploaded to the provided application. This +// switch is expected to be used with --app option. +const wchar_t kAppUploadFile[] = L"upload-file"; + +// Specifies if the dom_automation_controller_ needs to be bound in the +// renderer. This binding happens on per-frame basis and hence can potentially +// be a performance bottleneck. One should only enable it when automating +// dom based tests. +const wchar_t kDomAutomationController[] = L"dom-automation"; + +// Tells the plugin process the path of the plugin to load +const wchar_t kPluginPath[] = L"plugin-path"; + +// Specifies the flags passed to JS engine +const wchar_t kJavaScriptFlags[] = L"js-flags"; + +// The GeoID we should use. This is normally obtained from the operating system +// during first run and cached in the preferences afterwards. This is a numeric +// value; see http://msdn.microsoft.com/en-us/library/ms776390.aspx . +const wchar_t kGeoID[] = L"geoid"; + +// The language file that we want to try to open. Of the form +// language[-country] where language is the 2 letter code from ISO-639. +const wchar_t kLang[] = L"lang"; + +// Will add kDebugOnStart to every child processes. If a value is passed, it +// will be used as a filter to determine if the child process should have the +// kDebugOnStart flag passed on or not. +const wchar_t kDebugChildren[] = L"debug-children"; + +// Will add kWaitForDebugger to every child processes. If a value is passed, it +// will be used as a filter to determine if the child process should have the +// kWaitForDebugger flag passed on or not. +const wchar_t kWaitForDebuggerChildren[] = L"wait-for-debugger-children"; + +// Will filter log messages to show only the messages that are prefixed +// with the specified value +const wchar_t kLogFilterPrefix[] = L"log-filter-prefix"; + +// Force logging to be enabled. Logging is disabled by default in release +// builds. +const wchar_t kEnableLogging[] = L"enable-logging"; + +// Dump any accumualted histograms to the log when browser terminates (requires +// logging to be enabled to really do anything). Used by developers and test +// scripts. +const wchar_t kDumpHistogramsOnExit[] = L"dump-histograms-on-exit"; + +// Force logging to be disabled. Logging is enabled by default in debug +// builds. +const wchar_t kDisableLogging[] = L"disable-logging"; + +// enable remote debug / automation shell on the specified port +const wchar_t kRemoteShellPort[] = L"remote-shell-port"; + +// Runs un-installation steps that were done by chrome first-run. +const wchar_t kUninstall[] = L"uninstall"; + +// Number of entries to show in the omnibox popup. +const wchar_t kOmniBoxPopupCount[] = L"omnibox-popup-count"; + +// The value of this switch tells the app to listen for and broadcast +// automation-related messages on IPC channel with the given ID. +const wchar_t kAutomationClientChannelID[] = L"automation-channel"; + +// Indicates the last session should be restored on startup. This overrides +// the preferences value and is primarily intended for testing. +const wchar_t kRestoreLastSession[] = L"restore-last-session"; + +// Chrome supports a playback and record mode. Record mode saves *everything* +// to the cache. Playback mode reads data exclusively from the cache. This +// allows us to record a session into the cache and then replay it at will. +const wchar_t kRecordMode[] = L"record-mode"; +const wchar_t kPlaybackMode[] = L"playback-mode"; + +// Don't record/playback events when using record & playback. +const wchar_t kNoEvents[] = L"no-events"; + +// Make Windows happy by allowing it to show "Enable access to this program" +// checkbox in Add/Remove Programs->Set Program Access and Defaults. This +// only shows an error box because the only way to hide Chrome is by +// uninstalling it. +const wchar_t kHideIcons[] = L"hide-icons"; + +const wchar_t kShowIcons[] = L"show-icons"; + +// Make Chrome default browser +const wchar_t kMakeDefaultBrowser[] = L"make-default-browser"; + +// Use a specified proxy server, overrides system settings. This switch only +// affects HTTP and HTTPS requests. +const wchar_t kProxyServer[] = L"proxy-server"; + +// Chrome will support prefetching of DNS information. Until this becomes +// the default, we'll provide a command line switch. +extern const wchar_t kDnsLogDetails[] = L"dns-log-details"; +extern const wchar_t kDnsPrefetchDisable[] = L"dns-prefetch-disable"; + +// Enables support to debug printing subsystem. +const wchar_t kDebugPrint[] = L"debug-print"; + +// Allow initialization of all activex controls. This is only to help website +// developers test their controls to see if they are compatible in Chrome. +// Note there's a duplicate value in activex_shared.cc (to avoid +// dependency on chrome module). Please change both locations at the same time. +const wchar_t kAllowAllActiveX[] = L"allow-all-activex"; + +// Browser flag to disable the web inspector for all renderers. +const wchar_t kDisableDevTools[] = L"disable-dev-tools"; + +// Enable web inspector for all windows, even if they're part of the browser. +// Allows us to use our dev tools to debug browser windows itself. +const wchar_t kAlwaysEnableDevTools[] = L"always-enable-dev-tools"; + +// Used to set the value of SessionRestore::num_tabs_to_load_. See +// session_restore.h for details. +const wchar_t kTabCountToLoadOnSessionRestore[] = + L"tab-count-to-load-on-session-restore"; + +// Enable synamic loading of the Memory Profiler DLL, which will trace +// all memory allocations during the run. +const wchar_t kMemoryProfiling[] = L"memory-profile"; + +// Configure Chrome's memory model. +// Does chrome really need multiple memory models? No. But we get a lot +// of concerns from individuals about how the changes work on *their* +// system, and we need to be able to experiment with a few choices. +const wchar_t kMemoryModel[] = L"memory-model"; + +// By default, cookies are not allowed on file://. They are needed in for +// testing, for example page cycler and layout tests. See bug 1157243. +const wchar_t kEnableFileCookies[] = L"enable-file-cookies"; + +// Start the browser maximized, regardless of any previous settings. +// TODO(pjohnson): Remove this once bug 1137420 is fixed. We are using this +// as a workaround for not being able to use moveTo and resizeTo on a +// top-level window. +const wchar_t kStartMaximized[] = L"start-maximized"; + +// Spawn threads to watch for excessive delays in specified message loops. +// User should set breakpoints on Alarm() to examine problematic thread. +// Usage: -enable-watchdog=[ui][io] +// Order of the listed sub-arguments does not matter. +const wchar_t kEnableWatchdog[] = L"enable-watchdog"; + +// Display the First Run experience when the browser is started, regardless of +// whether or not it's actually the first run. +const wchar_t kFirstRun[] = L"first-run"; + +// Select an alternate message loop task dispatch strategy. +// Usage -message-loop-strategy=n +const wchar_t kMessageLoopStrategy[] = L"message-loop-strategy"; + +// Enable histograming of tasks served by MessageLoop. See about:histograms/Loop +// for results, which show frequency of messages on each thread, including APC +// count, object signalling count, etc. +const wchar_t kMessageLoopHistogrammer[] = L"message-loop-histogrammer"; + +// Perform importing from another browser. The value associated with this +// setting encodes the target browser and what items to import. +const wchar_t kImport[] = L"import"; + +// Change the DCHECKS to dump memory and continue instead of crashing. +// This is valid only in Release mode when --enable-dcheck is specified. +const wchar_t kSilentDumpOnDCHECK[] = L"silent-dump-on-dcheck"; + +// Normally when the user attempts to navigate to a page that was the result of +// a post we prompt to make sure they want to. This switch may be used to +// disable that check. This switch is used during automated testing. +const wchar_t kDisablePromptOnRepost[] = L"disable-prompt-on-repost"; + +// Disable pop-up blocking. +const wchar_t kDisablePopupBlocking[] = L"disable-popup-blocking"; + +// Don't execute JavaScript (browser JS like the new tab page still runs). +const wchar_t kDisableJavaScript[] = L"disable-javascript"; + +// Prevent Java from running. +const wchar_t kDisableJava[] = L"disable-java"; + +// Prevent plugins from running. +const wchar_t kDisablePlugins[] = L"disable-plugins"; + +// Prevent images from loading. +const wchar_t kDisableImages[] = L"disable-images"; + +// Use the low fragmentation heap for the CRT. +const wchar_t kUseLowFragHeapCrt[] = L"use-lf-heap"; + +#ifndef NDEBUG +// Debug only switch to specify which gears plugin dll to load. +const wchar_t kGearsPluginPathOverride[] = L"gears-plugin-path"; +#endif + +// Enable new HTTP stack. +const wchar_t kUseNewHttp[] = L"new-http"; + +} // namespace switches diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h new file mode 100644 index 0000000..f6ac2d4 --- /dev/null +++ b/chrome/common/chrome_switches.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. + +// Defines all the command-line switches used by Chrome. + +#ifndef CHROME_COMMON_CHROME_SWITCHES_H__ +#define CHROME_COMMON_CHROME_SWITCHES_H__ + +#include "base/base_switches.h" + +namespace switches { + +extern const wchar_t kDisableHangMonitor[]; +extern const wchar_t kDisableMetrics[]; +extern const wchar_t kDisableMetricsReporting[]; +extern const wchar_t kBrowserAssertTest[]; +extern const wchar_t kRendererAssertTest[]; +extern const wchar_t kBrowserCrashTest[]; +extern const wchar_t kRendererCrashTest[]; +extern const wchar_t kRendererStartupDialog[]; +extern const wchar_t kPluginStartupDialog[]; +extern const wchar_t kTestShellStartupDialog[]; +extern const wchar_t kPluginLauncher[]; + +extern const wchar_t kProcessChannelID[]; +extern const wchar_t kTestingChannelID[]; +extern const wchar_t kHomePage[]; +extern const wchar_t kBrowserStartRenderersManually[]; +extern const wchar_t kRendererProcess[]; +extern const wchar_t kRendererPath[]; +extern const wchar_t kPluginProcess[]; +extern const wchar_t kSingleProcess[]; +extern const wchar_t kProcessPerTab[]; +extern const wchar_t kProcessPerSite[]; +extern const wchar_t kInProcessPlugins[]; +extern const wchar_t kNoSandbox[]; +extern const wchar_t kSafePlugins[]; +extern const wchar_t kTrustedPlugins[]; +extern const wchar_t kTestSandbox[]; +extern const wchar_t kUserDataDir[]; +extern const wchar_t kApp[]; +extern const wchar_t kAppUploadFile[]; +extern const wchar_t kDomAutomationController[]; +extern const wchar_t kPluginPath[]; +extern const wchar_t kJavaScriptFlags[]; +extern const wchar_t kGeoID[]; +extern const wchar_t kLang[]; +extern const wchar_t kDebugChildren[]; +extern const wchar_t kWaitForDebuggerChildren[]; +extern const wchar_t kLogFilterPrefix[]; +extern const wchar_t kEnableLogging[]; +extern const wchar_t kDumpHistogramsOnExit[]; +extern const wchar_t kDisableLogging[]; +extern const wchar_t kRemoteShellPort[]; +extern const wchar_t kUninstall[]; +extern const wchar_t kOmniBoxPopupCount[]; + +extern const wchar_t kAutomationClientChannelID[]; + +extern const wchar_t kRestoreLastSession[]; + +extern const wchar_t kRecordMode[]; +extern const wchar_t kPlaybackMode[]; +extern const wchar_t kNoEvents[]; + +extern const wchar_t kHideIcons[]; +extern const wchar_t kShowIcons[]; +extern const wchar_t kMakeDefaultBrowser[]; + +extern const wchar_t kProxyServer[]; +extern const wchar_t kDebugPrint[]; + +extern const wchar_t kDnsLogDetails[]; +extern const wchar_t kDnsPrefetchDisable[]; + +extern const wchar_t kAllowAllActiveX[]; + +extern const wchar_t kDisableDevTools[]; +extern const wchar_t kAlwaysEnableDevTools[]; + +extern const wchar_t kTabCountToLoadOnSessionRestore[]; + +extern const wchar_t kMemoryProfiling[]; +extern const wchar_t kMemoryModel[]; + +extern const wchar_t kEnableFileCookies[]; + +extern const wchar_t kStartMaximized[]; + +extern const wchar_t kEnableWatchdog[]; + +extern const wchar_t kFirstRun[]; + +extern const wchar_t kMessageLoopStrategy[]; +extern const wchar_t kMessageLoopHistogrammer[]; + +extern const wchar_t kImport[]; + +extern const wchar_t kSilentDumpOnDCHECK[]; + +extern const wchar_t kDisablePromptOnRepost[]; + +extern const wchar_t kDisablePopupBlocking[]; +extern const wchar_t kDisableJavaScript[]; +extern const wchar_t kDisableJava[]; +extern const wchar_t kDisablePlugins[]; +extern const wchar_t kDisableImages[]; + +extern const wchar_t kUseLowFragHeapCrt[]; + +#ifndef NDEBUG +extern const wchar_t kGearsPluginPathOverride[]; +#endif + +extern const wchar_t kUseNewHttp[]; + +} // namespace switches + +#endif // CHROME_COMMON_CHROME_SWITCHES_H__ diff --git a/chrome/common/classfactory.cc b/chrome/common/classfactory.cc new file mode 100644 index 0000000..51b0750 --- /dev/null +++ b/chrome/common/classfactory.cc @@ -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. + +#include "classfactory.h" + + +GenericClassFactory::GenericClassFactory() : + reference_count_(1) +{ + InterlockedIncrement(&object_count_); +} + + +GenericClassFactory::~GenericClassFactory() { + InterlockedDecrement(&object_count_); +} + + +LONG GenericClassFactory::object_count_ = 0; + + +STDMETHODIMP GenericClassFactory::QueryInterface(REFIID riid, LPVOID* ppobject) { + *ppobject = NULL; + + if (IsEqualIID(riid, IID_IUnknown) || + IsEqualIID(riid, IID_IClassFactory)) + *ppobject = static_cast<IClassFactory*>(this); + else + return E_NOINTERFACE; + + this->AddRef(); + return S_OK; +} + + +STDMETHODIMP_(ULONG) GenericClassFactory::AddRef() { + return InterlockedIncrement(&reference_count_); +} + + +STDMETHODIMP_(ULONG) GenericClassFactory::Release() { + if(0 == InterlockedDecrement(&reference_count_)) { + delete this; + return 0; + } + return reference_count_; +} + + +STDMETHODIMP GenericClassFactory::LockServer(BOOL) { + return E_NOTIMPL; +} diff --git a/chrome/common/classfactory.h b/chrome/common/classfactory.h new file mode 100644 index 0000000..4eeab81 --- /dev/null +++ b/chrome/common/classfactory.h @@ -0,0 +1,94 @@ +// 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. + +// ClassFactory<class> +// is a simple class factory object for the parameterized class. +// +#ifndef _CLASSFACTORY_H_ +#define _CLASSFACTORY_H_ + +#include <unknwn.h> + +// GenericClassFactory +// provides the basic COM plumbing to implement IClassFactory, and +// maintains a static count on the number of these objects in existence. +// It remains for subclasses to implement CreateInstance. +class GenericClassFactory : public IClassFactory { +public: + GenericClassFactory(); + ~GenericClassFactory(); + + //IUnknown methods + STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObject); + STDMETHOD_(ULONG, AddRef)(); + STDMETHOD_(ULONG, Release)(); + + //IClassFactory methods + STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID* ppvObject) = 0; + STDMETHOD(LockServer)(BOOL fLock); + + // generally handy for DllUnloadNow -- count of existing descendant objects + static LONG GetObjectCount() { return object_count_; } + +protected: + LONG reference_count_; // mind the reference counting for this object + static LONG object_count_; // count of all these objects +}; + + +// OneClassFactory<T> +// Knows how to be a factory for T's +template <class T> +class OneClassFactory : public GenericClassFactory +{ +public: + //IClassFactory methods + STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObject); +}; + + +template <class T> +STDMETHODIMP OneClassFactory<T>::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** result) { + *result = NULL; + + if(pUnkOuter != NULL) + return CLASS_E_NOAGGREGATION; + + T* const obj = new T(); + if(!obj) + return E_OUTOFMEMORY; + + obj->AddRef(); + HRESULT const hr = obj->QueryInterface(riid, result); + obj->Release(); + + return hr; +} + +#endif diff --git a/chrome/common/clipboard_service.cc b/chrome/common/clipboard_service.cc new file mode 100644 index 0000000..d7c5d9f --- /dev/null +++ b/chrome/common/clipboard_service.cc @@ -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. + +// Many of these functions are based on those found in +// webkit/port/platform/PasteboardWin.cpp + +#include "chrome/common/clipboard_service.h" + +#include "SkBitmap.h" + +ClipboardService::ClipboardService() { +} + +void ClipboardService::WriteBitmap(const SkBitmap& bitmap) const { + SkAutoLockPixels bitmap_lock(bitmap); + Clipboard::WriteBitmap(bitmap.getPixels(), + gfx::Size(bitmap.width(), bitmap.height())); +} diff --git a/chrome/common/clipboard_service.h b/chrome/common/clipboard_service.h new file mode 100644 index 0000000..ab7087d --- /dev/null +++ b/chrome/common/clipboard_service.h @@ -0,0 +1,54 @@ +// 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 CHROME_COMMON_CLIPBOARD_SERIVCE_H__ +#define CHROME_COMMON_CLIPBOARD_SERIVCE_H__ + +#include <string> +#include <vector> + +#include "base/clipboard.h" + +class SkBitmap; + +class ClipboardService : public Clipboard { + public: + ClipboardService(); + + // Adds a bitmap to the clipboard + // This is the slowest way to copy a bitmap to the clipboard as we must fist + // memcpy the bits into GDI and the blit the bitmap to the clipboard. + void WriteBitmap(const SkBitmap& bitmap) const; + + private: + + DISALLOW_EVIL_CONSTRUCTORS(ClipboardService); +}; + +#endif // CHROME_COMMON_CLIPBOARD_SERIVCE_H__ diff --git a/chrome/common/common.vcproj b/chrome/common/common.vcproj new file mode 100644 index 0000000..4931cc8 --- /dev/null +++ b/chrome/common/common.vcproj @@ -0,0 +1,715 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="common" + ProjectGUID="{899F1280-3441-4D1F-BA04-CCD6208D9146}" + RootNamespace="common" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="4" + InheritedPropertySheets=".\common.vsprops;$(SolutionDir)..\build\debug.vsprops;..\tools\build\win\precompiled.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="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="4" + InheritedPropertySheets=".\common.vsprops;$(SolutionDir)..\build\release.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="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="net" + > + <File + RelativePath=".\net\cookie_monster_sqlite.cc" + > + </File> + <File + RelativePath=".\net\cookie_monster_sqlite.h" + > + </File> + <File + RelativePath=".\net\dns.h" + > + </File> + <File + RelativePath=".\net\url_request_intercept_job.cc" + > + </File> + <File + RelativePath=".\net\url_request_intercept_job.h" + > + </File> + </Filter> + <Filter + Name="gfx" + > + <File + RelativePath=".\gfx\chrome_canvas.cc" + > + </File> + <File + RelativePath=".\gfx\chrome_canvas.h" + > + </File> + <File + RelativePath=".\gfx\chrome_font.cc" + > + </File> + <File + RelativePath=".\gfx\chrome_font.h" + > + </File> + <File + RelativePath=".\gfx\color_utils.cc" + > + </File> + <File + RelativePath=".\gfx\color_utils.h" + > + </File> + <File + RelativePath=".\gfx\emf.cc" + > + </File> + <File + RelativePath=".\gfx\emf.h" + > + </File> + <File + RelativePath=".\gfx\favicon_size.h" + > + </File> + <File + RelativePath=".\gfx\icon_util.cc" + > + </File> + <File + RelativePath=".\gfx\icon_util.h" + > + </File> + <File + RelativePath=".\gfx\insets.h" + > + </File> + <File + RelativePath=".\gfx\path.cc" + > + </File> + <File + RelativePath=".\gfx\path.h" + > + </File> + <File + RelativePath=".\gfx\url_elider.cc" + > + </File> + <File + RelativePath=".\gfx\url_elider.h" + > + </File> + <File + RelativePath=".\gfx\utils.h" + > + </File> + </Filter> + <Filter + Name="ipc" + > + <File + RelativePath=".\ipc_channel.cc" + > + </File> + <File + RelativePath=".\ipc_channel.h" + > + </File> + <File + RelativePath=".\ipc_channel_proxy.cc" + > + </File> + <File + RelativePath=".\ipc_channel_proxy.h" + > + </File> + <File + RelativePath=".\ipc_logging.cc" + > + </File> + <File + RelativePath=".\ipc_logging.h" + > + </File> + <File + RelativePath=".\ipc_message.cc" + > + </File> + <File + RelativePath=".\ipc_message.h" + > + </File> + <File + RelativePath=".\ipc_message_macros.h" + > + </File> + <File + RelativePath=".\ipc_message_utils.h" + > + </File> + <File + RelativePath=".\ipc_sync_channel.cc" + > + </File> + <File + RelativePath=".\ipc_sync_channel.h" + > + </File> + <File + RelativePath=".\ipc_sync_message.cc" + > + </File> + <File + RelativePath=".\ipc_sync_message.h" + > + </File> + <File + RelativePath=".\plugin_messages.cc" + > + </File> + <File + RelativePath=".\plugin_messages.h" + > + </File> + <File + RelativePath=".\plugin_messages_internal.h" + > + </File> + <File + RelativePath=".\render_messages.cc" + > + </File> + <File + RelativePath=".\render_messages.h" + > + </File> + <File + RelativePath=".\render_messages_internal.h" + > + </File> + </Filter> + <File + RelativePath=".\animation.cc" + > + </File> + <File + RelativePath=".\animation.h" + > + </File> + <File + RelativePath=".\child_process.cc" + > + </File> + <File + RelativePath=".\child_process.h" + > + </File> + <File + RelativePath=".\chrome_constants.cc" + > + </File> + <File + RelativePath=".\chrome_constants.h" + > + </File> + <File + RelativePath=".\chrome_counters.cc" + > + </File> + <File + RelativePath=".\chrome_counters.h" + > + </File> + <File + RelativePath=".\chrome_paths.cc" + > + </File> + <File + RelativePath=".\chrome_paths.h" + > + </File> + <File + RelativePath=".\chrome_plugin_api.h" + > + </File> + <File + RelativePath=".\chrome_plugin_lib.cc" + > + </File> + <File + RelativePath=".\chrome_plugin_lib.h" + > + </File> + <File + RelativePath=".\chrome_plugin_util.cc" + > + </File> + <File + RelativePath=".\chrome_plugin_util.h" + > + </File> + <File + RelativePath=".\chrome_process_filter.cc" + > + </File> + <File + RelativePath=".\chrome_process_filter.h" + > + </File> + <File + RelativePath=".\chrome_switches.cc" + > + </File> + <File + RelativePath=".\chrome_switches.h" + > + </File> + <File + RelativePath=".\classfactory.cc" + > + </File> + <File + RelativePath=".\classfactory.h" + > + </File> + <File + RelativePath=".\clipboard_service.cc" + > + </File> + <File + RelativePath=".\clipboard_service.h" + > + </File> + <File + RelativePath=".\common_resources.h" + > + </File> + <File + RelativePath=".\debug_flags.cc" + > + </File> + <File + RelativePath=".\debug_flags.h" + > + </File> + <File + RelativePath=".\drag_drop_types.cc" + > + </File> + <File + RelativePath=".\drag_drop_types.h" + > + </File> + <File + RelativePath=".\env_util.cc" + > + </File> + <File + RelativePath=".\env_util.h" + > + </File> + <File + RelativePath=".\env_vars.cc" + > + </File> + <File + RelativePath=".\env_vars.h" + > + </File> + <File + RelativePath=".\filter_policy.h" + > + </File> + <File + RelativePath=".\gears_api.h" + > + </File> + <File + RelativePath=".\jpeg_codec.cc" + > + </File> + <File + RelativePath=".\jpeg_codec.h" + > + </File> + <File + RelativePath=".\json_value_serializer.cc" + > + </File> + <File + RelativePath=".\json_value_serializer.h" + > + </File> + <File + RelativePath=".\jstemplate_builder.cc" + > + </File> + <File + RelativePath=".\jstemplate_builder.h" + > + </File> + <File + RelativePath=".\l10n_util.cc" + > + </File> + <File + RelativePath=".\l10n_util.h" + > + </File> + <File + RelativePath=".\libxml_utils.cc" + > + </File> + <File + RelativePath=".\libxml_utils.h" + > + </File> + <File + RelativePath=".\logging_chrome.cc" + > + </File> + <File + RelativePath=".\logging_chrome.h" + > + </File> + <File + RelativePath=".\message_router.cc" + > + </File> + <File + RelativePath=".\message_router.h" + > + </File> + <File + RelativePath=".\mru_cache.h" + > + </File> + <File + RelativePath=".\notification_details.h" + > + </File> + <File + RelativePath=".\notification_service.cc" + > + </File> + <File + RelativePath=".\notification_service.h" + > + </File> + <File + RelativePath=".\notification_source.h" + > + </File> + <File + RelativePath=".\notification_types.h" + > + </File> + <File + RelativePath=".\os_exchange_data.cc" + > + </File> + <File + RelativePath=".\os_exchange_data.h" + > + </File> + <File + RelativePath=".\page_transition_types.h" + > + </File> + <File + RelativePath=".\slide_animation.cc" + > + </File> + <File + RelativePath=".\slide_animation.h" + > + </File> + <File + RelativePath=".\throb_animation.cc" + > + </File> + <File + RelativePath=".\throb_animation.h" + > + </File> + <File + RelativePath="..\tools\build\win\precompiled.cc" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\tools\build\win\precompiled.h" + > + </File> + <File + RelativePath=".\pref_member.h" + > + </File> + <File + RelativePath=".\pref_names.cc" + > + </File> + <File + RelativePath=".\pref_names.h" + > + </File> + <File + RelativePath=".\pref_service.cc" + > + </File> + <File + RelativePath=".\pref_service.h" + > + </File> + <File + RelativePath=".\process_watcher.cc" + > + </File> + <File + RelativePath=".\process_watcher.h" + > + </File> + <File + RelativePath=".\rand_util.cc" + > + </File> + <File + RelativePath=".\rand_util.h" + > + </File> + <File + RelativePath=".\ref_counted_util.h" + > + </File> + <File + RelativePath=".\resource_bundle.cc" + > + </File> + <File + RelativePath=".\resource_bundle.h" + > + </File> + <File + RelativePath=".\resource_dispatcher.cc" + > + </File> + <File + RelativePath=".\resource_dispatcher.h" + > + </File> + <File + RelativePath=".\scoped_vector.h" + > + </File> + <File + RelativePath=".\security_filter_peer.cc" + > + </File> + <File + RelativePath=".\security_filter_peer.h" + > + </File> + <File + RelativePath=".\sqlite_compiled_statement.cc" + > + </File> + <File + RelativePath=".\sqlite_compiled_statement.h" + > + </File> + <File + RelativePath=".\sqlite_utils.cc" + > + </File> + <File + RelativePath=".\sqlite_utils.h" + > + </File> + <File + RelativePath=".\stl_util-inl.h" + > + </File> + <File + RelativePath=".\task_queue.cc" + > + </File> + <File + RelativePath=".\task_queue.h" + > + </File> + <File + RelativePath=".\text_zoom.h" + > + </File> + <File + RelativePath=".\thumbnail_score.cc" + > + </File> + <File + RelativePath=".\thumbnail_score.h" + > + </File> + <File + RelativePath=".\time_format.cc" + > + </File> + <File + RelativePath=".\time_format.h" + > + </File> + <File + RelativePath=".\visitedlink_common.cc" + > + </File> + <File + RelativePath=".\visitedlink_common.h" + > + </File> + <File + RelativePath=".\win_safe_util.cc" + > + </File> + <File + RelativePath=".\win_safe_util.h" + > + </File> + <File + RelativePath=".\win_util.cc" + > + </File> + <File + RelativePath=".\win_util.h" + > + </File> + <File + RelativePath=".\worker_thread_ticker.cc" + > + </File> + <File + RelativePath=".\worker_thread_ticker.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/common/common.vsprops b/chrome/common/common.vsprops new file mode 100644 index 0000000..da53642 --- /dev/null +++ b/chrome/common/common.vsprops @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="common (chrome)" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\third_party\libjpeg\using_libjpeg.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;..\tools\build\win\using_generated_strings.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\npapi\using_npapi.vsprops;$(SolutionDir)third_party\wtl\using_wtl.vsprops" + > +</VisualStudioPropertySheet> diff --git a/chrome/common/common_resources.h b/chrome/common/common_resources.h new file mode 100644 index 0000000..9ec89db --- /dev/null +++ b/chrome/common/common_resources.h @@ -0,0 +1,3 @@ +// 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_JSTEMPLATE_JS 300 diff --git a/chrome/common/common_resources.rc b/chrome/common/common_resources.rc new file mode 100644 index 0000000..0a8690b --- /dev/null +++ b/chrome/common/common_resources.rc @@ -0,0 +1,16 @@ +// Resources used by common/*. +// +// 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 "common\\common_resources.h" + +///////////////////////////////////////////////////////////////////////////// +// +// data resources +// + +IDR_JSTEMPLATE_JS BINDATA "third_party\\jstemplate\\jstemplate_compiled.js" diff --git a/chrome/common/debug_flags.cc b/chrome/common/debug_flags.cc new file mode 100644 index 0000000..6b10d13 --- /dev/null +++ b/chrome/common/debug_flags.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 <windows.h> + +#include "chrome/common/debug_flags.h" + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "chrome/common/chrome_switches.h" + +bool DebugFlags::ProcessDebugFlags(std::wstring* command_line, + ChildProcessType type, + bool is_in_sandbox) { + bool should_help_child = false; + CommandLine current_cmd_line; + if (current_cmd_line.HasSwitch(switches::kDebugChildren)) { + // Look to pass-on the kDebugOnStart flag. + std::wstring value; + value = current_cmd_line.GetSwitchValue(switches::kDebugChildren); + if (value.empty() || + (type == RENDERER && value == switches::kRendererProcess) || + (type == PLUGIN && value == switches::kPluginProcess)) { + CommandLine::AppendSwitch(command_line, switches::kDebugOnStart); + should_help_child = true; + } + CommandLine::AppendSwitchWithValue(command_line, + switches::kDebugChildren, + value); + } else if (current_cmd_line.HasSwitch(switches::kWaitForDebuggerChildren)) { + // Look to pass-on the kWaitForDebugger flag. + std::wstring value; + value = current_cmd_line.GetSwitchValue(switches::kWaitForDebuggerChildren); + if (value.empty() || + (type == RENDERER && value == switches::kRendererProcess) || + (type == PLUGIN && value == switches::kPluginProcess)) { + CommandLine::AppendSwitch(command_line, switches::kWaitForDebugger); + } + CommandLine::AppendSwitchWithValue(command_line, + switches::kWaitForDebuggerChildren, + value); + } + return should_help_child; +}
\ No newline at end of file diff --git a/chrome/common/debug_flags.h b/chrome/common/debug_flags.h new file mode 100644 index 0000000..05c6e5d --- /dev/null +++ b/chrome/common/debug_flags.h @@ -0,0 +1,54 @@ +// 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 CHROME_COMMON_DEBUG_FLAGS_H__ +#define CHROME_COMMON_DEBUG_FLAGS_H__ + +#include <string> + +class DebugFlags { + public: + enum ChildProcessType { + RENDERER, + PLUGIN, + UNKNOWN + }; + + // Updates the command line arguments with debug-related flags. If debug flags + // have been used with this process, they will be filtered and added to + // command_line as needed. is_in_sandbox must be true if the child process will + // be in a sandbox. + // Returns true if the caller should "help" the child process by calling the JIT + // debugger on it. It may only happen if is_in_sandbox is true. + static bool ProcessDebugFlags(std::wstring* command_line, + ChildProcessType type, + bool is_in_sandbox); +}; + +#endif // CHROME_COMMON_DEBUG_FLAGS_H__ diff --git a/chrome/common/drag_drop_types.cc b/chrome/common/drag_drop_types.cc new file mode 100644 index 0000000..0b83cdf --- /dev/null +++ b/chrome/common/drag_drop_types.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 "chrome/common/drag_drop_types.h" + +#include <oleidl.h> + +int DragDropTypes::DropEffectToDragOperation( + DWORD effect) { + int drag_operation = DRAG_NONE; + if (effect & DROPEFFECT_LINK) + drag_operation |= DRAG_LINK; + if (effect & DROPEFFECT_COPY) + drag_operation |= DRAG_COPY; + if (effect & DROPEFFECT_MOVE) + drag_operation |= DRAG_MOVE; + return drag_operation; +} + +DWORD DragDropTypes::DragOperationToDropEffect(int drag_operation) { + DWORD drop_effect = DROPEFFECT_NONE; + if (drag_operation & DRAG_LINK) + drop_effect |= DROPEFFECT_LINK; + if (drag_operation & DRAG_COPY) + drop_effect |= DROPEFFECT_COPY; + if (drag_operation & DRAG_MOVE) + drop_effect |= DROPEFFECT_MOVE; + return drop_effect; +} diff --git a/chrome/common/drag_drop_types.h b/chrome/common/drag_drop_types.h new file mode 100644 index 0000000..f3a7a6e --- /dev/null +++ b/chrome/common/drag_drop_types.h @@ -0,0 +1,48 @@ +// 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 CHROME_COMMON_DRAG_DROP_TYPES_H__ +#define CHROME_COMMON_DRAG_DROP_TYPES_H__ + +#include <windows.h> + +class DragDropTypes { + public: + enum DragOperation { + DRAG_NONE = 0, + DRAG_MOVE = 1 << 0, + DRAG_COPY = 1 << 1, + DRAG_LINK = 1 << 2 + }; + + static DWORD DragOperationToDropEffect(int drag_operation); + static int DropEffectToDragOperation(DWORD effect); +}; + +#endif diff --git a/chrome/common/env_util.cc b/chrome/common/env_util.cc new file mode 100644 index 0000000..d2f09ff --- /dev/null +++ b/chrome/common/env_util.cc @@ -0,0 +1,82 @@ +// 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 "chrome/common/env_util.h" + +#include "base/basictypes.h" +#include "base/logging.h" + +static const DWORDLONG kBytesPerMegabyte = 1048576; + +namespace env_util { + +std::string GetOperatingSystemName() { + return "Windows"; +} + +std::string GetOperatingSystemVersion() { + OSVERSIONINFO info = {0}; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&info); + + char result[20]; + memset(result, 0, arraysize(result)); + _snprintf_s(result, arraysize(result), + "%lu.%lu", info.dwMajorVersion, info.dwMinorVersion); + return std::string(result); +} + +int GetPhysicalMemoryMB() { + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + if (::GlobalMemoryStatusEx(&status)) + return static_cast<int>(status.ullTotalPhys / kBytesPerMegabyte); + + NOTREACHED() << "GlobalMemoryStatusEx failed."; + return 0; +} + +std::string GetCPUArchitecture() { + // TODO: Make this vary when we support any other architectures. + return "x86"; +} + +void GetPrimaryDisplayDimensions(int* width, int* height) { + if (width) + *width = GetSystemMetrics(SM_CXSCREEN); + + if (height) + *height = GetSystemMetrics(SM_CYSCREEN); +} + +int GetDisplayCount() { + return GetSystemMetrics(SM_CMONITORS); +} + +} // namespace env_util diff --git a/chrome/common/env_util.h b/chrome/common/env_util.h new file mode 100644 index 0000000..b098a8f --- /dev/null +++ b/chrome/common/env_util.h @@ -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. + +// This file defines utility functions that can report details about the +// host operating environment. + +#ifndef CHROME_COMMON_ENV_UTIL_H__ +#define CHROME_COMMON_ENV_UTIL_H__ + +#include <windows.h> +#include <string> + +namespace env_util { + +// Test if the given environment variable is defined. +inline bool HasEnvironmentVariable(const wchar_t* var) { + return GetEnvironmentVariable(var, NULL, 0) != 0; +} + +// Returns the name of the host operating system. +std::string GetOperatingSystemName(); + +// Returns the version of the host operating system. +std::string GetOperatingSystemVersion(); + +// Returns the total amount of physical memory present. +int GetPhysicalMemoryMB(); + +// Returns the CPU architecture of the system. +std::string GetCPUArchitecture(); + +// Returns the pixel dimensions of the primary display via the +// width and height parameters. +void GetPrimaryDisplayDimensions(int* width, int* height); + +// Return the number of displays. +int GetDisplayCount(); + +} // namespace env_util + +#endif // CHROME_COMMON_ENV_UTIL_H__ diff --git a/chrome/common/env_vars.cc b/chrome/common/env_vars.cc new file mode 100644 index 0000000..17ac68f --- /dev/null +++ b/chrome/common/env_vars.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 "chrome/common/env_vars.h" + +namespace env_vars { + +// We call running in unattended mode (for automated testing) "headless". +// This mode can be enabled using this variable or by the kNoErrorDialogs +// switch. +const wchar_t kHeadless[] = L"CHROME_HEADLESS"; + +// The name of the log file. +const wchar_t kLogFileName[] = L"CHROME_LOG_FILE"; + +// CHROME_CRASHED exists if a previous instance of chrome has crashed. This +// triggers the 'restart chrome' dialog. CHROME_RESTART contains the strings +// that are needed to show the dialog. +// +// The strings RIGHT_TO_LEFT and LEFT_TO_RIGHT indicate the locale direction. +// For example, for Hebrew and Arabic locales, we use RIGHT_TO_LEFT so that the +// dialog is displayed using the right orientation. +// +// If you modify these constants, you also need to modify them in breakpad.cc. +const wchar_t kShowRestart[] = L"CHROME_CRASHED"; +const wchar_t kRestartInfo[] = L"CHROME_RESTART"; +const wchar_t kRtlLocale[] = L"RIGHT_TO_LEFT"; +const wchar_t kLtrLocale[] = L"LEFT_TO_RIGHT"; + +} // namespace env_vars diff --git a/chrome/common/env_vars.h b/chrome/common/env_vars.h new file mode 100644 index 0000000..51b18ad --- /dev/null +++ b/chrome/common/env_vars.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. + +// Defines all the environment variables used by Chrome. + +#ifndef CHROME_COMMON_ENV_VARS_H__ +#define CHROME_COMMON_ENV_VARS_H__ + +namespace env_vars { + +extern const wchar_t kHeadless[]; +extern const wchar_t kLogFileName[]; +extern const wchar_t kShowRestart[]; +extern const wchar_t kRestartInfo[]; +extern const wchar_t kRtlLocale[]; +extern const wchar_t kLtrLocale[]; + +} // namespace env_vars + +#endif // CHROME_COMMON_ENV_VARS_H__ diff --git a/chrome/common/filter_policy.h b/chrome/common/filter_policy.h new file mode 100644 index 0000000..b8019df --- /dev/null +++ b/chrome/common/filter_policy.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 CHROME_COMMON_FILTER_POLICY_H__ +#define CHROME_COMMON_FILTER_POLICY_H__ + +#include "base/basictypes.h" + +// When an insecure resource (mixed content or bad HTTPS) is loaded, the browser +// can decide to filter it. The filtering is done in the renderer. This class +// enumerates the different policy that can be used for the filtering. It is +// passed along with resource response messages. +class FilterPolicy { + public: + enum Type { + // Pass all types of resources through unmodified. + DONT_FILTER = 0, + + // Block all types of resources, except images. For images, modify them to + // indicate that they have been filtered. + // TODO(abarth): This is a misleading name for this enum value. We should + // change it to something more suggestive of what this + // actually does. + FILTER_ALL_EXCEPT_IMAGES, + + // Block all types of resources. + FILTER_ALL + }; + + static bool ValidType(int32 type) { + return type >= DONT_FILTER && type <= FILTER_ALL; + } + + static Type FromInt(int32 type) { + return static_cast<Type>(type); + } + + private: + // Don't instantiate this class. + FilterPolicy(); + ~FilterPolicy(); + +}; + +#endif // CHROME_COMMON_FILTER_POLICY_H__ diff --git a/chrome/common/gears_api.h b/chrome/common/gears_api.h new file mode 100644 index 0000000..a30654b --- /dev/null +++ b/chrome/common/gears_api.h @@ -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. +// +// This header specifies extensions to the Chrome Plugin API to support Gears. + +#ifndef CHROME_COMMON_GEARS_API_H__ +#define CHROME_COMMON_GEARS_API_H__ + +#include "chrome/common/chrome_plugin_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// CommandIDs used when Chrome calls into Gears using CPP_HandleCommand. +// Note: do not change the enum values. We want to preserve backwards +// compatibility. +typedef enum { + // Ask gears to show its settings dialog. Typical usage is for the plugin + // to display it using a call to CPB_ShowHtmlDialog. No command_data is + // provided. + GEARSPLUGINCOMMAND_SHOW_SETTINGS = 0, + + // Ask gears to create a shortcut to a web page. command_data points + // to a GearsShortcutData struct. + GEARSPLUGINCOMMAND_CREATE_SHORTCUT = 1, + + // Query gears for the list of installed shortcuts. command_data points + // to a GearsShortcutList struct. + GEARSPLUGINCOMMAND_GET_SHORTCUT_LIST = 2, +} GearsPluginCommand; + +// CommandIDs used when Gears calls into Chrome using CPB_HandleCommand. +// Note: do not change the enum values. We want to preserve backwards +// compatibility. +typedef enum { + // Tell chrome that the GEARSPLUGINCOMMAND_CREATE_SHORTCUT command is done, + // and the user has closed the dialog. command_data points to the same + // GearsShortcutData struct that was passed to the plugin command. + GEARSBROWSERCOMMAND_CREATE_SHORTCUT_DONE = 1, + + // Notifies the browser of changes to the gears shortcuts database. + // command_data is null. + GEARSBROWSERCOMMAND_NOTIFY_SHORTCUTS_CHANGED = 3, +} GearsBrowserCommand; + +// Note: currently only 16x16, 32x32, 48x48, and 128x128 icons are supported. +typedef struct _GearsShortcutIcon { + const char* size; // unused + const char* url; // the URL of the icon, which should be a PNG image + int width; // width of the icon + int height; // height of the icon +} GearsShortcutIcon; + +// Command data for GEARSPLUGINCOMMAND_CREATE_SHORTCUT. +typedef struct _GearsShortcutData { + const char* name; // the shortcut's name (also used as the filename) + const char* url; // the URL that the shortcut should launch + const char* description; // an optional description + GearsShortcutIcon icons[4]; // list of icons to use for this shortcut +} GearsShortcutData; + +// Command data for GEARSPLUGINCOMMAND_GET_SHORTCUT_LIST. +typedef struct _GearsShortcutList { + // Note: these are output params, set by Gears. There are no input params. + // Memory for these shortcuts, including the strings they hold, should be + // freed by the browser using CPB_Free. + GearsShortcutData* shortcuts; // array of installed shortcuts + uint32 num_shortcuts; // size of the array +} GearsShortcutList; + +// Command data for GEARSBROWSERCOMMAND_CREATE_SHORTCUT_DONE +typedef struct _GearsCreateShortcutResult { + GearsShortcutData* shortcut; // pointer to struct passed to + // GEARSPLUGINCOMMAND_CREATE_SHORTCUT + CPError result; // CPERR_SUCCESS if shortcut was created, or error otherwise +} GearsCreateShortcutResult; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CHROME_COMMON_GEARS_API_H__ diff --git a/chrome/common/gfx/chrome_canvas.cc b/chrome/common/gfx/chrome_canvas.cc new file mode 100644 index 0000000..631d2f3 --- /dev/null +++ b/chrome/common/gfx/chrome_canvas.cc @@ -0,0 +1,404 @@ +// 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 <limits> + +#include "chrome/common/gfx/chrome_canvas.h" + +#include "base/gfx/platform_canvas.h" +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "skia/include/SkShader.h" + +ChromeCanvas::ChromeCanvas(int width, int height, bool is_opaque) + : gfx::PlatformCanvas(width, height, is_opaque) { +} + +ChromeCanvas::ChromeCanvas() : gfx::PlatformCanvas() { +} + +ChromeCanvas::~ChromeCanvas() { +} + +bool ChromeCanvas::GetClipRect(gfx::Rect* r) { + SkRect clip; + if (!getClipBounds(&clip)) { + if (r) + r->SetRect(0, 0, 0, 0); + return false; + } + r->SetRect(SkScalarRound(clip.fLeft), SkScalarRound(clip.fTop), + SkScalarRound(clip.fRight - clip.fLeft), + SkScalarRound(clip.fBottom - clip.fTop)); + return true; +} + +bool ChromeCanvas::ClipRectInt(int x, int y, int w, int h) { + SkRect new_clip; + new_clip.set(SkIntToScalar(x), SkIntToScalar(y), + SkIntToScalar(x + w), SkIntToScalar(y + h)); + return clipRect(new_clip); +} + +bool ChromeCanvas::IntersectsClipRectInt(int x, int y, int w, int h) { + SkRect clip; + return getClipBounds(&clip) && + clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w), + SkIntToScalar(y + h)); +} + +void ChromeCanvas::TranslateInt(int x, int y) { + translate(SkIntToScalar(x), SkIntToScalar(y)); +} + +void ChromeCanvas::ScaleInt(int x, int y) { + scale(SkIntToScalar(x), SkIntToScalar(y)); +} + +void ChromeCanvas::FillRectInt(const SkColor& color, + int x, int y, int w, int h) { + SkPaint paint; + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + paint.setPorterDuffXfermode(SkPorterDuff::kSrcOver_Mode); + FillRectInt(x, y, w, h, paint); +} + +void ChromeCanvas::FillRectInt(int x, int y, int w, int h, + const SkPaint& paint) { + SkRect rc = {SkIntToScalar(x), SkIntToScalar(y), + SkIntToScalar(x + w), SkIntToScalar(y + h) }; + drawRect(rc, paint); +} + +void ChromeCanvas::DrawRectInt(const SkColor& color, + int x, int y, int w, int h) { + DrawRectInt(color, x, y, w, h, SkPorterDuff::kSrcOver_Mode); +} + +void ChromeCanvas::DrawRectInt(const SkColor& color, int x, int y, int w, int h, + SkPorterDuff::Mode mode) { + SkPaint paint; + paint.setColor(color); + paint.setStyle(SkPaint::kStroke_Style); + // Contrary to the docs, a width of 0 results in nothing. + paint.setStrokeWidth(SkIntToScalar(1)); + paint.setPorterDuffXfermode(mode); + + SkRect rc = {SkIntToScalar(x), SkIntToScalar(y), + SkIntToScalar(x + w), SkIntToScalar(y + h) }; + drawRect(rc, paint); +} + +void ChromeCanvas::DrawFocusRect(int x, int y, int width, int height) { + // Create a 2D bitmap containing alternating on/off pixels - we do this + // so that you never get two pixels of the same color around the edges + // of the focus rect (this may mean that opposing edges of the rect may + // have a dot pattern out of phase to each other). + static SkBitmap* dots = NULL; + if (!dots) { + int col_pixels = 32; + int row_pixels = 32; + + dots = new SkBitmap; + dots->setConfig(SkBitmap::kARGB_8888_Config, col_pixels, row_pixels); + dots->allocPixels(); + dots->eraseARGB(0, 0, 0, 0); + + uint32_t* dot = dots->getAddr32(0, 0); + for (int i = 0; i < row_pixels; i++) { + for (int u = 0; u < col_pixels; u++) { + if ((u % 2 + i % 2) % 2 != 0) { + dot[i * row_pixels + u] = SK_ColorGRAY; + } + } + } + } + + // First the horizontal lines. + + // Make a shader for the bitmap with an origin of the box we'll draw. This + // shader is refcounted and will have an initial refcount of 1. + SkShader* shader = SkShader::CreateBitmapShader( + *dots, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); + // Assign the shader to the paint & release our reference. The paint will + // now own the shader and the shader will be destroyed when the paint goes + // out of scope. + SkPaint paint; + paint.setShader(shader); + shader->unref(); + + SkRect rect; + rect.set(SkIntToScalar(x), SkIntToScalar(y), + SkIntToScalar(x + width), SkIntToScalar(y + 1)); + drawRect(rect, paint); + rect.set(SkIntToScalar(x), SkIntToScalar(y + height - 1), + SkIntToScalar(x + width), SkIntToScalar(y + height)); + drawRect(rect, paint); + + rect.set(SkIntToScalar(x), SkIntToScalar(y), + SkIntToScalar(x + 1), SkIntToScalar(y + height)); + drawRect(rect, paint); + rect.set(SkIntToScalar(x + width - 1), SkIntToScalar(y), + SkIntToScalar(x + width), SkIntToScalar(y + height)); + drawRect(rect, paint); +} + +void ChromeCanvas::DrawBitmapInt(const SkBitmap& bitmap, int x, int y) { + drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y)); +} + +void ChromeCanvas::DrawBitmapInt(const SkBitmap& bitmap, int x, int y, + const SkPaint& paint) { + drawBitmap(bitmap, SkIntToScalar(x), SkIntToScalar(y), &paint); +} + +void ChromeCanvas::DrawBitmapInt(const SkBitmap& bitmap, int src_x, int src_y, + int src_w, int src_h, int dest_x, int dest_y, + int dest_w, int dest_h, + bool filter) { + SkPaint p; + DrawBitmapInt(bitmap, src_x, src_y, src_w, src_h, dest_x, dest_y, + dest_w, dest_h, filter, p); +} + +void ChromeCanvas::DrawBitmapInt(const SkBitmap& bitmap, int src_x, int src_y, + int src_w, int src_h, int dest_x, int dest_y, + int dest_w, int dest_h, + bool filter, const SkPaint& paint) { + DLOG_ASSERT(src_x + src_w < std::numeric_limits<int16_t>::max() && + src_y + src_h < std::numeric_limits<int16_t>::max()); + if (src_w <= 0 || src_h <= 0 || dest_w <= 0 || dest_h <= 0) { + NOTREACHED() << "Attempting to draw bitmap to/from an empty rect!"; + return; + } + + if (!IntersectsClipRectInt(dest_x, dest_y, dest_w, dest_h)) + return; + + SkRect dest_rect = { SkIntToScalar(dest_x), + SkIntToScalar(dest_y), + SkIntToScalar(dest_x + dest_w), + SkIntToScalar(dest_y + dest_h) }; + + if (src_w == dest_w && src_h == dest_h) { + // Workaround for apparent bug in Skia that causes image to occasionally + // shift. + SkIRect src_rect = { src_x, src_y, src_x + src_w, src_y + src_h }; + drawBitmapRect(bitmap, &src_rect, dest_rect, &paint); + return; + } + + // Make a bitmap shader that contains the bitmap we want to draw. This is + // basically what SkCanvas.drawBitmap does internally, but it gives us + // more control over quality and will use the mipmap in the source image if + // it has one, whereas drawBitmap won't. + SkShader* shader = SkShader::CreateBitmapShader(bitmap, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + SkMatrix shader_scale; + shader_scale.setScale( + SkFloatToScalar(static_cast<float>(dest_w) / src_w), + SkFloatToScalar(static_cast<float>(dest_h) / src_h)); + shader_scale.postTranslate(SkIntToScalar(dest_x - src_x), SkIntToScalar(dest_y - src_y)); + shader->setLocalMatrix(shader_scale); + + // Set up our paint to use the shader & release our reference (now just owned + // by the paint). + SkPaint p(paint); + p.setFilterBitmap(filter); + p.setShader(shader); + shader->unref(); + + // The rect will be filled by the bitmap. + drawRect(dest_rect, p); +} + +int ChromeCanvas::ComputeFormatFlags(int flags) { + int f = 0; + + // Setting the text alignment explicitly in case it hasn't already been set. + // This will make sure that we don't align text to the left on RTL locales + // just because no alignment flag was passed to DrawStringInt(). + if (!(flags & (TEXT_ALIGN_CENTER | TEXT_ALIGN_RIGHT | TEXT_ALIGN_LEFT))) { + flags |= l10n_util::DefaultCanvasTextAlignment(); + } + + if (flags & HIDE_PREFIX) + f |= DT_HIDEPREFIX; + else if ((flags & SHOW_PREFIX) == 0) + f |= DT_NOPREFIX; + + if (flags & MULTI_LINE) + f |= DT_WORDBREAK; + else + f |= DT_SINGLELINE | DT_END_ELLIPSIS | DT_VCENTER; + + // vertical alignment + if (flags & TEXT_VALIGN_TOP) + f |= DT_TOP; + else if (flags & TEXT_VALIGN_BOTTOM) + f |= DT_BOTTOM; + else + f |= DT_VCENTER; + + // horizontal alignment + if (flags & TEXT_ALIGN_CENTER) + f |= DT_CENTER; + else if (flags & TEXT_ALIGN_RIGHT) + f |= DT_RIGHT; + else + f |= DT_LEFT; + + // In order to make sure RTL/BiDi strings are rendered correctly, we must + // pass the flag DT_RTLREADING to DrawText (when the locale's language is + // a right-to-left language) so that Windows does the right thing. + // + // In addition to correctly displaying text containing both RTL and LTR + // elements (for example, a string containing a telephone number within a + // sentence in Hebrew, or a sentence in Hebrew that contains a word in + // English) this flag also makes sure that if there is not enough space to + // display the entire string, the ellipsis is displayed on the left hand side + // of the truncated string and not on the right hand side. + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) { + f |= DT_RTLREADING; + } + + return f; +} + +void ChromeCanvas::SizeStringInt(const std::wstring& text, + const ChromeFont& font, + int *width, int *height, int flags) { + HDC dc = beginPlatformPaint(); + HFONT old_font = static_cast<HFONT>(SelectObject(dc, font.hfont())); + RECT b; + b.left = 0; + b.top = 0; + b.right = *width; + b.bottom = *height; + DoDrawText(dc, text, &b, ComputeFormatFlags(flags) | DT_CALCRECT); + endPlatformPaint(); + + // Restore the old font. This way we don't have to worry if the caller + // deletes the font and the DC lives longer. + SelectObject(dc, old_font); + *width = b.right; + *height = b.bottom; +} + +void ChromeCanvas::DrawStringInt(const std::wstring& text, HFONT font, + const SkColor& color, int x, int y, int w, + int h, int flags) { + if (!IntersectsClipRectInt(x, y, w, h)) + return; + + getTopPlatformDevice().prepareForGDI(x, y, w, h); + RECT text_bounds = { x, y, x + w, y + h }; + HDC dc = beginPlatformPaint(); + SetBkMode(dc, TRANSPARENT); + HFONT old_font = (HFONT)SelectObject(dc, font); + COLORREF brush_color = RGB(SkColorGetR(color), SkColorGetG(color), + SkColorGetB(color)); + SetTextColor(dc, brush_color); + + int f = ComputeFormatFlags(flags); + DoDrawText(dc, text, &text_bounds, f); + endPlatformPaint(); + + // Restore the old font. This way we don't have to worry if the caller + // deletes the font and the DC lives longer. + SelectObject(dc, old_font); + getTopPlatformDevice().postProcessGDI(x, y, w, h); +} + +// We make sure that LTR text we draw in an RTL context is modified +// appropriately to make sure it maintains it LTR orientation. +void ChromeCanvas::DoDrawText(HDC hdc, const std::wstring& text, + RECT* text_bounds, int flags) { + std::wstring localized_text; + const wchar_t* string_ptr = text.c_str(); + int string_size = static_cast<int>(text.length()); + if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text)) { + string_ptr = localized_text.c_str(); + string_size = static_cast<int>(localized_text.length()); + } + + DrawText(hdc, string_ptr, string_size, text_bounds, flags); +} + + +void ChromeCanvas::DrawStringInt(const std::wstring& text, + const ChromeFont& font, + const SkColor& color, + int x, int y, int w, int h, int flags) { + DrawStringInt(text, font.hfont(), color, x, y, w, h, flags); +} + +void ChromeCanvas::TileImageInt(const SkBitmap& bitmap, + int x, int y, int w, int h, + SkPorterDuff::Mode mode) { + if (!IntersectsClipRectInt(x, y, w, h)) + return; + + SkPaint paint; + int bitmap_width = bitmap.width(); + int bitmap_height = bitmap.height(); + + SkShader* shader = SkShader::CreateBitmapShader(bitmap, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + paint.setShader(shader); + paint.setPorterDuffXfermode(mode); + + // CreateBitmapShader returns a Shader with a reference count of one, we + // need to unref after paint takes ownership of the shader. + shader->unref(); + save(); + translate(SkIntToScalar(x), SkIntToScalar(y)); + ClipRectInt(0, 0, w, h); + drawPaint(paint); + restore(); +} + +void ChromeCanvas::TileImageInt(const SkBitmap& bitmap, + int x, int y, int w, int h) { + TileImageInt(bitmap, x, y, w, h, SkPorterDuff::kSrcOver_Mode); +} + +SkBitmap ChromeCanvas::ExtractBitmap() { + const SkBitmap& device_bitmap = getDevice()->accessBitmap(false); + + // Make a bitmap to return, and a canvas to draw into it. We don't just want + // to call extractSubset or the copy constuctor, since we want an actual copy + // of the bitmap. + SkBitmap result; + device_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config); + return result; +} diff --git a/chrome/common/gfx/chrome_canvas.h b/chrome/common/gfx/chrome_canvas.h new file mode 100644 index 0000000..a291880 --- /dev/null +++ b/chrome/common/gfx/chrome_canvas.h @@ -0,0 +1,213 @@ +// 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 CHROME_COMMON_GFX_CHROME_CANVAS_H__ +#define CHROME_COMMON_GFX_CHROME_CANVAS_H__ + +#include <windows.h> +#include <string> +#include "base/basictypes.h" +#include "base/gfx/platform_canvas.h" +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/l10n_util.h" + +namespace gfx { +class Rect; +} + +// ChromeCanvas is the SkCanvas used by Views for all painting. It +// provides a handful of methods for the common operations used throughout +// Views. With few exceptions, you should NOT create a ChromeCanvas directly, +// rather one will be passed to you via the various paint methods in view. +// +// All methods that take integer arguments (as is used throughout views) +// end with Int. If you need to use methods provided by the superclass +// you'll need to do a conversion. In particular you'll need to use +// macro SkIntToScalar(xxx), or if converting from a scalar to an integer +// SkScalarRound. +// +// A handful of methods in this class are overloaded providing an additional +// argument of type SkPorterDuff::Mode. SkPorterDuff::Mode specifies how the +// source and destination colors are combined. Unless otherwise specified, +// the variant that does not take a SkPorterDuff::Mode uses a transfer mode +// of kSrcOver_Mode. +class ChromeCanvas : public gfx::PlatformCanvas { + public: + // Specifies the alignment for text rendered with the DrawStringInt method. + static const int TEXT_ALIGN_LEFT = 1; + static const int TEXT_ALIGN_CENTER = 2; + static const int TEXT_ALIGN_RIGHT = 4; + static const int TEXT_VALIGN_TOP = 8; + static const int TEXT_VALIGN_MIDDLE = 16; + static const int TEXT_VALIGN_BOTTOM = 32; + + // Specifies the text consists of multiple lines. + static const int MULTI_LINE = 64; + + // By default DrawStringInt does not process the prefix ('&') character + // specially. That is, the string "&foo" is rendered as "&foo". When + // rendering text from a resource that uses the prefix character for + // mnemonics, the prefix should be processed and can be rendered as an + // underline (SHOW_PREFIX), or not rendered at all (HIDE_PREFIX). + static const int SHOW_PREFIX = 128; + static const int HIDE_PREFIX = 256; + + // Creates an empty ChromeCanvas. Callers must use initialize before using + // the canvas. + ChromeCanvas(); + + ChromeCanvas(int width, int height, bool is_opaque); + + virtual ~ChromeCanvas(); + + // Retrieves the clip rectangle and sets it in the specified rectangle if any. + // Returns true if the clip rect is non-empty. + bool GetClipRect(gfx::Rect* clip_rect); + + // Wrapper function that takes integer arguments. + // Returns true if the clip is non-empty. + // See clipRect for specifics. + bool ClipRectInt(int x, int y, int w, int h); + + // Test whether the provided rectangle intersects the current clip rect. + bool IntersectsClipRectInt(int x, int y, int w, int h); + + // Wrapper function that takes integer arguments. + // See translate() for specifics. + void TranslateInt(int x, int y); + + // Wrapper function that takes integer arguments. + // See scale() for specifics. + void ScaleInt(int x, int y); + + // Fills the given rectangle with the given paint's parameters. + void FillRectInt(int x, int y, int w, int h, const SkPaint& paint); + + // Fills the specified region with the specified color using a transfer + // mode of SkPorterDuff::kSrcOver_Mode. + void FillRectInt(const SkColor& color, int x, int y, int w, int h); + + // Draws a single pixel line in the specified region with the specified + // color, using a transfer mode of SkPorterDuff::kSrcOver_Mode. + void DrawRectInt(const SkColor& color, int x, int y, int w, int h); + + // Draws a single pixel line in the specified region with the specified + // color and transfer mode. + void DrawRectInt(const SkColor& color, int x, int y, int w, int h, + SkPorterDuff::Mode mode); + + // Draws a bitmap with the origin at the specified location. The upper left + // corner of the bitmap is rendered at the specified location. + void DrawBitmapInt(const SkBitmap& bitmap, int x, int y); + + // Draws a bitmap with the origin at the specified location, using the + // specified paint. The upper left corner of the bitmap is rendered at the + // specified location. + void DrawBitmapInt(const SkBitmap& bitmap, int x, int y, + const SkPaint& paint); + + // Draws a portion of a bitmap in the specified location. The src parameters + // correspond to the region of the bitmap to draw in the region defined + // by the dest coordinates. + // + // If the width or height of the source differs from that of the destination, + // the bitmap will be scaled. When scaling down, it is highly recommended + // that you call buildMipMap(false) on your bitmap to ensure that it has + // a mipmap, which will result in much higher-quality output. Set |filter| + // to use filtering for bitmaps, otherwise the nearest-neighbor algorithm + // is used for resampling. + // + // An optional custom SkPaint can be provided. + void DrawBitmapInt(const SkBitmap& bitmap, int src_x, int src_y, int src_w, + int src_h, int dest_x, int dest_y, int dest_w, int dest_h, + bool filter); + void DrawBitmapInt(const SkBitmap& bitmap, int src_x, int src_y, int src_w, + int src_h, int dest_x, int dest_y, int dest_w, int dest_h, + bool filter, const SkPaint& paint); + + // Draws text with the specified color, font and location. The text is + // aligned to the left, vertically centered, clipped to the region. If the + // text is too big, it is truncated and '...' is added to the end. + void DrawStringInt(const std::wstring& text, const ChromeFont& font, + const SkColor& color, int x, int y, int w, int h) { + DrawStringInt(text, font, color, x, y, w, h, + l10n_util::DefaultCanvasTextAlignment()); + } + + // Draws text with the specified color, font and location. The last argument + // specifies flags for how the text should be rendered. It can be one of + // TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT or TEXT_ALIGN_LEFT. + void DrawStringInt(const std::wstring& text, const ChromeFont& font, + const SkColor& color, int x, int y, int w, int h, + int flags); + + // Draws a dotted gray rectangle used for focus purposes. + void DrawFocusRect(int x, int y, int width, int height); + + // Compute the size required to draw some text with the provided font. + // Attempts to fit the text with the provided width and height. Increases + // height and then width as needed to make the text fit. This method + // supports multiple lines. + void SizeStringInt(const std::wstring& test, const ChromeFont& font, + int *width, int* height, int flags); + + // Tiles the image in the specified region. + void TileImageInt(const SkBitmap& bitmap, int x, int y, int w, int h, + SkPorterDuff::Mode mode); + + // Tiles the image in the specified region using a transfer mode of + // SkPorterDuff::kSrcOver_Mode. + void TileImageInt(const SkBitmap& bitmap, int x, int y, int w, int h); + + // Extracts a bitmap from the contents of this canvas. + SkBitmap ExtractBitmap(); + + private: + // Draws text with the specified color, font and location. The text is + // aligned to the left, vertically centered, clipped to the region. If the + // text is too big, it is truncated and '...' is added to the end. + void DrawStringInt(const std::wstring& text, HFONT font, + const SkColor& color, int x, int y, int w, int h, + int flags); + + // Compute the windows flags necessary to implement the provided text + // ChromeCanvas flags + int ChromeCanvas::ComputeFormatFlags(int flags); + + // A wrapper around Windows' DrawText. This function takes care of adding + // Unicode directionality marks to the text in certain cases. + void DoDrawText(HDC hdc, const std::wstring& text, RECT* text_bounds, + int flags); + + DISALLOW_EVIL_CONSTRUCTORS(ChromeCanvas); +}; + +typedef gfx::CanvasPaintT<ChromeCanvas> ChromeCanvasPaint; + +#endif // CHROME_COMMON_GFX_CHROME_CANVAS_H__ diff --git a/chrome/common/gfx/chrome_font.cc b/chrome/common/gfx/chrome_font.cc new file mode 100644 index 0000000..198227e --- /dev/null +++ b/chrome/common/gfx/chrome_font.cc @@ -0,0 +1,188 @@ +// 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 "chrome/common/gfx/chrome_font.h" + +#include <algorithm> +#include <math.h> + +#include "base/logging.h" +#include "base/win_util.h" +#include "chrome/app/locales/locale_settings.h" +#include "chrome/common/l10n_util.h" + +#include "generated_resources.h" + +/*static*/ +ChromeFont::HFontRef* ChromeFont::base_font_ref_; + +// If the tmWeight field of a TEXTMETRIC structure has a value >= this, the +// font is bold. +static const int kTextMetricWeightBold = 700; + +// +// ChromeFont +// + +// static +ChromeFont ChromeFont::CreateFont(HFONT font) { + DCHECK(font); + LOGFONT font_info; + GetObject(font, sizeof(LOGFONT), &font_info); + return ChromeFont(CreateHFontRef(CreateFontIndirect(&font_info))); +} + +ChromeFont ChromeFont::CreateFont(const std::wstring& font_name, + int font_size) { + HDC hdc = GetDC(NULL); + long lf_height = -MulDiv(font_size, GetDeviceCaps(hdc, LOGPIXELSY), 72); + ReleaseDC(NULL, hdc); + HFONT hf = ::CreateFont(lf_height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + font_name.c_str()); + return ChromeFont::CreateFont(hf); +} + +// static +ChromeFont::HFontRef* ChromeFont::GetBaseFontRef() { + if (base_font_ref_ == NULL) { + NONCLIENTMETRICS metrics; + win_util::GetNonClientMetrics(&metrics); + + // See comment in ChromeFont::DeriveFont() about font size. + DCHECK_GE(abs(metrics.lfMessageFont.lfHeight), 5); + HFONT font = CreateFontIndirect(&metrics.lfMessageFont); + DLOG_ASSERT(font); + base_font_ref_ = ChromeFont::CreateHFontRef(font); + // base_font_ref_ is global, up the ref count so it's never deleted. + base_font_ref_->AddRef(); + } + return base_font_ref_; +} + +std::wstring ChromeFont::FontName() { + LOGFONT font_info; + GetObject(hfont(), sizeof(LOGFONT), &font_info); + return (std::wstring(font_info.lfFaceName)); +} + +int ChromeFont::FontSize() { + LOGFONT font_info; + GetObject(hfont(), sizeof(LOGFONT), &font_info); + long lf_height = font_info.lfHeight; + HDC hdc = GetDC(NULL); + int device_caps = GetDeviceCaps(hdc, LOGPIXELSY); + int font_size = 0; + if (device_caps != 0) { + float font_size_float = -static_cast<float>(lf_height)*72/device_caps; + font_size = static_cast<int>(::ceil(font_size_float - 0.5)); + } + ReleaseDC(NULL, hdc); + return font_size; +} + +ChromeFont ChromeFont::DeriveFont(int size_delta, int style) const { + LOGFONT font_info; + GetObject(hfont(), sizeof(LOGFONT), &font_info); + // LOGFONT returns two types of font heights, negative is measured slightly + // differently (character height, vs cell height). + if (font_info.lfHeight < 0) { + font_info.lfHeight -= size_delta; + } else { + font_info.lfHeight += size_delta; + } + // Even with "Small Fonts", the smallest readable font size is 5. It is easy + // to create a non-drawing font and forget about the fact that text should be + // drawn in the UI. This test ensures that the font will be readable. + DCHECK_GE(abs(font_info.lfHeight), 5); + font_info.lfUnderline = ((style & UNDERLINED) == UNDERLINED); + font_info.lfItalic = ((style & ITALIC) == ITALIC); + font_info.lfWeight = (style & BOLD) ? FW_BOLD : FW_NORMAL; + + if (style & WEB) { + font_info.lfPitchAndFamily = FF_SWISS; + wcscpy_s(font_info.lfFaceName, + l10n_util::GetString(IDS_WEB_FONT_FAMILY).c_str()); + } + + HFONT hfont = CreateFontIndirect(&font_info); + return ChromeFont(CreateHFontRef(hfont)); +} + +int ChromeFont::GetStringWidth(const std::wstring& text) const { + int width = 0; + HDC dc = GetDC(NULL); + HFONT previous_font = static_cast<HFONT>(SelectObject(dc, hfont())); + SIZE size; + if (GetTextExtentPoint32(dc, text.c_str(), static_cast<int>(text.size()), + &size)) { + width = size.cx; + } else { + width = 0; + } + SelectObject(dc, previous_font); + ReleaseDC(NULL, dc); + return width; +} + +ChromeFont::HFontRef* ChromeFont::CreateHFontRef(HFONT font) { + TEXTMETRIC font_metrics; + HDC screen_dc = GetDC(NULL); + HFONT previous_font = static_cast<HFONT>(SelectObject(screen_dc, font)); + int last_map_mode = SetMapMode(screen_dc, MM_TEXT); + GetTextMetrics(screen_dc, &font_metrics); + // Yes, this is how Microsoft recommends calculating the dialog unit + // conversions. + SIZE ave_text_size; + GetTextExtentPoint32(screen_dc, + L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + 52, &ave_text_size); + const int dlu_base_x = (ave_text_size.cx / 26 + 1) / 2; + // To avoid the DC referencing font_handle_, select the previous font. + SelectObject(screen_dc, previous_font); + SetMapMode(screen_dc, last_map_mode); + ReleaseDC(NULL, screen_dc); + + const int height = std::max(1, static_cast<int>(font_metrics.tmHeight)); + const int baseline = std::max(1, static_cast<int>(font_metrics.tmAscent)); + const int ave_char_width = + std::max(1, static_cast<int>(font_metrics.tmAveCharWidth)); + int style = 0; + if (font_metrics.tmItalic) { + style |= ChromeFont::ITALIC; + } + if (font_metrics.tmUnderlined) { + style |= ChromeFont::UNDERLINED; + } + if (font_metrics.tmWeight >= kTextMetricWeightBold) { + style |= ChromeFont::BOLD; + } + + return new HFontRef(font, height, baseline, ave_char_width, style, + dlu_base_x); +} + diff --git a/chrome/common/gfx/chrome_font.h b/chrome/common/gfx/chrome_font.h new file mode 100644 index 0000000..9a4caa5 --- /dev/null +++ b/chrome/common/gfx/chrome_font.h @@ -0,0 +1,181 @@ +// 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 CHROME_COMMON_GFX_CHROME_FONT_H__ +#define CHROME_COMMON_GFX_CHROME_FONT_H__ + +#include <windows.h> +#include <string> + +#include "base/basictypes.h" +#include "base/ref_counted.h" + +// ChromeFont provides a wrapper around an underlying font. Copy and assignment +// operators are explicitly allowed, and cheap. +class ChromeFont { + public: + // The following constants indicate the font style. + static const int BOLD = 1; + static const int ITALIC = 2; + static const int UNDERLINED = 4; + static const int WEB = 8; + + // Creates a ChromeFont from the specified HFONT. The supplied HFONT is + // effectively copied. + static ChromeFont CreateFont(HFONT hfont); + + // Creates a ChromeFont given font name (e.g. arial), font size (e.g. 12). + static ChromeFont CreateFont(const std::wstring& font_name, int font_size); + + // Creates a font with the default name and style. + ChromeFont() : font_ref_(GetBaseFontRef()) { } + ~ChromeFont() { } + + // Returns a new Font derived from the existing font. + // size_deta is the size to add to the current font. For example, a value + // of 5 results in a font 5 units bigger than this font. + ChromeFont DeriveFont(int size_delta) const { + return DeriveFont(size_delta, style()); + } + + // Returns a new Font derived from the existing font. + // size_deta is the size to add to the current font. See the single + // argument version of this method for an example. + // The style parameter specifies the new style for the font, and is a + // bitmask of the values: BOLD, ITALIC, UNDERLINED and WEB. + ChromeFont DeriveFont(int size_delta, int style) const; + + // Returns the number of vertical pixels needed to display characters from + // the specified font. + int height() const { return font_ref_->height(); } + + // Returns the baseline, or ascent, of the font. + int baseline() const { return font_ref_->baseline(); } + + // Returns the average character width for the font. + int ave_char_width() const { return font_ref_->ave_char_width(); } + + // Returns the number of horizontal pixels needed to display the specified + // string. + int GetStringWidth(const std::wstring& text) const; + + // Returns the style of the font. + int style() const { return font_ref_->style(); } + + // Returns the handle to the underlying HFONT. This is used by ChromeCanvas to + // draw text. + HFONT hfont() const { return font_ref_->hfont(); } + + // Dialog units to pixels conversion. + // See http://support.microsoft.com/kb/145994 for details. + int horizontal_dlus_to_pixels(int dlus) { + return dlus * font_ref_->dlu_base_x() / 4; + } + int vertical_dlus_to_pixels(int dlus) { + return dlus * font_ref_->height() / 8; + } + + // Font Name. + std::wstring FontName(); + + // Font Size. + int FontSize(); + + private: + // Chrome text drawing bottoms out in the Windows GDI functions that take an + // HFONT (an opaque handle into Windows). To avoid lots of GDI object + // allocation and destruction, ChromeFont indirectly refers to the HFONT + // by way of an HFontRef. That is, every ChromeFront has an HFontRef, which + // has an HFONT. + // + // HFontRef is reference counted. Upon deletion, it deletes the HFONT. + // By making HFontRef maintain the reference to the HFONT, multiple + // HFontRefs can share the same HFONT, and ChromeFont can provide value + // semantics. + class HFontRef : public base::RefCounted<HFontRef> { + public: + // This constructor takes control of the HFONT, and will delete it when + // the HFontRef is deleted. + HFontRef(HFONT hfont, + int height, + int baseline, + int ave_char_width, + int style, + int dlu_base_x) + : hfont_(hfont), + height_(height), + baseline_(baseline), + ave_char_width_(ave_char_width), + style_(style), + dlu_base_x_(dlu_base_x) { + DLOG_ASSERT(hfont); + } + + ~HFontRef() { + DeleteObject(hfont_); + } + + // Accessors + HFONT hfont() const { return hfont_; } + int height() const { return height_; } + int baseline() const { return baseline_; } + int ave_char_width() const { return ave_char_width_; } + int style() const { return style_; } + int dlu_base_x() const { return dlu_base_x_; } + + private: + const HFONT hfont_; + const int height_; + const int baseline_; + const int ave_char_width_; + const int style_; + // Constants used in converting dialog units to pixels. + const int dlu_base_x_; + + DISALLOW_EVIL_CONSTRUCTORS(HFontRef); + }; + + + // Returns the base font ref. This should ONLY be invoked on the + // UI thread. + static HFontRef* GetBaseFontRef(); + + // Creates and returns a new HFONTRef from the specified HFONT. + static HFontRef* CreateHFontRef(HFONT font); + + explicit ChromeFont(HFontRef* font_ref) : font_ref_(font_ref) { } + + // Reference to the base font all fonts are derived from. + static HFontRef* base_font_ref_; + + // Indirect reference to the HFontRef, which references the underlying HFONT. + scoped_refptr<HFontRef> font_ref_; +}; + +#endif // CHROME_COMMON_GFX_CHROME_FONT_H__ diff --git a/chrome/common/gfx/color_utils.cc b/chrome/common/gfx/color_utils.cc new file mode 100644 index 0000000..cff264b --- /dev/null +++ b/chrome/common/gfx/color_utils.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. + + +#include <math.h> +#include <windows.h> + +#include "chrome/common/gfx/color_utils.h" + +#include "base/basictypes.h" +#include "base/gfx/skia_utils.h" +#include "base/logging.h" +#include "skia/include/SkBitmap.h" + +namespace color_utils { + +// These transformations are based on the equations in: +// http://en.wikipedia.org/wiki/Lab_color +// http://en.wikipedia.org/wiki/SRGB_color_space#Specification_of_the_transformation +// See also: +// http://www.brucelindbloom.com/index.html?ColorCalculator.html + +static const double kCIEConversionAlpha = 0.055; +static const double kCIEConversionGamma = 2.2; +static const double kE = 0.008856; +static const double kK = 903.3; + +static double CIEConvertNonLinear(uint8 color_component) { + double color_component_d = static_cast<double>(color_component) / 255.0; + if (color_component_d > 0.04045) { + double base = (color_component_d + kCIEConversionAlpha) / (1 + kCIEConversionAlpha); + return pow(base, kCIEConversionGamma); + } else { + return color_component_d / 12.92; + } +} + +// Note: this works only for sRGB. +void SkColorToCIEXYZ(SkColor c, CIE_XYZ* xyz) { + uint8 r = SkColorGetR(c); + uint8 g = SkColorGetG(c); + uint8 b = SkColorGetB(c); + + xyz->X = + 0.4124 * CIEConvertNonLinear(r) + + 0.3576 * CIEConvertNonLinear(g) + + 0.1805 * CIEConvertNonLinear(b); + xyz->Y = + 0.2126 * CIEConvertNonLinear(r) + + 0.7152 * CIEConvertNonLinear(g) + + 0.0722 * CIEConvertNonLinear(g); + xyz->Z = + 0.0193 * CIEConvertNonLinear(r) + + 0.1192 * CIEConvertNonLinear(g) + + 0.9505 * CIEConvertNonLinear(b); +} + +static double LabConvertNonLinear(double value) { + if (value > 0.008856) { + double goat = pow(value, static_cast<double>(1) / 3); + return goat; + } + return (kK * value + 16) / 116; +} + +void CIEXYZToLabColor(const CIE_XYZ& xyz, LabColor* lab) { + CIE_XYZ white_xyz; + SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); + double fx = LabConvertNonLinear(xyz.X / white_xyz.X); + double fy = LabConvertNonLinear(xyz.Y / white_xyz.Y); + double fz = LabConvertNonLinear(xyz.Z / white_xyz.Z); + lab->L = static_cast<int>(116 * fy) - 16; + lab->a = static_cast<int>(500 * (fx - fy)); + lab->b = static_cast<int>(200 * (fy - fz)); +} + +static uint8 sRGBColorComponentFromLinearComponent(double component) { + double result; + if (component <= 0.0031308) { + result = 12.92 * component; + } else { + result = (1 + kCIEConversionAlpha) * pow(component, (static_cast<double>(1) / 2.4)) - kCIEConversionAlpha; + } + return std::min(static_cast<uint8>(255), static_cast<uint8>(result * 255)); +} + +SkColor CIEXYZToSkColor(SkAlpha alpha, const CIE_XYZ& xyz) { + double r_linear = 3.2410 * xyz.X - 1.5374 * xyz.Y - 0.4986 * xyz.Z; + double g_linear = -0.9692 * xyz.X + 1.8760 * xyz.Y + 0.0416 * xyz.Z; + double b_linear = 0.0556 * xyz.X - 0.2040 * xyz.Y + 1.0570 * xyz.Z; + uint8 r = sRGBColorComponentFromLinearComponent(r_linear); + uint8 g = sRGBColorComponentFromLinearComponent(g_linear); + uint8 b = sRGBColorComponentFromLinearComponent(b_linear); + return SkColorSetARGB(alpha, r, g, b); +} + +static double gen_yr(const LabColor& lab) { + if (lab.L > (kE * kK)) + return pow((lab.L + 16.0) / 116, 3.0); + return static_cast<double>(lab.L) / kK; +} + +static double fy(const LabColor& lab) { + double yr = gen_yr(lab); + if (yr > kE) + return (lab.L + 16.0) / 116; + return (kK * yr + 16.0) / 116; +} + +static double fx(const LabColor& lab) { + return (static_cast<double>(lab.a) / 500) + fy(lab); +} + +static double gen_xr(const LabColor& lab) { + double x = fx(lab); + double x_cubed = pow(x, 3.0); + if (x_cubed > kE) + return x_cubed; + return (116.0 * x - 16.0) / kK; +} + +static double fz(const LabColor& lab) { + return fy(lab) - (static_cast<double>(lab.b) / 200); +} + +static double gen_zr(const LabColor& lab) { + double z = fz(lab); + double z_cubed = pow(z, 3.0); + if (z_cubed > kE) + return z_cubed; + return (116.0 * z - 16.0) / kK; +} + +void LabColorToCIEXYZ(const LabColor& lab, CIE_XYZ* xyz) { + CIE_XYZ result; + + CIE_XYZ white_xyz; + SkColorToCIEXYZ(SkColorSetRGB(255, 255, 255), &white_xyz); + + result.X = gen_xr(lab) * white_xyz.X; + result.Y = gen_yr(lab) * white_xyz.Y; + result.Z = gen_zr(lab) * white_xyz.Z; + + *xyz = result; +} + +void SkColorToLabColor(SkColor c, LabColor* lab) { + CIE_XYZ xyz; + SkColorToCIEXYZ(c, &xyz); + CIEXYZToLabColor(xyz, lab); +} + +SkColor LabColorToSkColor(const LabColor& lab, SkAlpha alpha) { + CIE_XYZ xyz; + LabColorToCIEXYZ(lab, &xyz); + return CIEXYZToSkColor(alpha, xyz); +} + +static const int kCloseToBoundary = 64; +static const int kAverageBoundary = 15; + +bool IsColorCloseToTransparent(SkAlpha alpha) { + return alpha < kCloseToBoundary; +} + +bool IsColorCloseToGrey(int r, int g, int b) { + int average = (r + g + b) / 3; + return (abs(r - average) < kAverageBoundary) && + (abs(g - average) < kAverageBoundary) && + (abs(b - average) < kAverageBoundary); +} + +SkColor GetAverageColorOfFavicon(SkBitmap* favicon, SkAlpha alpha) { + int r = 0, g = 0, b = 0; + + SkAutoLockPixels favicon_lock(*favicon); + SkColor* pixels = static_cast<SkColor*>(favicon->getPixels()); + // Assume ARGB_8888 format. + DCHECK(favicon->getConfig() == SkBitmap::kARGB_8888_Config); + SkColor* current_color = pixels; + + DCHECK(favicon->width() <= 16 && favicon->height() <= 16); + + int pixel_count = favicon->width() * favicon->height(); + int color_count = 0; + for (int i = 0; i < pixel_count; ++i, ++current_color) { + // Disregard this color if it is close to black, close to white, or close + // to transparent since any of those pixels do not contribute much to the + // color makeup of this icon. + int cr = SkColorGetR(*current_color); + int cg = SkColorGetG(*current_color); + int cb = SkColorGetB(*current_color); + + if (IsColorCloseToTransparent(SkColorGetA(*current_color)) || + IsColorCloseToGrey(cr, cg, cb)) + continue; + + r += cr; + g += cg; + b += cb; + ++color_count; + } + + SkColor result; + if (color_count > 0) { + result = SkColorSetARGB(alpha, + r / color_count, + g / color_count, + b / color_count); + } else { + result = SkColorSetARGB(alpha, 0, 0, 0); + } + return result; +} + +inline int GetLumaForColor(SkColor* color) { + int r = SkColorGetR(*color); + int g = SkColorGetG(*color); + int b = SkColorGetB(*color); + + int luma = static_cast<int>(0.3*r + 0.59*g + 0.11*b); + if (luma < 0) + luma = 0; + else if (luma > 255) + luma = 255; + + return luma; +} + +void BuildLumaHistogram(SkBitmap* bitmap, int histogram[256]) { + SkAutoLockPixels bitmap_lock(*bitmap); + // Assume ARGB_8888 format. + DCHECK(bitmap->getConfig() == SkBitmap::kARGB_8888_Config); + + int pixel_width = bitmap->width(); + int pixel_height = bitmap->height(); + for (int y = 0; y < pixel_height; ++y) { + SkColor* current_color = static_cast<uint32_t*>(bitmap->getAddr32(0, y)); + for (int x = 0; x < pixel_width; ++x, ++current_color) { + histogram[GetLumaForColor(current_color)]++; + } + } +} + +SkColor SetColorAlpha(SkColor c, SkAlpha alpha) { + return SkColorSetARGB(alpha, SkColorGetR(c), SkColorGetG(c), SkColorGetB(c)); +} + +SkColor GetSysSkColor(int which) { + return gfx::COLORREFToSkColor(::GetSysColor(which)); +} + +} // namespace color_utils diff --git a/chrome/common/gfx/color_utils.h b/chrome/common/gfx/color_utils.h new file mode 100644 index 0000000..48f1a9e --- /dev/null +++ b/chrome/common/gfx/color_utils.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 CHROME_COMMON_GFX_COLOR_UTILS_H__ +#define CHROME_COMMON_GFX_COLOR_UTILS_H__ + +#include "SkColor.h" + +class SkBitmap; + +namespace color_utils { + +// Represents set of CIE XYZ tristimulus values. +struct CIE_XYZ { + double X; + double Y; // luminance + double Z; +}; + +// Represents a L*a*b* color value +struct LabColor { + int L; + int a; + int b; +}; + +// Note: these transformations assume sRGB as the source color space + +// Convert between different color spaces +void SkColorToCIEXYZ(SkColor c, CIE_XYZ* xyz); +void CIEXYZToLabColor(const CIE_XYZ& xyz, LabColor* lab); + +SkColor CIEXYZToSkColor(SkAlpha alpha, const CIE_XYZ& xyz); +void LabColorToCIEXYZ(const LabColor& lab, CIE_XYZ* xyz); + +void SkColorToLabColor(SkColor c, LabColor* lab); +SkColor LabColorToSkColor(const LabColor& lab, SkAlpha alpha); + +// Determine if a given alpha value is nearly completely transparent. +bool IsColorCloseToTransparent(SkAlpha alpha); + +// Determine if a color is near grey. +bool IsColorCloseToGrey(int r, int g, int b); + +// Gets a color representing a bitmap. The definition of "representing" is the +// average color in the bitmap. The color returned is modified to have the +// specified alpha. +SkColor GetAverageColorOfFavicon(SkBitmap* bitmap, SkAlpha alpha); + +// Builds a histogram based on the Y' of the Y'UV representation of +// this image. +void BuildLumaHistogram(SkBitmap* bitmap, int histogram[256]); + +// Create a color from a base color and a specific alpha value. +SkColor SetColorAlpha(SkColor c, SkAlpha alpha); + +// Gets a Windows system color as a SkColor +SkColor GetSysSkColor(int which); + +} // namespace color_utils + +#endif // #ifndef CHROME_COMMON_GFX_COLOR_UTILS_H__ diff --git a/chrome/common/gfx/emf.cc b/chrome/common/gfx/emf.cc new file mode 100644 index 0000000..3fa358a --- /dev/null +++ b/chrome/common/gfx/emf.cc @@ -0,0 +1,342 @@ +// 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 "chrome/common/gfx/emf.h" + +#include "base/gfx/rect.h" +#include "base/logging.h" + +namespace gfx { + +Emf::Emf() : emf_(NULL), hdc_(NULL) { +} + +Emf::~Emf() { + CloseEmf(); + DCHECK(!emf_ && !hdc_); +} + +bool Emf::CreateDc(HDC sibling, const RECT* rect) { + DCHECK(!emf_ && !hdc_); + hdc_ = CreateEnhMetaFile(sibling, NULL, rect, NULL); + DCHECK(hdc_); + return hdc_ != NULL; +} + +bool Emf::CreateFromData(const void* buffer, size_t size) { + DCHECK(!emf_ && !hdc_); + emf_ = SetEnhMetaFileBits(static_cast<unsigned>(size), + reinterpret_cast<const BYTE*>(buffer)); + DCHECK(emf_); + return emf_ != NULL; +} + +bool Emf::CloseDc() { + DCHECK(!emf_ && hdc_); + emf_ = CloseEnhMetaFile(hdc_); + DCHECK(emf_); + hdc_ = NULL; + return emf_ != NULL; +} + +void Emf::CloseEmf() { + DCHECK(!hdc_); + if (emf_) { + DeleteEnhMetaFile(emf_); + emf_ = NULL; + } +} + +bool Emf::Playback(HDC hdc, const RECT* rect) const { + DCHECK(emf_ && !hdc_); + RECT bounds; + if (!rect) { + // Get the natural bounds of the EMF buffer. + bounds = GetBounds().ToRECT(); + rect = &bounds; + } + return PlayEnhMetaFile(hdc, emf_, rect) != 0; +} + +bool Emf::SafePlayback(HDC context) const { + DCHECK(emf_ && !hdc_); + XFORM base_matrix; + if (!GetWorldTransform(context, &base_matrix)) { + NOTREACHED(); + return false; + } + + return EnumEnhMetaFile(context, + emf_, + &Emf::SafePlaybackProc, + reinterpret_cast<void*>(&base_matrix), + &GetBounds().ToRECT()) != 0; +} + +gfx::Rect Emf::GetBounds() const { + DCHECK(emf_ && !hdc_); + ENHMETAHEADER header; + if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) { + NOTREACHED(); + return gfx::Rect(); + } + if (header.rclBounds.left == 0 && + header.rclBounds.top == 0 && + header.rclBounds.right == -1 && + header.rclBounds.bottom == -1) { + // A freshly created EMF buffer that has no drawing operation has invalid + // bounds. Instead of having an (0,0) size, it has a (-1,-1) size. Detect + // this special case and returns an empty Rect instead of an invalid one. + return gfx::Rect(); + } + return gfx::Rect(header.rclBounds.left, + header.rclBounds.top, + header.rclBounds.right - header.rclBounds.left, + header.rclBounds.bottom - header.rclBounds.top); +} + +unsigned Emf::GetDataSize() const { + DCHECK(emf_ && !hdc_); + return GetEnhMetaFileBits(emf_, 0, NULL); +} + +bool Emf::GetData(void* buffer, size_t size) const { + DCHECK(emf_ && !hdc_); + DCHECK(buffer && size); + unsigned size2 = GetEnhMetaFileBits(emf_, static_cast<unsigned>(size), + reinterpret_cast<BYTE*>(buffer)); + DCHECK(size2 == size); + return size2 == size && size2 != 0; +} + +bool Emf::GetData(std::vector<uint8>* buffer) const { + unsigned size = GetDataSize(); + if (!size) + return false; + + buffer->resize(size); + if (!GetData(&buffer->front(), size)) + return false; + return true; +} + +bool Emf::SaveTo(const std::wstring& filename) const { + HANDLE file = CreateFile(filename.c_str(), GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + CREATE_ALWAYS, 0, NULL); + if (file == INVALID_HANDLE_VALUE) + return false; + + bool success = false; + std::vector<uint8> buffer; + if (GetData(&buffer)) { + DWORD written = 0; + if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()), + &written, NULL) && + written == buffer.size()) { + success = true; + } + } + CloseHandle(file); + return success; +} + +int CALLBACK Emf::SafePlaybackProc(HDC hdc, + HANDLETABLE* handle_table, + const ENHMETARECORD* record, + int objects_count, + LPARAM param) { + const XFORM* base_matrix = reinterpret_cast<const XFORM*>(param); + EnumerationContext context; + context.handle_table = handle_table; + context.objects_count = objects_count; + context.hdc = hdc; + Record record_instance(&context, record); + bool success = record_instance.SafePlayback(base_matrix); + DCHECK(success); + return 1; +} + +Emf::Record::Record() { +} + +Emf::Record::Record(const EnumerationContext* context, + const ENHMETARECORD* record) + : record_(record), + context_(context) { + DCHECK(record_); +} + +bool Emf::Record::Play() const { + return 0 != PlayEnhMetaFileRecord(context_->hdc, + context_->handle_table, + record_, + context_->objects_count); +} + +bool Emf::Record::SafePlayback(const XFORM* base_matrix) const { + // For EMF field description, see [MS-EMF] Enhanced Metafile Format + // Specification. + // + // This is the second major EMF breakage I get; the first one being + // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored. + // + // This function is the guts of the fix for bug 1186598. Some printer drivers + // somehow choke on certain EMF records, but calling the corresponding + // function directly on the printer HDC is fine. Still, playing the EMF record + // fails. Go figure. + // + // The main issue is that SetLayout is totally unsupported on these printers + // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is + // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!) + // Damn. + // + // So I resorted to manually parse the EMF records and play them one by one. + // The issue with this method compared to using PlayEnhMetaFile to play back + // an EMF buffer is that the later silently fixes the matrix to take in + // account the matrix currently loaded at the time of the call. + // The matrix magic is done transparently when using PlayEnhMetaFile but since + // I'm processing one field at a time, I need to do the fixup myself. Note + // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when + // called inside an EnumEnhMetaFile loop. Go figure (bis). + // + // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need + // to fix the matrix according to the matrix previously loaded before playing + // back the buffer. Otherwise, the previously loaded matrix would be ignored + // and the EMF buffer would always be played back at its native resolution. + // Duh. + // + // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that + // could remain. + // + // Note: I should probably care about view ports and clipping, eventually. + bool res; + switch (record()->iType) { + case EMR_SETWORLDTRANSFORM: { + DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM)); + const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm); + HDC hdc = context_->hdc; + if (base_matrix) { + res = 0 != SetWorldTransform(hdc, base_matrix) && + ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY); + } else { + res = 0 != SetWorldTransform(hdc, xform); + } + break; + } + case EMR_MODIFYWORLDTRANSFORM: { + DCHECK_EQ(record()->nSize, + sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD)); + const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm); + const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1); + HDC hdc = context_->hdc; + switch (*option) { + case MWT_IDENTITY: + if (base_matrix) { + res = 0 != SetWorldTransform(hdc, base_matrix); + } else { + res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY); + } + break; + case MWT_LEFTMULTIPLY: + case MWT_RIGHTMULTIPLY: + res = 0 != ModifyWorldTransform(hdc, xform, *option); + break; + case 4: // MWT_SET + if (base_matrix) { + res = 0 != SetWorldTransform(hdc, base_matrix) && + ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY); + } else { + res = 0 != SetWorldTransform(hdc, xform); + } + break; + default: + res = false; + break; + } + break; + } + case EMR_SETLAYOUT: + // Ignore it. + res = true; + break; + default: { + res = Play(); + break; + } + } + return res; +} + +Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) { + context_.handle_table = NULL; + context_.objects_count = 0; + context_.hdc = NULL; + items_.clear(); + if (!EnumEnhMetaFile(context, + emf.emf(), + &Emf::Enumerator::EnhMetaFileProc, + reinterpret_cast<void*>(this), + rect)) { + NOTREACHED(); + items_.clear(); + } + DCHECK_EQ(context_.hdc, context); +} + +Emf::Enumerator::const_iterator Emf::Enumerator::begin() const { + return items_.begin(); +} + +Emf::Enumerator::const_iterator Emf::Enumerator::end() const { + return items_.end(); +} + +int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc, + HANDLETABLE* handle_table, + const ENHMETARECORD* record, + int objects_count, + LPARAM param) { + Enumerator& emf = *reinterpret_cast<Enumerator*>(param); + if (!emf.context_.handle_table) { + DCHECK(!emf.context_.handle_table); + DCHECK(!emf.context_.objects_count); + emf.context_.handle_table = handle_table; + emf.context_.objects_count = objects_count; + emf.context_.hdc = hdc; + } else { + DCHECK_EQ(emf.context_.handle_table, handle_table); + DCHECK_EQ(emf.context_.objects_count, objects_count); + DCHECK_EQ(emf.context_.hdc, hdc); + } + emf.items_.push_back(Record(&emf.context_, record)); + return 1; +} + +} // namespace gfx diff --git a/chrome/common/gfx/emf.h b/chrome/common/gfx/emf.h new file mode 100644 index 0000000..0dd90f8 --- /dev/null +++ b/chrome/common/gfx/emf.h @@ -0,0 +1,204 @@ +// 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 CHROME_COMMON_GFX_EMF_H__ +#define CHROME_COMMON_GFX_EMF_H__ + +#include <windows.h> +#include <vector> + +#include "base/basictypes.h" + +namespace gfx { + +class Rect; + +// Simple wrapper class that manage an EMF data stream and its virtual HDC. +class Emf { + public: + class Record; + class Enumerator; + struct EnumerationContext; + + Emf(); + ~Emf(); + + // Generates a virtual HDC that will record every GDI commands and compile it + // in a EMF data stream. + // hdc is used to setup the default DPI and color settings. hdc is optional. + // rect specifies the dimensions (in .01-millimeter units) of the EMF. rect is + // optional. + bool CreateDc(HDC sibling, const RECT* rect); + + // Load a EMF data stream. buffer contains EMF data. + bool CreateFromData(const void* buffer, size_t size); + + // TODO(maruel): CreateFromFile(). If ever used. Maybe users would like to + // have the ability to save web pages to an EMF file? Afterward, it is easy to + // convert to PDF or PS. + + // Closes the HDC created by CreateDc() and generates the compiled EMF + // data. + bool CloseDc(); + + // Closes the EMF data handle when it is not needed anymore. + void CloseEmf(); + + // "Plays" the EMF buffer in a HDC. It is the same effect as calling the + // original GDI function that were called when recording the EMF. |rect| is in + // "logical units" and is optional. If |rect| is NULL, the natural EMF bounds + // are used. + // Note: Windows has been known to have stack buffer overflow in its GDI + // functions, whether used directly or indirectly through precompiled EMF + // data. We have to accept the risk here. Since it is used only for printing, + // it requires user intervention. + bool Playback(HDC hdc, const RECT* rect) const; + + // The slow version of Playback(). It enumerates all the records and play them + // back in the HDC. The trick is that it skip over the records known to have + // issue with some printers. See Emf::Record::SafePlayback implementation for + // details. + bool SafePlayback(HDC hdc) const; + + // Retrieves the bounds of the painted area by this EMF buffer. This value + // should be passed to Playback to keep the exact same size. + gfx::Rect GetBounds() const; + + // Retrieves the EMF stream size. + unsigned GetDataSize() const; + + // Retrieves the EMF stream. + bool GetData(void* buffer, size_t size) const; + + // Retrieves the EMF stream. It is an helper function. + bool GetData(std::vector<uint8>* buffer) const; + + HENHMETAFILE emf() const { + return emf_; + } + + HDC hdc() const { + return hdc_; + } + + // Saves the EMF data to a file as-is. It is recommended to use the .emf file + // extension but it is not enforced. This function synchronously writes to the + // file. For testing only. + bool SaveTo(const std::wstring& filename) const; + + private: + // Playbacks safely one EMF record. + static int CALLBACK SafePlaybackProc(HDC hdc, + HANDLETABLE* handle_table, + const ENHMETARECORD* record, + int objects_count, + LPARAM param); + + // Compiled EMF data handle. + HENHMETAFILE emf_; + + // Valid when generating EMF data through a virtual HDC. + HDC hdc_; + + DISALLOW_EVIL_CONSTRUCTORS(Emf); +}; + +struct Emf::EnumerationContext { + HANDLETABLE* handle_table; + int objects_count; + HDC hdc; +}; + +// One EMF record. It keeps pointers to the EMF buffer held by Emf::emf_. +// The entries become invalid once Emf::CloseEmf() is called. +class Emf::Record { + public: + Record(); + + // Plays the record. + bool Play() const; + + // Plays the record working around quirks with SetLayout, + // SetWorldTransform and ModifyWorldTransform. See implementation for details. + bool SafePlayback(const XFORM* base_matrix) const; + + // Access the underlying EMF record. + const ENHMETARECORD* record() const { return record_; } + + protected: + Record(const EnumerationContext* context, + const ENHMETARECORD* record); + + private: + friend class Emf; + friend class Enumerator; + const ENHMETARECORD* record_; + const EnumerationContext* context_; +}; + +// Retrieves individual records out of a Emf buffer. The main use is to skip +// over records that are unsupported on a specific printer or to play back +// only a part of an EMF buffer. +class Emf::Enumerator { + public: + // Iterator type used for iterating the records. + typedef std::vector<Record>::const_iterator const_iterator; + + // Enumerates the records at construction time. |hdc| and |rect| are + // both optional at the same time or must both be valid. + // Warning: |emf| must be kept valid for the time this object is alive. + Enumerator(const Emf& emf, HDC hdc, const RECT* rect); + + // Retrieves the first Record. + const_iterator begin() const; + + // Retrieves the end of the array. + const_iterator end() const; + + private: + // Processes one EMF record and saves it in the items_ array. + static int CALLBACK EnhMetaFileProc(HDC hdc, + HANDLETABLE* handle_table, + const ENHMETARECORD* record, + int objects_count, + LPARAM param); + + // The collection of every EMF records in the currently loaded EMF buffer. + // Initialized by Enumerate(). It keeps pointers to the EMF buffer held by + // Emf::emf_. The entries become invalid once Emf::CloseEmf() is called. + std::vector<Record> items_; + + EnumerationContext context_; + + DISALLOW_EVIL_CONSTRUCTORS(Enumerator); +}; + +} // namespace gfx + +#endif // CHROME_COMMON_GFX_EMF_H__ diff --git a/chrome/common/gfx/emf_unittest.cc b/chrome/common/gfx/emf_unittest.cc new file mode 100644 index 0000000..00434d2 --- /dev/null +++ b/chrome/common/gfx/emf_unittest.cc @@ -0,0 +1,140 @@ +// 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 "chrome/common/gfx/emf.h" + +// For quick access. +#include <wingdi.h> + +#include "base/basictypes.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "chrome/browser/printing/win_printing_context.h" +#include "chrome/common/chrome_paths.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// This test is automatically disabled if no printer named "UnitTest Printer" is +// available. +class EmfPrintingTest : public testing::Test { + public: + typedef testing::Test Parent; + static bool IsTestCaseDisabled() { + // It is assumed this printer is a HP Color LaserJet 4550 PCL or 4700. + HDC hdc = CreateDC(L"WINSPOOL", L"UnitTest Printer", NULL, NULL); + if (!hdc) + return true; + DeleteDC(hdc); + return false; + } +}; + +} // namespace + +TEST(EmfTest, DC) { + static const int EMF_HEADER_SIZE = 128; + + // Simplest use case. + gfx::Emf emf; + RECT rect = {100, 100, 200, 200}; + HDC hdc = CreateCompatibleDC(NULL); + EXPECT_TRUE(hdc != NULL); + EXPECT_TRUE(emf.CreateDc(hdc, &rect)); + EXPECT_TRUE(emf.hdc() != NULL); + // In theory, you'd use the HDC with GDI functions here. + EXPECT_TRUE(emf.CloseDc()); + unsigned size = emf.GetDataSize(); + EXPECT_EQ(size, EMF_HEADER_SIZE); + std::vector<BYTE> data; + EXPECT_TRUE(emf.GetData(&data)); + EXPECT_EQ(data.size(), size); + emf.CloseEmf(); + EXPECT_TRUE(DeleteDC(hdc)); + + // Playback the data. + hdc = CreateCompatibleDC(NULL); + EXPECT_TRUE(hdc); + EXPECT_TRUE(emf.CreateFromData(&data.front(), size)); + RECT output_rect = {0, 0, 10, 10}; + EXPECT_TRUE(emf.Playback(hdc, &output_rect)); + EXPECT_TRUE(DeleteDC(hdc)); +} + + +// Disabled if no "UnitTest printer" exist. Useful to reproduce bug 1186598. +TEST_F(EmfPrintingTest, Enumerate) { + if (IsTestCaseDisabled()) + return; + + printing::PrintSettings settings; + + // My test case is a HP Color LaserJet 4550 PCL. + settings.set_device_name(L"UnitTest Printer"); + + // Initialize it. + printing::PrintingContext context; + EXPECT_EQ(context.InitWithSettings(settings), printing::PrintingContext::OK); + + std::wstring test_file; + PathService::Get(chrome::DIR_TEST_DATA, &test_file); + + // Load any EMF with an image. + gfx::Emf emf; + file_util::AppendToPath(&test_file, L"printing"); + file_util::AppendToPath(&test_file, L"test4.emf"); + std::string emf_data; + file_util::ReadFileToString(test_file, &emf_data); + ASSERT_TRUE(emf_data.size()); + EXPECT_TRUE(emf.CreateFromData(&emf_data[0], emf_data.size())); + + // This will print to file. The reason is that when running inside a + // unit_test, printing::PrintingContext automatically dumps its files to the + // current directory. + // TODO(maruel): Clean the .PRN file generated in current directory. + context.NewDocument(L"EmfTest.Enumerate"); + context.NewPage(); + // Process one at a time. + gfx::Emf::Enumerator emf_enum(emf, context.context(), + &emf.GetBounds().ToRECT()); + for (gfx::Emf::Enumerator::const_iterator itr = emf_enum.begin(); + itr != emf_enum.end(); + ++itr) { + // To help debugging. + ptrdiff_t index = itr - emf_enum.begin(); + // If you get this assert, you need to lookup iType in wingdi.h. It starts + // with EMR_HEADER. + EMR_HEADER; + EXPECT_TRUE(itr->SafePlayback(NULL)) << + " index: " << index << " type: " << itr->record()->iType; + } + context.PageDone(); + context.DocumentDone(); +} diff --git a/chrome/common/gfx/favicon_size.h b/chrome/common/gfx/favicon_size.h new file mode 100644 index 0000000..92514a0 --- /dev/null +++ b/chrome/common/gfx/favicon_size.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 CHROME_COMMON_GFX_FAVICON_SIZE_H__ +#define CHROME_COMMON_GFX_FAVICON_SIZE_H__ + +// Size (along each axis) of the favicon. +const int kFavIconSize = 16; + +// If the width or height is bigger than the favicon size, a new width/height +// is calculated and returned in width/height that maintains the aspect +// ratio of the supplied values. +static void calc_favicon_target_size(int* width, int* height) { + if (*width > kFavIconSize || *height > kFavIconSize) { + // Too big, resize it maintaining the aspect ratio. + float aspect_ratio = static_cast<float>(*width) / + static_cast<float>(*height); + *height = kFavIconSize; + *width = static_cast<int>(aspect_ratio * *height); + if (*width > kFavIconSize) { + *width = kFavIconSize; + *height = static_cast<int>(*width / aspect_ratio); + } + } +} + +#endif // CHROME_COMMON_GFX_FAVICON_SIZE_H__ diff --git a/chrome/common/gfx/icon_util.cc b/chrome/common/gfx/icon_util.cc new file mode 100644 index 0000000..2290623 --- /dev/null +++ b/chrome/common/gfx/icon_util.cc @@ -0,0 +1,501 @@ +// 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 "chrome/common/gfx/icon_util.h" +#include "base/file_util.h" +#include "base/gfx/image_operations.h" +#include "base/gfx/size.h" +#include "base/logging.h" +#include "chrome/common/win_util.h" +#include "skia/include/SkBitmap.h" + +// Defining the dimensions for the icon images. We store only one value because +// we always resize to a square image; that is, the value 48 means that we are +// going to resize the given bitmap to a 48 by 48 pixels bitmap. +// +// The icon images appear in the icon file in same order in which their +// corresponding dimensions appear in the |icon_dimensions_| array, so it is +// important to keep this array sorted. Also note that the maximum icon image +// size we can handle is 255 by 255. +const int IconUtil::icon_dimensions_[] = { + 8, // Recommended by the MSDN as a nice to have icon size. + 10, // Used by the Shell (e.g. for shortcuts). + 14, // Recommended by the MSDN as a nice to have icon size. + 16, // Toolbar, Application and Shell icon sizes. + 22, // Recommended by the MSDN as a nice to have icon size. + 24, // Used by the Shell (e.g. for shortcuts). + 32, // Toolbar, Dialog and Wizard icon size. + 40, // Quick Launch. + 48, // Alt+Tab icon size. + 64, // Recommended by the MSDN as a nice to have icon size. + 96, // Recommended by the MSDN as a nice to have icon size. + 128 // Used by the Shell (e.g. for shortcuts). +}; + +HICON IconUtil::CreateHICONFromSkBitmap(const SkBitmap& bitmap) { + // Only 32 bit ARGB bitmaps are supported. We also try to perform as many + // validations as we can on the bitmap. + SkAutoLockPixels bitmap_lock(bitmap); + if ((bitmap.getConfig() != SkBitmap::kARGB_8888_Config) || + (bitmap.width() <= 0) || (bitmap.height() <= 0) || + (bitmap.getPixels() == NULL)) { + return NULL; + } + + // We start by creating a DIB which we'll use later on in order to create + // the HICON. We use BITMAPV5HEADER since the bitmap we are about to convert + // may contain an alpha channel and the V5 header allows us to specify the + // alpha mask for the DIB. + BITMAPV5HEADER bitmap_header; + InitializeBitmapHeader(&bitmap_header, bitmap.width(), bitmap.height()); + void* bits; + HDC hdc = ::GetDC(NULL); + HBITMAP dib; + dib = ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&bitmap_header), + DIB_RGB_COLORS, &bits, NULL, 0); + DCHECK(dib); + ::ReleaseDC(NULL, hdc); + memcpy(bits, bitmap.getPixels(), bitmap.width() * bitmap.height() * 4); + + // Icons are generally created using an AND and XOR masks where the AND + // specifies boolean transparency (the pixel is either opaque or + // transparent) and the XOR mask contains the actual image pixels. However, + // since our bitmap has an alpha channel, the AND monochrome bitmap won't + // actually be used for computing the pixel transparency. Since every icon + // must have an AND mask bitmap, we go ahead and create one so that we can + // associate it with the ICONINFO structure we'll later pass to + // ::CreateIconIndirect(). The monochrome bitmap is created such that all the + // pixels are opaque. + HBITMAP mono_bitmap = ::CreateBitmap(bitmap.width(), bitmap.height(), + 1, 1, NULL); + DCHECK(mono_bitmap); + ICONINFO icon_info; + icon_info.fIcon = TRUE; + icon_info.xHotspot = 0; + icon_info.yHotspot = 0; + icon_info.hbmMask = mono_bitmap; + icon_info.hbmColor = dib; + HICON icon = ::CreateIconIndirect(&icon_info); + ::DeleteObject(dib); + ::DeleteObject(mono_bitmap); + return icon; +} + +SkBitmap* IconUtil::CreateSkBitmapFromHICON(HICON icon, const gfx::Size& s) { + // We start with validating parameters. + ICONINFO icon_info; + if (!icon || !(::GetIconInfo(icon, &icon_info)) || + !icon_info.fIcon || (s.width() <= 0) || (s.height() <= 0)) { + return NULL; + } + + // Allocating memory for the SkBitmap object. We are going to create an ARGB + // bitmap so we should set the configuration appropriately. + SkBitmap* bitmap = new SkBitmap; + DCHECK(bitmap); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, s.width(), s.height()); + bitmap->allocPixels(); + SkAutoLockPixels bitmap_lock(*bitmap); + + // Now we should create a DIB so that we can use ::DrawIconEx in order to + // obtain the icon's image. + BITMAPV5HEADER h; + InitializeBitmapHeader(&h, s.width(), s.height()); + HDC dc = ::GetDC(NULL); + unsigned int* bits; + HBITMAP dib = ::CreateDIBSection(dc, + reinterpret_cast<BITMAPINFO*>(&h), + DIB_RGB_COLORS, + reinterpret_cast<void**>(&bits), + NULL, + 0); + DCHECK(dib); + HDC dib_dc = CreateCompatibleDC(dc); + DCHECK(dib_dc); + ::SelectObject(dib_dc, dib); + + // Windows icons are defined using two different masks. The XOR mask, which + // represents the icon image and an AND mask which is a monochrome bitmap + // which indicates the transparency of each pixel. + // + // To make things more complex, the icon image itself can be an ARGB bitmap + // and therefore contain an alpha channel which specifies the transparency + // for each pixel. Unfortunately, there is no easy way to determine whether + // or not a bitmap has an alpha channel and therefore constructing the bitmap + // for the icon is nothing but straightforward. + // + // The idea is to read the AND mask but use it only if we know for sure that + // the icon image does not have an alpha channel. The only way to tell if the + // bitmap has an alpha channel is by looking through the pixels and checking + // whether there are non-zero alpha bytes. + // + // We start by drawing the AND mask into our DIB. + memset(bits, 0, s.width() * s.height() * 4); + ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_MASK); + + // Capture boolean opacity. We may not use it if we find out the bitmap has + // an alpha channel. + bool* opaque = new bool[s.width() * s.height()]; + DCHECK(opaque); + int x, y; + for (y = 0; y < s.height(); ++y) { + for (x = 0; x < s.width(); ++x) + opaque[(y * s.width()) + x] = !bits[(y * s.width()) + x]; + } + + // Then draw the image itself which is really the XOR mask. + memset(bits, 0, s.width() * s.height() * 4); + ::DrawIconEx(dib_dc, 0, 0, icon, s.width(), s.height(), 0, NULL, DI_NORMAL); + memcpy(bitmap->getPixels(), + static_cast<void*>(bits), + s.width() * s.height() * 4); + + // Finding out whether the bitmap has an alpha channel. + bool bitmap_has_alpha_channel = false; + unsigned int* p = static_cast<unsigned int*>(bitmap->getPixels()); + for (y = 0; y < s.height(); ++y) { + for (x = 0; x < s.width(); ++x) { + if ((*p & 0xff000000) != 0) { + bitmap_has_alpha_channel = true; + break; + } + p++; + } + + if (bitmap_has_alpha_channel) { + break; + } + } + + // If the bitmap does not have an alpha channel, we need to build it using + // the previously captured AND mask. Otherwise, we are done. + if (!bitmap_has_alpha_channel) { + p = static_cast<unsigned int*>(bitmap->getPixels()); + for (y = 0; y < s.height(); ++y) { + for (x = 0; x < s.width(); ++x) { + DCHECK_EQ((*p & 0xff000000), 0); + if (opaque[(y * s.width()) + x]) { + *p |= 0xff000000; + } else { + *p &= 0x00ffffff; + } + p++; + } + } + } + + delete [] opaque; + ::DeleteDC(dib_dc); + ::DeleteObject(dib); + ::ReleaseDC(NULL, dc); + + return bitmap; +} + +bool IconUtil::CreateIconFileFromSkBitmap(const SkBitmap& bitmap, + const std::wstring& icon_file_name) { + // Only 32 bit ARGB bitmaps are supported. We also make sure the bitmap has + // been properly initialized. + SkAutoLockPixels bitmap_lock(bitmap); + if ((bitmap.getConfig() != SkBitmap::kARGB_8888_Config) || + (bitmap.height() <= 0) || (bitmap.width() <= 0) || + (bitmap.getPixels() == NULL)) { + return false; + } + + // We start by creating the file. + win_util::ScopedHandle icon_file(::CreateFile(icon_file_name.c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL)); + + if (icon_file.Get() == INVALID_HANDLE_VALUE) { + return false; + } + + // Creating a set of bitmaps corresponding to the icon images we'll end up + // storing in the icon file. Each bitmap is created by resizing the given + // bitmap to the desired size. + std::vector<SkBitmap> bitmaps; + CreateResizedBitmapSet(bitmap, &bitmaps); + int bitmap_count = static_cast<int>(bitmaps.size()); + DCHECK_GT(bitmap_count, 0); + + // Computing the total size of the buffer we need in order to store the + // images in the desired icon format. + int buffer_size = ComputeIconFileBufferSize(bitmaps); + unsigned char* buffer = new unsigned char[buffer_size]; + DCHECK_NE(buffer, static_cast<unsigned char*>(NULL)); + memset(buffer, 0, buffer_size); + + // Setting the information in the structures residing within the buffer. + // First, we set the information which doesn't require iterating through the + // bitmap set and then we set the bitmap specific structures. In the latter + // step we also copy the actual bits. + ICONDIR* icon_dir = reinterpret_cast<ICONDIR*>(buffer); + icon_dir->idType = kResourceTypeIcon; + icon_dir->idCount = bitmap_count; + int icon_dir_count = bitmap_count - 1; + int offset = sizeof(ICONDIR) + (sizeof(ICONDIRENTRY) * icon_dir_count); + for (int i = 0; i < bitmap_count; i++) { + ICONIMAGE* image = reinterpret_cast<ICONIMAGE*>(buffer + offset); + DCHECK_LT(offset, buffer_size); + int icon_image_size = 0; + SetSingleIconImageInformation(bitmaps[i], + i, + icon_dir, + image, + offset, + &icon_image_size); + DCHECK_GT(icon_image_size, 0); + offset += icon_image_size; + } + DCHECK_EQ(offset, buffer_size); + + // Finally, writing the data info the file. + DWORD bytes_written; + bool delete_file = false; + if (!WriteFile(icon_file.Get(), buffer, buffer_size, &bytes_written, NULL) || + bytes_written != buffer_size) { + delete_file = true; + } + + ::CloseHandle(icon_file.Take()); + delete [] buffer; + if (delete_file) { + bool success = file_util::Delete(icon_file_name, false); + DCHECK(success); + } + + return !delete_file; +} + +int IconUtil::GetIconDimensionCount() { + return sizeof(icon_dimensions_) / sizeof(icon_dimensions_[0]); +} + +void IconUtil::InitializeBitmapHeader(BITMAPV5HEADER* header, int width, + int height) { + DCHECK(header); + memset(header, 0, sizeof(BITMAPV5HEADER)); + header->bV5Size = sizeof(BITMAPV5HEADER); + + // Note that icons are created using top-down DIBs so we must negate the + // value used for the icon's height. + header->bV5Width = width; + header->bV5Height = -height; + header->bV5Planes = 1; + header->bV5Compression = BI_RGB; + + // Initializing the bitmap format to 32 bit ARGB. + header->bV5BitCount = 32; + header->bV5RedMask = 0x00FF0000; + header->bV5GreenMask = 0x0000FF00; + header->bV5BlueMask = 0x000000FF; + header->bV5AlphaMask = 0xFF000000; + + // Use the system color space. The default value is LCS_CALIBRATED_RGB, which + // causes us to crash if we don't specify the approprite gammas, etc. See + // <http://msdn.microsoft.com/en-us/library/ms536531(VS.85).aspx> and + // <http://b/1283121>. + header->bV5CSType = LCS_WINDOWS_COLOR_SPACE; +} + +void IconUtil::SetSingleIconImageInformation(const SkBitmap& bitmap, + int index, + ICONDIR* icon_dir, + ICONIMAGE* icon_image, + int image_offset, + int* image_byte_count) { + DCHECK_GE(index, 0); + DCHECK_NE(icon_dir, static_cast<ICONDIR*>(NULL)); + DCHECK_NE(icon_image, static_cast<ICONIMAGE*>(NULL)); + DCHECK_GT(image_offset, 0); + DCHECK_NE(image_byte_count, static_cast<int*>(NULL)); + + // We start by computing certain image values we'll use later on. + int xor_mask_size; + int and_mask_size; + int bytes_in_resource; + ComputeBitmapSizeComponents(bitmap, + &xor_mask_size, + &and_mask_size, + &bytes_in_resource); + + icon_dir->idEntries[index].bWidth = static_cast<BYTE>(bitmap.width()); + icon_dir->idEntries[index].bHeight = static_cast<BYTE>(bitmap.height()); + icon_dir->idEntries[index].wPlanes = 1; + icon_dir->idEntries[index].wBitCount = 32; + icon_dir->idEntries[index].dwBytesInRes = bytes_in_resource; + icon_dir->idEntries[index].dwImageOffset = image_offset; + icon_image->icHeader.biSize = sizeof(BITMAPINFOHEADER); + + // The width field in the BITMAPINFOHEADER structure accounts for the height + // of both the AND mask and the XOR mask so we need to multiply the bitmap's + // height by 2. The same does NOT apply to the width field. + icon_image->icHeader.biHeight = bitmap.height() * 2; + icon_image->icHeader.biWidth = bitmap.width(); + icon_image->icHeader.biPlanes = 1; + icon_image->icHeader.biBitCount = 32; + + // We use a helper function for copying to actual bits from the SkBitmap + // object into the appropriate space in the buffer. We use a helper function + // (rather than just copying the bits) because there is no way to specify the + // orientation (bottom-up vs. top-down) of a bitmap residing in a .ico file. + // Thus, if we just copy the bits, we'll end up with a bottom up bitmap in + // the .ico file which will result in the icon being displayed upside down. + // The helper function copies the image into the buffer one scanline at a + // time. + // + // Note that we don't need to initialize the AND mask since the memory + // allocated for the icon data buffer was initialized to zero. The icon we + // create will therefore use an AND mask containing only zeros, which is OK + // because the underlying image has an alpha channel. An AND mask containing + // only zeros essentially means we'll initially treat all the pixels as + // opaque. + unsigned char* image_addr = reinterpret_cast<unsigned char*>(icon_image); + unsigned char* xor_mask_addr = image_addr + sizeof(BITMAPINFOHEADER); + CopySkBitmapBitsIntoIconBuffer(bitmap, xor_mask_addr, xor_mask_size); + *image_byte_count = bytes_in_resource; +} + +void IconUtil::CopySkBitmapBitsIntoIconBuffer(const SkBitmap& bitmap, + unsigned char* buffer, + int buffer_size) { + SkAutoLockPixels bitmap_lock(bitmap); + unsigned char* bitmap_ptr = static_cast<unsigned char*>(bitmap.getPixels()); + int bitmap_size = bitmap.height() * bitmap.width() * 4; + DCHECK_EQ(buffer_size, bitmap_size); + for (int i = 0; i < bitmap_size; i += bitmap.width() * 4) { + memcpy(buffer + bitmap_size - bitmap.width() * 4 - i, + bitmap_ptr + i, + bitmap.width() * 4); + } +} + +void IconUtil::CreateResizedBitmapSet(const SkBitmap& bitmap_to_resize, + std::vector<SkBitmap>* bitmaps) { + DCHECK_NE(bitmaps, static_cast<std::vector<SkBitmap>* >(NULL)); + DCHECK_EQ(static_cast<int>(bitmaps->size()), 0); + + bool inserted_original_bitmap = false; + for (int i = 0; i < GetIconDimensionCount(); i++) { + // If the dimensions of the bitmap we are resizing are the same as the + // current dimensions, then we should insert the bitmap and not a resized + // bitmap. If the bitmap's dimensions are smaller, we insert our bitmap + // first so that the bitmaps we return in the vector are sorted based on + // their dimensions. + if (!inserted_original_bitmap) { + if ((bitmap_to_resize.width() == icon_dimensions_[i]) && + (bitmap_to_resize.height() == icon_dimensions_[i])) { + bitmaps->push_back(bitmap_to_resize); + inserted_original_bitmap = true; + continue; + } + + if ((bitmap_to_resize.width() < icon_dimensions_[i]) && + (bitmap_to_resize.height() < icon_dimensions_[i])) { + bitmaps->push_back(bitmap_to_resize); + inserted_original_bitmap = true; + } + } + bitmaps->push_back(gfx::ImageOperations::Resize( + bitmap_to_resize, gfx::ImageOperations::RESIZE_LANCZOS3, + gfx::Size(icon_dimensions_[i], icon_dimensions_[i]))); + } + + if (!inserted_original_bitmap) { + bitmaps->push_back(bitmap_to_resize); + } +} + +int IconUtil::ComputeIconFileBufferSize(const std::vector<SkBitmap>& set) { + // We start by counting the bytes for the structures that don't depend on the + // number of icon images. Note that sizeof(ICONDIR) already accounts for a + // single ICONDIRENTRY structure, which is why we subtract one from the + // number of bitmaps. + int total_buffer_size = 0; + total_buffer_size += sizeof(ICONDIR); + int bitmap_count = static_cast<int>(set.size()); + total_buffer_size += sizeof(ICONDIRENTRY) * (bitmap_count - 1); + int dimension_count = GetIconDimensionCount(); + DCHECK_GE(bitmap_count, dimension_count); + + // Add the bitmap specific structure sizes. + for (int i = 0; i < bitmap_count; i++) { + int xor_mask_size; + int and_mask_size; + int bytes_in_resource; + ComputeBitmapSizeComponents(set[i], + &xor_mask_size, + &and_mask_size, + &bytes_in_resource); + total_buffer_size += bytes_in_resource; + } + return total_buffer_size; +} + +void IconUtil::ComputeBitmapSizeComponents(const SkBitmap& bitmap, + int* xor_mask_size, + int* and_mask_size, + int* bytes_in_resource) { + // The XOR mask size is easy to calculate since we only deal with 32bpp + // images. + *xor_mask_size = bitmap.width() * bitmap.height() * 4; + + // Computing the AND mask is a little trickier since it is a monochrome + // bitmap (regardless of the number of bits per pixels used in the XOR mask). + // There are two things we must make sure we do when computing the AND mask + // size: + // + // 1. Make sure the right number of bytes is allocated for each AND mask + // scan line in case the number of pixels in the image is not divisible by + // 8. For example, in a 15X15 image, 15 / 8 is one byte short of + // containing the number of bits we need in order to describe a single + // image scan line so we need to add a byte. Thus, we need 2 bytes instead + // of 1 for each scan line. + // + // 2. Make sure each scan line in the AND mask is 4 byte aligned (so that the + // total icon image has a 4 byte alignment). In the 15X15 image example + // above, we can not use 2 bytes so we increase it to the next multiple of + // 4 which is 4. + // + // Once we compute the size for a singe AND mask scan line, we multiply that + // number by the image height in order to get the total number of bytes for + // the AND mask. Thus, for a 15X15 image, we need 15 * 4 which is 60 bytes + // for the monochrome bitmap representing the AND mask. + int and_line_length = (bitmap.width() + 7) >> 3; + and_line_length = (and_line_length + 3) & ~3; + *and_mask_size = and_line_length * bitmap.height(); + int masks_size = *xor_mask_size + *and_mask_size; + *bytes_in_resource = masks_size + sizeof(BITMAPINFOHEADER); +} diff --git a/chrome/common/gfx/icon_util.h b/chrome/common/gfx/icon_util.h new file mode 100644 index 0000000..fe0945f --- /dev/null +++ b/chrome/common/gfx/icon_util.h @@ -0,0 +1,218 @@ +// 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 CHROME_COMMON_ICON_UTIL_H__ +#define CHROME_COMMON_ICON_UTIL_H__ + +#include <windows.h> +#include <string> +#include <vector> +#include "base/basictypes.h" + +namespace gfx { + class Size; +} +class SkBitmap; + +/////////////////////////////////////////////////////////////////////////////// +// +// The IconUtil class contains helper functions for manipulating Windows icons. +// The class interface contains methods for converting an HICON handle into an +// SkBitmap object and vice versa. The class can also create a .ico file given +// a PNG image contained in an SkBitmap object. The following code snippet +// shows an example usage of IconUtil::CreateHICONFromSkBitmap(): +// +// SkBitmap bitmap; +// +// // Fill |bitmap| with valid data +// bitmap.setConfig(...); +// bitmap.allocPixels(); +// +// ... +// +// // Convert the bitmap into a Windows HICON +// HICON icon = IconUtil::CreateHICONFromSkBitmap(bitmap); +// if (icon == NULL) { +// // Handle error +// ... +// } +// +// // Use the icon with a WM_SETICON message +// ::SendMessage(hwnd, WM_SETICON, static_cast<WPARAM>(ICON_BIG), +// reinterpret_cast<LPARAM>(icon)); +// +// // Destroy the icon when we are done +// ::DestroyIcon(icon); +// +/////////////////////////////////////////////////////////////////////////////// +class IconUtil { + public: + // Given an SkBitmap object, the function converts the bitmap to a Windows + // icon and returns the corresponding HICON handle. If the function can not + // convert the bitmap, NULL is returned. + // + // The client is responsible for destroying the icon when it is no longer + // needed by calling ::DestroyIcon(). + static HICON CreateHICONFromSkBitmap(const SkBitmap& bitmap); + + // Given a valid HICON handle representing an icon, this function converts + // the icon into an SkBitmap object containing an ARGB bitmap using the + // dimensions specified in |s|. |s| must specify valid dimensions (both + // width() an height() must be greater than zero). If the function can + // convert the icon to a bitmap (most probably due to an invalid parameter), + // the return value is NULL. + // + // The client owns the returned bitmap object and is responsible for deleting + // it when it is no longer needed. + static SkBitmap* CreateSkBitmapFromHICON(HICON icon, const gfx::Size& s); + + // Given an initialized SkBitmap object and a file name, this function + // creates a .ico file with the given name using the provided bitmap. The + // icon file is created with multiple icon images of varying predefined + // dimensions because Windows uses different image sizes when loading icons, + // depending on where the icon is drawn (ALT+TAB window, desktop shortcut, + // Quick Launch, etc.). |icon_file_name| needs to specify the full path for + // the desired .ico file. + // + // The function returns true on success and false otherwise. + static bool CreateIconFileFromSkBitmap(const SkBitmap& bitmap, + const std::wstring& icon_file_name); + + private: + // The icon format is published in the MSDN but there is no definition of + // the icon file structures in any of the Windows header files so we need to + // define these structure within the class. We must make sure we use 2 byte + // packing so that the structures are layed out properly within the file. +#pragma pack(push) +#pragma pack(2) + + // ICONDIRENTRY contains meta data for an individual icon image within a + // .ico file. + struct ICONDIRENTRY { + BYTE bWidth; + BYTE bHeight; + BYTE bColorCount; + BYTE bReserved; + WORD wPlanes; + WORD wBitCount; + DWORD dwBytesInRes; + DWORD dwImageOffset; + }; + + // ICONDIR Contains information about all the icon images contained within a + // single .ico file. + struct ICONDIR { + WORD idReserved; + WORD idType; + WORD idCount; + ICONDIRENTRY idEntries[1]; + }; + + // Contains the actual icon image. + struct ICONIMAGE { + BITMAPINFOHEADER icHeader; + RGBQUAD icColors[1]; + BYTE icXOR[1]; + BYTE icAND[1]; + }; +#pragma pack(pop) + + // Used for indicating that the .ico contains an icon (rather than a cursor) + // image. This value is set in the |idType| field of the ICONDIR structure. + static const int kResourceTypeIcon = 1; + + // The dimensions of the icon images we insert into the .ico file. + static const int icon_dimensions_[]; + + // Returns how many icon dimensions are defined. + static int GetIconDimensionCount(); + + // A helper function that initializes a BITMAPV5HEADER structure with a set + // of values. + static void InitializeBitmapHeader(BITMAPV5HEADER* header, int width, + int height); + + // Given a single SkBitmap object and pointers to the corresponding icon + // structures within the icon data buffer, this function sets the image + // information (dimensions, color depth, etc.) in the icon structures and + // also copies the underlying icon image into the appropriate location. + // + // The function will set the data pointed to by |image_byte_count| with the + // number of image bytes written to the buffer. Note that the number of bytes + // includes only the image data written into the memory pointed to by + // |icon_image|. + static void SetSingleIconImageInformation(const SkBitmap& bitmap, + int index, + ICONDIR* icon_dir, + ICONIMAGE* icon_image, + int image_offset, + int* image_byte_count); + + // Copies the bits of an SkBitmap object into a buffer holding the bits of + // the corresponding image for an icon within the .ico file. + static void CopySkBitmapBitsIntoIconBuffer(const SkBitmap& bitmap, + unsigned char* buffer, + int buffer_size); + + // Given a single bitmap, this function creates a set of bitmaps with + // specific dimensions by resizing the given bitmap to the appropriate sizes. + static void CreateResizedBitmapSet(const SkBitmap& bitmap_to_resize, + std::vector<SkBitmap>* bitmaps); + + // Given a set of bitmaps with varying dimensions, this function computes + // the amount of memory needed in order to store the bitmaps as image icons + // in a .ico file. + static int ComputeIconFileBufferSize(const std::vector<SkBitmap>& set); + + // A helper function for computing various size components of a given bitmap. + // The different sizes can be used within the various .ico file structures. + // + // |xor_mask_size| - the size, in bytes, of the XOR mask in the ICONIMAGE + // structure. + // |and_mask_size| - the size, in bytes, of the AND mask in the ICONIMAGE + // structure. + // |bytes_in_resource| - the total number of bytes set in the ICONIMAGE + // structure. This value is equal to the sum of the + // bytes in the AND mask and the XOR mask plus the size + // of the BITMAPINFOHEADER structure. Note that since + // only 32bpp are handled by the IconUtil class, the + // icColors field in the ICONIMAGE structure is ignored + // and is not accounted for when computing the + // different size components. + static void ComputeBitmapSizeComponents(const SkBitmap& bitmap, + int* xor_mask_size, + int* and_mask_size, + int* bytes_in_resource); + + // Prevent clients from instantiating objects of that class by declaring the + // ctor/dtor as private. + DISALLOW_IMPLICIT_CONSTRUCTORS(IconUtil); +}; + +#endif // CHROME_COMMON_ICON_UTIL_H__ diff --git a/chrome/common/gfx/icon_util_unittest.cc b/chrome/common/gfx/icon_util_unittest.cc new file mode 100644 index 0000000..c0cfa03 --- /dev/null +++ b/chrome/common/gfx/icon_util_unittest.cc @@ -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. + +#include <atlbase.h> +#include <atlapp.h> +#include <atlmisc.h> + +#include "chrome/common/gfx/icon_util.h" +#include "base/gfx/size.h" +#include "base/scoped_ptr.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "chrome/common/chrome_paths.h" +#include "skia/include/SkBitmap.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + + static const wchar_t* const kSmallIconName = L"icon_util\\16_X_16_icon.ico"; + static const wchar_t* const kLargeIconName = L"icon_util\\128_X_128_icon.ico"; + static const wchar_t* const kTempIconFilename = L"temp_test_icon.ico"; + + class IconUtilTest : public testing::Test { + public: + IconUtilTest() { + PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_); + } + ~IconUtilTest() {} + + static const int kSmallIconWidth = 16; + static const int kSmallIconHeight = 16; + static const int kLargeIconWidth = 128; + static const int kLargeIconHeight = 128; + + // Given a file name for an .ico file and an image dimentions, this + // function loads the icon and returns an HICON handle. + HICON LoadIconFromFile(const std::wstring& filename, + int width, + int height) { + HICON icon = + static_cast<HICON>(LoadImage(NULL, + filename.c_str(), + IMAGE_ICON, + width, + height, + LR_LOADTRANSPARENT | LR_LOADFROMFILE)); + return icon; + } + + protected: + // The root directory for test files. + std::wstring test_data_directory_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(IconUtilTest); + }; +}; + +// The following test case makes sure IconUtil::SkBitmapFromHICON fails +// gracefully when called with invalid input parameters. +TEST_F(IconUtilTest, TestIconToBitmapInvalidParameters) { + std::wstring icon_filename(test_data_directory_); + file_util::AppendToPath(&icon_filename, kSmallIconName); + gfx::Size icon_size(kSmallIconWidth, kSmallIconHeight); + HICON icon = LoadIconFromFile(icon_filename, + icon_size.width(), + icon_size.height()); + ASSERT_TRUE(icon != NULL); + + // Invalid size parameter. + gfx::Size invalid_icon_size(kSmallIconHeight, 0); + EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(icon, invalid_icon_size), + static_cast<SkBitmap*>(NULL)); + + // Invalid icon. + EXPECT_EQ(IconUtil::CreateSkBitmapFromHICON(NULL, icon_size), + static_cast<SkBitmap*>(NULL)); + + // The following code should succeed. + scoped_ptr<SkBitmap> bitmap; + bitmap.reset(IconUtil::CreateSkBitmapFromHICON(icon, icon_size)); + EXPECT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + ::DestroyIcon(icon); +} + +// The following test case makes sure IconUtil::CreateHICONFromSkBitmap fails +// gracefully when called with invalid input parameters. +TEST_F(IconUtilTest, TestBitmapToIconInvalidParameters) { + HICON icon = NULL; + scoped_ptr<SkBitmap> bitmap; + + // Wrong bitmap format. + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight); + icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); + EXPECT_EQ(icon, static_cast<HICON>(NULL)); + + // Invalid bitmap size. + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0); + icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); + EXPECT_EQ(icon, static_cast<HICON>(NULL)); + + // Valid bitmap configuration but no pixels allocated. + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, + kSmallIconWidth, + kSmallIconHeight); + icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); + EXPECT_TRUE(icon == NULL); +} + +// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap +// fails gracefully when called with invalid input parameters. +TEST_F(IconUtilTest, TestCreateIconFileInvalidParameters) { + scoped_ptr<SkBitmap> bitmap; + std::wstring valid_icon_filename(test_data_directory_); + file_util::AppendToPath(&valid_icon_filename, kSmallIconName); + std::wstring invalid_icon_filename(L"C:\\<>?.ico"); + + // Wrong bitmap format. + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kA8_Config, kSmallIconWidth, kSmallIconHeight); + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + valid_icon_filename)); + + // Invalid bitmap size. + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, 0, 0); + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + valid_icon_filename)); + + // Bitmap with no allocated pixels. + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, + kSmallIconWidth, + kSmallIconHeight); + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + valid_icon_filename)); + + // Invalid file name. + bitmap->allocPixels(); + EXPECT_FALSE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + invalid_icon_filename)); +} + +// This test case makes sure that when we load an icon from disk and convert +// the HICON into a bitmap, the bitmap has the expected format and dimentions. +TEST_F(IconUtilTest, TestCreateSkBitmapFromHICON) { + scoped_ptr<SkBitmap> bitmap; + std::wstring small_icon_filename(test_data_directory_); + file_util::AppendToPath(&small_icon_filename, kSmallIconName); + gfx::Size small_icon_size(kSmallIconWidth, kSmallIconHeight); + HICON small_icon = LoadIconFromFile(small_icon_filename, + small_icon_size.width(), + small_icon_size.height()); + ASSERT_NE(small_icon, static_cast<HICON>(NULL)); + bitmap.reset(IconUtil::CreateSkBitmapFromHICON(small_icon, small_icon_size)); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + EXPECT_EQ(bitmap->width(), small_icon_size.width()); + EXPECT_EQ(bitmap->height(), small_icon_size.height()); + EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config); + ::DestroyIcon(small_icon); + + std::wstring large_icon_filename(test_data_directory_); + file_util::AppendToPath(&large_icon_filename, kLargeIconName); + gfx::Size large_icon_size(kLargeIconWidth, kLargeIconHeight); + HICON large_icon = LoadIconFromFile(large_icon_filename, + large_icon_size.width(), + large_icon_size.height()); + ASSERT_NE(large_icon, static_cast<HICON>(NULL)); + bitmap.reset(IconUtil::CreateSkBitmapFromHICON(large_icon, large_icon_size)); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + EXPECT_EQ(bitmap->width(), large_icon_size.width()); + EXPECT_EQ(bitmap->height(), large_icon_size.height()); + EXPECT_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config); + ::DestroyIcon(large_icon); +} + +// This test case makes sure that when an HICON is created from an SkBitmap, +// the returned handle is valid and refers to an icon with the expected +// dimentions color depth etc. +TEST_F(IconUtilTest, TestBasicCreateHICONFromSkBitmap) { + scoped_ptr<SkBitmap> bitmap; + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, + kSmallIconWidth, + kSmallIconHeight); + bitmap->allocPixels(); + HICON icon = IconUtil::CreateHICONFromSkBitmap(*bitmap); + EXPECT_NE(icon, static_cast<HICON>(NULL)); + ICONINFO icon_info; + ASSERT_TRUE(::GetIconInfo(icon, &icon_info)); + EXPECT_TRUE(icon_info.fIcon); + + // Now that have the icon information, we should obtain the specification of + // the icon's bitmap and make sure it matches the specification of the + // SkBitmap we started with. + // + // The bitmap handle contained in the icon information is a handle to a + // compatible bitmap so we need to call ::GetDIBits() in order to retrieve + // the bitmap's header information. + BITMAPINFO bitmap_info; + ::ZeroMemory(&bitmap_info, sizeof(BITMAPINFO)); + bitmap_info.bmiHeader.biSize = sizeof(BITMAPINFO); + HDC hdc = ::GetDC(NULL); + int result = ::GetDIBits(hdc, + icon_info.hbmColor, + 0, + kSmallIconWidth, + NULL, + &bitmap_info, + DIB_RGB_COLORS); + ASSERT_GT(result, 0); + EXPECT_EQ(bitmap_info.bmiHeader.biWidth, kSmallIconWidth); + EXPECT_EQ(bitmap_info.bmiHeader.biHeight, kSmallIconHeight); + EXPECT_EQ(bitmap_info.bmiHeader.biPlanes, 1); + EXPECT_EQ(bitmap_info.bmiHeader.biBitCount, 32); + ::ReleaseDC(NULL, hdc); + ::DestroyIcon(icon); +} + +// The following test case makes sure IconUtil::CreateIconFileFromSkBitmap +// creates a valid .ico file given an SkBitmap. +TEST_F(IconUtilTest, TestCreateIconFile) { + scoped_ptr<SkBitmap> bitmap; + std::wstring icon_filename(test_data_directory_); + file_util::AppendToPath(&icon_filename, kTempIconFilename); + + // Allocating the bitmap. + bitmap.reset(new SkBitmap); + ASSERT_NE(bitmap.get(), static_cast<SkBitmap*>(NULL)); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, + kSmallIconWidth, + kSmallIconHeight); + bitmap->allocPixels(); + + // Setting the pixels to black. + memset(bitmap->getPixels(), 0, bitmap->width() * bitmap->height() * 4); + + EXPECT_TRUE(IconUtil::CreateIconFileFromSkBitmap(*bitmap, + icon_filename)); + + // We are currently only testing that it is possible to load an icon from + // the .ico file we just created. We don't really check the additional icon + // images created by IconUtil::CreateIconFileFromSkBitmap. + HICON icon = LoadIconFromFile(icon_filename, + kSmallIconWidth, + kSmallIconHeight); + EXPECT_NE(icon, static_cast<HICON>(NULL)); + if (icon != NULL) { + ::DestroyIcon(icon); + } +} diff --git a/chrome/common/gfx/insets.h b/chrome/common/gfx/insets.h new file mode 100644 index 0000000..d92323d --- /dev/null +++ b/chrome/common/gfx/insets.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 CHROME_COMMON_GFX_INSETS_H__ +#define CHROME_COMMON_GFX_INSETS_H__ + +namespace gfx { + +// +// An insets represents the borders of a container (the space the container must +// leave at each of its edges). +// + +class Insets { + public: + Insets() : top_(0), left_(0), bottom_(0), right_(0) {} + Insets(int top, int left, int bottom, int right) + : top_(top), left_(left), bottom_(bottom), right_(right) { } + + ~Insets() {} + + int top() const { return top_; } + int left() const { return left_; } + int bottom() const { return bottom_; } + int right() const { return right_; } + + // Returns the total width taken up by the insets, which is the sum of the + // left and right insets. + int width() const { return left_ + right_; } + + // Returns the total height taken up by the insets, which is the sum of the + // top and bottom insets. + int height() const { return top_ + bottom_; } + + void Set(int top, int left, int bottom, int right) { + top_ = top; + left_ = left; + bottom_ = bottom; + right_ = right; + } + + bool operator==(const Insets& insets) const { + return top_ == insets.top_ && left_ == insets.left_ && + bottom_ == insets.bottom_ && right_ == insets.right_; + } + + bool operator!=(const Insets& insets) const { + return !(*this == insets); + } + + private: + int top_; + int left_; + int bottom_; + int right_; +}; + +} // namespace + +#endif // CHROME_COMMON_GFX_INSETS_H__ diff --git a/chrome/common/gfx/path.cc b/chrome/common/gfx/path.cc new file mode 100644 index 0000000..0ba6d9a --- /dev/null +++ b/chrome/common/gfx/path.cc @@ -0,0 +1,54 @@ +// 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 "chrome/common/gfx/path.h" + +#include "base/scoped_ptr.h" + +namespace gfx { + +Path::Path() : SkPath() { + moveTo(0, 0); +} + +HRGN Path::CreateHRGN() const { + int point_count = getPoints(NULL, 0); + scoped_array<SkPoint> points(new SkPoint[point_count]); + getPoints(points.get(), point_count); + scoped_array<POINT> windows_points(new POINT[point_count]); + for (int i = 0; i < point_count; ++i) { + windows_points[i].x = SkScalarRound(points[i].fX); + windows_points[i].y = SkScalarRound(points[i].fY); + } + HRGN region = ::CreatePolygonRgn(windows_points.get(), point_count, ALTERNATE); + + return region; +} + +}; diff --git a/chrome/common/gfx/path.h b/chrome/common/gfx/path.h new file mode 100644 index 0000000..cc22a41 --- /dev/null +++ b/chrome/common/gfx/path.h @@ -0,0 +1,54 @@ +// 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 CHROME_COMMON_GFX_CHROME_PATH_H__ +#define CHROME_COMMON_GFX_CHROME_PATH_H__ + +#include <windows.h> + +#include "base/basictypes.h" +#include "SkPath.h" + +namespace gfx { + +class Path : public SkPath { + public: + Path(); + + // Creates a HRGN from the path. The caller is responsible for freeing + // resources used by this region. + HRGN CreateHRGN() const; + + private: + DISALLOW_EVIL_CONSTRUCTORS(Path); +}; + +} + +#endif // #ifndef CHROME_COMMON_GFX_CHROME_PATH_H__ diff --git a/chrome/common/gfx/url_elider.cc b/chrome/common/gfx/url_elider.cc new file mode 100644 index 0000000..cd64b29 --- /dev/null +++ b/chrome/common/gfx/url_elider.cc @@ -0,0 +1,435 @@ +// 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/string_util.h" +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/gfx/url_elider.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/views/hwnd_view_container.h" +#include "chrome/views/label.h" +#include "net/base/escape.h" +#include "net/base/net_util.h" +#include "net/base/registry_controlled_domain.h" + +const wchar_t kEllipsis[] = L"\x2026"; + +namespace gfx { + +// Appends the given part of the original URL to the output string formatted for +// the user. The given parsed structure will be updated. The host name formatter +// also takes the same accept languages component as ElideURL. +static void AppendFormattedHost(const GURL& url, + const std::wstring& languages, + std::wstring* output, + url_parse::Parsed* new_parsed); + +// Calls the unescaper for the substring |in_component| inside of the URL +// |spec|. The decoded string will be appended to |output| and the resulting +// range will be filled into |out_component|. +static void AppendFormattedComponent(const std::string& spec, + const url_parse::Component& in_component, + std::wstring* output, + url_parse::Component* out_component); + +// A helper function to get Clean Url String from a GURL. The parsing of the +// URL may change because various parts of the string will change lengths. The +// new parsing will be placed in the given out parameter. +static std::wstring GetCleanStringFromUrl(const GURL& url, + const std::wstring& languages, + url_parse::Parsed* new_parsed); + +// This function takes a GURL object and elides it. It returns a string +// which composed of parts from subdomain, domain, path, filename and query. +// A "..." is added automatically at the end if the elided string is bigger +// than the available pixel width. For available pixel width = 0, a formatted, +// but un-elided, string is returned. +// +// TODO(pkasting): http://b/119635 This whole function gets +// kerning/ligatures/etc. issues potentially wrong by assuming that the width of +// a rendered string is always the sum of the widths of its substrings. Also I +// suspect it could be made simpler. +std::wstring ElideUrl(const GURL& url, + const ChromeFont& font, + int available_pixel_width, + const std::wstring& languages) { + // Get a formatted string and corresponding parsing of the url. + url_parse::Parsed parsed; + std::wstring url_string = GetCleanStringFromUrl(url, languages, &parsed); + if (available_pixel_width <= 0) + return url_string; + + // If non-standard or not file type, return plain eliding. + if (!(url.SchemeIsFile() || url.IsStandard())) + return ElideText(url_string, font, available_pixel_width); + + // Now start eliding url_string to fit within available pixel width. + // Fist pass - check to see whether entire url_string fits. + int pixel_width_url_string = font.GetStringWidth(url_string); + if (available_pixel_width >= pixel_width_url_string) + return url_string; + + // Get the path substring, including query and reference. + size_t path_start_index = parsed.path.begin; + size_t path_len = parsed.path.len; + std::wstring url_path_query_etc = url_string.substr(path_start_index); + std::wstring url_path = url_string.substr(path_start_index, path_len); + + // Return general elided text if url minus the query fits. + std::wstring url_minus_query = url_string.substr(0, path_start_index + + path_len); + if (available_pixel_width >= font.GetStringWidth(url_minus_query)) + return ElideText(url_string, font, available_pixel_width); + + // Get Host. + std::wstring url_host = UTF8ToWide(url.host()); + + // Get domain and registry information from the URL. + std::wstring url_domain = UTF8ToWide( + RegistryControlledDomainService::GetDomainAndRegistry(url)); + if (url_domain.empty()) + url_domain = url_host; + + // Add port if required. + if (!url.port().empty()){ + url_host += L":" + UTF8ToWide(url.port()); + url_domain += L":" + UTF8ToWide(url.port()); + } + + // Get sub domain. + std::wstring url_subdomain; + size_t domain_start_index = url_host.find(url_domain); + if (domain_start_index > 0) + url_subdomain = url_host.substr(0, domain_start_index); + if ((url_subdomain == L"www." || url_subdomain.empty() || + url.SchemeIsFile())) { + url_subdomain.clear(); + } + + // If this is a file type, the path is now defined as everything after ":". + // For example, "C:/aa/aa/bb", the path is "/aa/bb/cc". Interesting, the + // domain is now C: - this is a nice hack for eliding to work pleasantly. + if (url.SchemeIsFile()) { + // Split the path string using ":" + std::vector<std::wstring> file_path_split; + SplitString(url_path, L':', &file_path_split); + if (file_path_split.size() > 1) { // File is of type "file:///C:/.." + url_host.clear(); + url_domain.clear(); + url_subdomain.clear(); + + url_host = url_domain = file_path_split.at(0).substr(1) + L":"; + url_path_query_etc = url_path = file_path_split.at(1); + } + } + + // Second Pass - remove scheme - the rest fits. + int pixel_width_url_host = font.GetStringWidth(url_host); + int pixel_width_url_path = font.GetStringWidth(url_path_query_etc); + if (available_pixel_width >= + pixel_width_url_host + pixel_width_url_path) + return url_host + url_path_query_etc; + + // Third Pass: Subdomain, domain and entire path fits. + int pixel_width_url_domain = font.GetStringWidth(url_domain); + int pixel_width_url_subdomain = font.GetStringWidth(url_subdomain); + if (available_pixel_width >= + pixel_width_url_subdomain + pixel_width_url_domain + + pixel_width_url_path) + return url_subdomain + url_domain + url_path_query_etc; + + // Query element. + std::wstring url_query; + const int pixel_width_dots_trailer = font.GetStringWidth(kEllipsis); + if (parsed.query.is_nonempty()) { + url_query = L"?" + url_string.substr(parsed.query.begin); + if (available_pixel_width >= (pixel_width_url_subdomain + + pixel_width_url_domain + pixel_width_url_path - + font.GetStringWidth(url_query))) { + return ElideText(url_subdomain + url_domain + url_path_query_etc, font, + available_pixel_width); + } + } + + // Parse url_path using '/'. + std::vector<std::wstring> url_path_elements; + SplitString(url_path, L'/', &url_path_elements); + + // Get filename - note that for a path ending with / + // such as www.google.com/intl/ads/, the file name is ads/. + int url_path_number_of_elements = static_cast<int> (url_path_elements. + size()); + std::wstring url_filename; + if ((url_path_elements.at(url_path_number_of_elements - 1)).length() > 0) { + url_filename = *(url_path_elements.end()-1); + } else if (url_path_number_of_elements > 1) { // Path ends with a '/'. + url_filename = url_path_elements.at(url_path_number_of_elements - 2) + + L"/"; + url_path_number_of_elements--; + } + + // Start eliding the path and replacing elements by "../". + std::wstring an_ellipsis_and_a_slash(kEllipsis); + an_ellipsis_and_a_slash += '/'; + int pixel_width_url_filename = font.GetStringWidth(url_filename); + int pixel_width_dot_dot_slash = font.GetStringWidth(an_ellipsis_and_a_slash); + int pixel_width_slash = font.GetStringWidth(L"/"); + int pixel_width_url_path_elements[256]; // Declared static for speed. + for (int i = 0; i < url_path_number_of_elements; i++) { + pixel_width_url_path_elements[i] = + font.GetStringWidth(url_path_elements.at(i)); + } + + if (url_path_number_of_elements <= 1) { + // Nothing FITS - return domain and rest. + return ElideText(url_subdomain + url_domain + url_path_query_etc, font, + available_pixel_width); + } + + // Check with both subdomain and domain. + std::wstring elided_path; + int pixel_width_elided_path; + for (int i = url_path_number_of_elements - 1; i >= 1; i--) { + // Add the initial elements of the path. + elided_path.clear(); + pixel_width_elided_path = 0; + for (int j = 0; j < i; j++) { + elided_path += url_path_elements.at(j) + L"/"; + pixel_width_elided_path += pixel_width_url_path_elements[j] + + pixel_width_slash; + } + + // Add url_file_name. + if (i == (url_path_number_of_elements - 1)) { + elided_path += url_filename; + pixel_width_elided_path += pixel_width_url_filename; + } else { + elided_path += an_ellipsis_and_a_slash + url_filename; + pixel_width_elided_path += pixel_width_dot_dot_slash + + pixel_width_url_filename; + } + + if (available_pixel_width >= + pixel_width_url_subdomain + pixel_width_url_domain + + pixel_width_elided_path) { + return ElideText(url_subdomain + url_domain + elided_path + url_query, + font, available_pixel_width); + } + } + + // Check with only domain. + // If a subdomain is present, add an ellipsis before domain. + // This is added only if the subdomain pixel width is larger than + // the pixel width of kEllipsis. Otherwise, subdomain remains, + // which means that this case has been resolved earlier. + std::wstring url_elided_domain = url_subdomain + url_domain; + int pixel_width_url_elided_domain = pixel_width_url_domain; + if (pixel_width_url_subdomain > pixel_width_dots_trailer) { + if (!url_subdomain.empty()) { + url_elided_domain = kEllipsis + url_domain; + pixel_width_url_elided_domain += pixel_width_dots_trailer; + } else { + url_elided_domain = url_domain; + } + + for (int i = url_path_number_of_elements - 1; i >= 1; i--) { + // Add the initial elements of the path. + elided_path.clear(); + pixel_width_elided_path = 0; + for (int j = 0; j < i; j++) { + elided_path += url_path_elements.at(j) + L"/"; + pixel_width_elided_path += pixel_width_url_path_elements[j] + + pixel_width_slash; + } + + // Add url_file_name. + if (i == (url_path_number_of_elements - 1)) { + elided_path += url_filename; + pixel_width_elided_path += pixel_width_url_filename; + } else { + elided_path += an_ellipsis_and_a_slash + url_filename; + pixel_width_elided_path += pixel_width_dot_dot_slash + + pixel_width_url_filename; + } + + if (available_pixel_width >= + pixel_width_url_elided_domain + pixel_width_elided_path) { + return ElideText(url_elided_domain + elided_path + url_query, font, + available_pixel_width); + } + } + } + + // Return elided domain/../filename anyway. + std::wstring final_elided_url_string(url_elided_domain); + if ((available_pixel_width - font.GetStringWidth(url_elided_domain)) > + pixel_width_dot_dot_slash + pixel_width_dots_trailer + + font.GetStringWidth(L"UV")) // A hack to prevent trailing "../...". + final_elided_url_string += elided_path; + else + final_elided_url_string += url_path; + + return ElideText(final_elided_url_string, font, available_pixel_width); +} + +// This function adds an ellipsis at the end of the text if the text +// does not fit the given pixel width. +std::wstring ElideText(const std::wstring& text, + const ChromeFont& font, + int available_pixel_width){ + if (text.empty()) + return text; + + int current_text_pixel_width = font.GetStringWidth(text); + if (current_text_pixel_width <= available_pixel_width) + return text; + + if (font.GetStringWidth(kEllipsis) > available_pixel_width) + return std::wstring(); + + // Use binary search to compute the elided text. + size_t lo = 0; + size_t hi = text.length() - 1; + size_t guess = hi / 2; + while (lo < hi) { + // We check the length of the whole desired string at once to ensure we + // handle kerning/ligatures/etc. correctly. + std::wstring guess_str = text.substr(0, guess) + kEllipsis; + int guess_length = font.GetStringWidth(guess_str); + if (guess_length > available_pixel_width) { + if (hi == guess) + break; + hi = guess; + } else { + if (lo == guess) + break; + lo = guess; + } + guess = (lo + hi) / 2; + } + + return text.substr(0, lo) + kEllipsis; +} + +void AppendFormattedHost(const GURL& url, + const std::wstring& languages, + std::wstring* output, + url_parse::Parsed* new_parsed) { + const url_parse::Component& host = + url.parsed_for_possibly_invalid_spec().host; + + if (host.is_nonempty()) { + // Handle possible IDN in the host name. + new_parsed->host.begin = static_cast<int>(output->length()); + + const std::string& spec = url.possibly_invalid_spec(); + DCHECK(host.begin >= 0 && + ((spec.length() == 0 && host.begin == 0) || + host.begin < static_cast<int>(spec.length()))); + net_util::IDNToUnicode(&spec[host.begin], host.len, languages, output); + + new_parsed->host.len = + static_cast<int>(output->length()) - new_parsed->host.begin; + } else { + new_parsed->host.reset(); + } +} + +void AppendFormattedComponent(const std::string& spec, + const url_parse::Component& in_component, + std::wstring* output, + url_parse::Component* out_component) { + if (in_component.is_nonempty()) { + out_component->begin = static_cast<int>(output->length()); + + output->append(UnescapeAndDecodeUTF8URLComponent( + spec.substr(in_component.begin, in_component.len), UnescapeRule::NORMAL)); + + out_component->len = + static_cast<int>(output->length()) - out_component->begin; + } else { + out_component->reset(); + } +} + +std::wstring GetCleanStringFromUrl(const GURL& url, + const std::wstring& languages, + url_parse::Parsed* new_parsed) { + std::wstring url_string; + + // Check for empty URLs or 0 available text width. + if (url.is_empty()) + return url_string; + + // We handle both valid and invalid URLs (this will give us the spec + // regardless of validity). + const std::string& spec = url.possibly_invalid_spec(); + const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec(); + + // Construct a new URL with the username and password fields removed. We + // don't want to display those to the user since they can be used for + // attacks, e.g. "http://google.com:search@evil.ru/" + // + // Copy everything before the host name we want (the scheme and the + // separators), minus the username start we computed above. These are ASCII. + int prefix_end = parsed.CountCharactersBefore( + url_parse::Parsed::USERNAME, true); + for (int i = 0; i < prefix_end; i++) + url_string.push_back(spec[i]); + new_parsed->scheme = parsed.scheme; + new_parsed->username.reset(); + new_parsed->password.reset(); + + AppendFormattedHost(url, languages, &url_string, new_parsed); + + // Port. + if (parsed.port.is_nonempty()) { + url_string.push_back(':'); + for (int i = parsed.port.begin; i < parsed.port.end(); i++) + url_string.push_back(spec[i]); + } + + // Path and query both get the same general unescape & convert treatment. + AppendFormattedComponent(spec, parsed.path, &url_string, &new_parsed->path); + if (parsed.query.is_valid()) + url_string.push_back('?'); + AppendFormattedComponent(spec, parsed.query, &url_string, &new_parsed->query); + + // Reference is stored in valid, unescaped UTF-8, so we can just convert. + if (parsed.ref.is_valid()) { + url_string.push_back('#'); + if (parsed.ref.len > 0) + url_string.append(UTF8ToWide(std::string(&spec[parsed.ref.begin], + parsed.ref.len))); + } + + return url_string; +} + +} // namespace gfx. diff --git a/chrome/common/gfx/url_elider.h b/chrome/common/gfx/url_elider.h new file mode 100644 index 0000000..afa8a1c --- /dev/null +++ b/chrome/common/gfx/url_elider.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 CHROME_COMMON_GFX_URL_ELIDER_H__ +#define CHROME_COMMON_GFX_URL_ELIDER_H__ + +#include <windows.h> + +#include "base/basictypes.h" +#include "chrome/common/gfx/chrome_font.h" +#include "googleurl/src/gurl.h" + +namespace gfx { + +// This function takes a GURL object and elides it. It returns a string +// which composed of parts from subdomain, domain, path, filename and query. +// A "..." is added automatically at the end if the elided string is bigger +// than the available pixel width. For available pixel width = 0, empty +// string is returned. |languages| is a comma separted list of ISO 639 +// language codes and is used to determine what characters are understood +// by a user. It should come from |prefs::kAcceptLanguages|. +std::wstring ElideUrl(const GURL& url, + const ChromeFont& font, + int available_pixel_width, + const std::wstring& languages); + +std::wstring ElideText(const std::wstring& text, + const ChromeFont& font, + int available_pixel_width); + +} // namespace gfx. + +#endif // #ifndef CHROME_COMMON_GFX_URL_ELIDER_H__ diff --git a/chrome/common/gfx/url_elider_unittest.cc b/chrome/common/gfx/url_elider_unittest.cc new file mode 100644 index 0000000..b72f314 --- /dev/null +++ b/chrome/common/gfx/url_elider_unittest.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 "base/string_util.h" +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/gfx/url_elider.h" +#include "chrome/views/hwnd_view_container.h" +#include "chrome/views/label.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace gfx; + +namespace { + +const wchar_t kEllipsis[] = L"\x2026"; + +struct Testcase { + const std::string input; + const std::wstring output; +}; + +struct WideTestcase { + const std::wstring input; + const std::wstring output; +}; + +void RunTest(Testcase* testcases, size_t num_testcases) { + static const ChromeFont font; + for (size_t i = 0; i < num_testcases; ++i) { + const GURL url(testcases[i].input); + // Should we test with non-empty language list? + // That's kinda redundant with net_util_unittests. + EXPECT_EQ(testcases[i].output, + ElideUrl(url, font, font.GetStringWidth(testcases[i].output), + std::wstring())); + } +} + +} // namespace + +// Test eliding of commonplace URLs. +TEST(URLEliderTest, TestGeneralEliding) { + const std::wstring kEllipsisStr(kEllipsis); + Testcase testcases[] = { + {"http://www.google.com/intl/en/ads/", + L"http://www.google.com/intl/en/ads/"}, + {"http://www.google.com/intl/en/ads/", L"www.google.com/intl/en/ads/"}, + {"http://www.google.com/intl/en/ads/", + L"google.com/intl/" + kEllipsisStr + L"/ads/"}, + {"http://www.google.com/intl/en/ads/", + L"google.com/" + kEllipsisStr + L"/ads/"}, + {"http://www.google.com/intl/en/ads/", L"google.com/" + kEllipsisStr}, + {"http://www.google.com/intl/en/ads/", L"goog" + kEllipsisStr}, + {"https://subdomain.foo.com/bar/filename.html", + L"subdomain.foo.com/bar/filename.html"}, + {"https://subdomain.foo.com/bar/filename.html", + L"subdomain.foo.com/" + kEllipsisStr + L"/filename.html"}, + {"http://subdomain.foo.com/bar/filename.html", + kEllipsisStr + L"foo.com/" + kEllipsisStr + L"/filename.html"}, + {"http://www.google.com/intl/en/ads/?aLongQueryWhichIsNotRequired", + L"http://www.google.com/intl/en/ads/?aLongQ" + kEllipsisStr}, + }; + + RunTest(testcases, arraysize(testcases)); +} + +// Test eliding of empty strings, URLs with ports, passwords, queries, etc. +TEST(URLEliderTest, TestMoreEliding) { + const std::wstring kEllipsisStr(kEllipsis); + Testcase testcases[] = { + {"http://www.google.com/foo?bar", L"http://www.google.com/foo?bar"}, + {"http://xyz.google.com/foo?bar", L"xyz.google.com/foo?" + kEllipsisStr}, + {"http://xyz.google.com/foo?bar", L"xyz.google.com/foo" + kEllipsisStr}, + {"http://xyz.google.com/foo?bar", L"xyz.google.com/fo" + kEllipsisStr}, + {"http://a.b.com/pathname/c?d", L"a.b.com/" + kEllipsisStr + L"/c?d"}, + {"", L""}, + {"http://foo.bar..example.com...hello/test/filename.html", + L"foo.bar..example.com...hello/" + kEllipsisStr + L"/filename.html"}, + {"http://foo.bar../", L"http://foo.bar../"}, + {"http://xn--1lq90i.cn/foo", L"http://\x5317\x4eac.cn/foo"}, + {"http://me:mypass@secrethost.com:99/foo?bar#baz", + L"http://secrethost.com:99/foo?bar#baz"}, + {"http://me:mypass@ss%xxfdsf.com/foo", L"http://ss%25xxfdsf.com/foo"}, + {"mailto:elgoato@elgoato.com", L"mailto:elgoato@elgoato.com"}, + {"javascript:click(0)", L"javascript:click(0)"}, + {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename", + L"chess.eecs.berkeley.edu:4430/login/arbitfilename"}, + {"https://chess.eecs.berkeley.edu:4430/login/arbitfilename", + kEllipsisStr + L"berkeley.edu:4430/" + kEllipsisStr + L"/arbitfilename"}, + + // Unescaping. + {"http://www/%E4%BD%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0", + L"http://www/\x4f60\x597d?q=\x4f60\x597d#\x4f60"}, + + // Invalid unescaping for path. The ref will always be valid UTF-8. We don't + // bother to do too many edge cases, since these are handled by the escaper + // unittest. + {"http://www/%E4%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0", + L"http://www/%E4%A0%E5%A5%BD?q=\x4f60\x597d#\x4f60"}, + }; + + RunTest(testcases, arraysize(testcases)); +} + +// Test eliding of file: URLs. +TEST(URLEliderTest, TestFileURLEliding) { + const std::wstring kEllipsisStr(kEllipsis); + Testcase testcases[] = { + {"file:///C:/path1/path2/path3/filename", + L"file:///C:/path1/path2/path3/filename"}, + {"file:///C:/path1/path2/path3/filename", + L"C:/path1/path2/path3/filename"}, + {"file:///C:path1/path2/path3/filename", + L"C:/path1/path2/" + kEllipsisStr + L"/filename"}, + {"file:///C:path1/path2/path3/filename", + L"C:/path1/" + kEllipsisStr + L"/filename"}, + {"file:///C:path1/path2/path3/filename", + L"C:/" + kEllipsisStr + L"/filename"}, + {"file://filer/foo/bar/file", L"filer/foo/bar/file"}, + {"file://filer/foo/bar/file", L"filer/foo/" + kEllipsisStr + L"/file"}, + {"file://filer/foo/bar/file", L"filer/" + kEllipsisStr + L"/file"}, + }; + + RunTest(testcases, arraysize(testcases)); +} + +TEST(URLEliderTest, ElideTextLongStrings) { + const std::wstring kEllipsisStr(kEllipsis); + std::wstring data_scheme(L"data:text/plain,"); + + std::wstring ten_a(10, L'a'); + std::wstring hundred_a(100, L'a'); + std::wstring thousand_a(1000, L'a'); + std::wstring ten_thousand_a(10000, L'a'); + std::wstring hundred_thousand_a(100000, L'a'); + std::wstring million_a(1000000, L'a'); + + WideTestcase testcases[] = { + {data_scheme + ten_a, + data_scheme + ten_a}, + {data_scheme + hundred_a, + data_scheme + hundred_a}, + {data_scheme + thousand_a, + data_scheme + std::wstring(156, L'a') + kEllipsisStr}, + {data_scheme + ten_thousand_a, + data_scheme + std::wstring(156, L'a') + kEllipsisStr}, + {data_scheme + hundred_thousand_a, + data_scheme + std::wstring(156, L'a') + kEllipsisStr}, + {data_scheme + million_a, + data_scheme + std::wstring(156, L'a') + kEllipsisStr}, + }; + + for (size_t i = 0; i < arraysize(testcases); ++i) { + const ChromeFont font; + EXPECT_EQ(testcases[i].output, + ElideText(testcases[i].input, font, + font.GetStringWidth(testcases[i].output))); + EXPECT_EQ(kEllipsisStr, + ElideText(testcases[i].input, font, + font.GetStringWidth(kEllipsisStr))); + } +} diff --git a/chrome/common/gfx/utils.h b/chrome/common/gfx/utils.h new file mode 100644 index 0000000..3030c1d --- /dev/null +++ b/chrome/common/gfx/utils.h @@ -0,0 +1,37 @@ +// 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 CHROME_COMMON_GFX_UTILS_H__ +#define CHROME_COMMON_GFX_UTILS_H__ + +// twips are a unit of type measurement, and RichEdit controls use them +// to set offsets. +const int kTwipsPerInch = 1440; + +#endif // CHROME_COMMON_GFX_UTILS_H__ diff --git a/chrome/common/ipc_channel.cc b/chrome/common/ipc_channel.cc new file mode 100644 index 0000000..d469736 --- /dev/null +++ b/chrome/common/ipc_channel.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 <windows.h> +#include <sstream> + +#include "chrome/common/chrome_counters.h" +#include "chrome/common/ipc_channel.h" + +#include "base/logging.h" +#include "base/win_util.h" +#include "chrome/common/ipc_logging.h" +#include "chrome/common/ipc_message_utils.h" + +using namespace std; + +namespace IPC { + +//------------------------------------------------------------------------------ + +Channel::State::State() + : is_pending(false) { + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.hEvent = CreateEvent(NULL, // default security attributes + TRUE, // manual-reset event + TRUE, // initial state = signaled + NULL); // unnamed event object +} + +Channel::State::~State() { + if (overlapped.hEvent) + CloseHandle(overlapped.hEvent); +} + +//------------------------------------------------------------------------------ + +Channel::Channel(const wstring& channel_id, Mode mode, Listener* listener) + : pipe_(INVALID_HANDLE_VALUE), + listener_(listener), + waiting_connect_(mode == MODE_SERVER), + processing_incoming_(false) { + if (!CreatePipe(channel_id, mode)) { + // The pipe may have been closed already. + LOG(WARNING) << "Unable to create pipe named \"" << channel_id << + "\" in " << (mode == 0 ? "server" : "client") << " mode."; + } +} + +void Channel::Close() { + // make sure we are no longer watching the pipe events + MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL); + MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent, NULL); + + if (pipe_ != INVALID_HANDLE_VALUE) { + CloseHandle(pipe_); + pipe_ = INVALID_HANDLE_VALUE; + } + + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } +} + +bool Channel::Send(Message* message) { + chrome::Counters::ipc_send_counter().Increment(); +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sending message @" << message << " on channel @" << this + << " with type " << message->type() + << " (" << output_queue_.size() << " in queue)"; +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::current()->OnSendMessage(message, L""); +#endif + + output_queue_.push(message); + // ensure waiting to write + if (!waiting_connect_) { + if (!output_state_.is_pending) { + if (!ProcessOutgoingMessages()) + return false; + } else if (WaitForSingleObject(output_state_.overlapped.hEvent, 0) == + WAIT_OBJECT_0) { + OnObjectSignaled(output_state_.overlapped.hEvent); + } + } + + return true; +} + +const wstring Channel::PipeName(const wstring& channel_id) const { + wostringstream ss; + // XXX(darin): get application name from somewhere else + ss << L"\\\\.\\pipe\\chrome." << channel_id; + return ss.str(); +} + +bool Channel::CreatePipe(const wstring& channel_id, Mode mode) { + DCHECK(pipe_ == INVALID_HANDLE_VALUE); + const wstring pipe_name = PipeName(channel_id); + if (mode == MODE_SERVER) { + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.bInheritHandle = FALSE; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + if (!win_util::GetLogonSessionOnlyDACL( + reinterpret_cast<SECURITY_DESCRIPTOR**>( + &security_attributes.lpSecurityDescriptor))) { + NOTREACHED(); + } + + pipe_ = CreateNamedPipeW(pipe_name.c_str(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | + FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, // number of pipe instances + BUF_SIZE, // output buffer size (XXX tune) + BUF_SIZE, // input buffer size (XXX tune) + 5000, // timeout in milliseconds (XXX tune) + &security_attributes); + LocalFree(security_attributes.lpSecurityDescriptor); + } else { + pipe_ = CreateFileW(pipe_name.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | + FILE_FLAG_OVERLAPPED, + NULL); + } + if (pipe_ == INVALID_HANDLE_VALUE) { + // If this process is being closed, the pipe may be gone already. + LOG(WARNING) << "failed to create pipe: " << GetLastError(); + return false; + } + + // Create the Hello message to be sent when Connect is called + scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE, + IPC::Message::PRIORITY_NORMAL)); + if (!m->WriteInt(GetCurrentProcessId())) { + CloseHandle(pipe_); + pipe_ = INVALID_HANDLE_VALUE; + return false; + } + + output_queue_.push(m.release()); + return true; +} + +bool Channel::Connect() { + DLOG(WARNING) << "Connect called twice"; + + if (pipe_ == INVALID_HANDLE_VALUE) + return false; + + // Check to see if there is a client connected to our pipe... + if (waiting_connect_) + ProcessConnection(); + + if (!input_state_.is_pending) { + // complete setup asynchronously. to do that, we force the input state + // to be signaled, which will cause us to be called back from the event + // queue. by not setting input_state_.is_pending to true, we indicate + // to OnObjectSignaled that this is the special initialization signal. + + SetEvent(input_state_.overlapped.hEvent); + MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, this); + } + + if (!waiting_connect_) + ProcessOutgoingMessages(); + return true; +} + +bool Channel::ProcessConnection() { + input_state_.is_pending = false; + MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL); + + // Do we have a client connected to our pipe? + DCHECK(pipe_ != INVALID_HANDLE_VALUE); + BOOL ok = ConnectNamedPipe(pipe_, &input_state_.overlapped); + + DWORD err = GetLastError(); + if (ok) { + // Uhm, the API documentation says that this function should never + // return success when used in overlapped mode. + NOTREACHED(); + return false; + } + + switch (err) { + case ERROR_IO_PENDING: + input_state_.is_pending = true; + MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, this); + break; + case ERROR_PIPE_CONNECTED: + waiting_connect_ = false; + break; + default: + NOTREACHED(); + return false; + } + + return true; +} + +bool Channel::ProcessIncomingMessages() { + DWORD bytes_read = 0; + + MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, NULL); + + if (input_state_.is_pending) { + input_state_.is_pending = false; + + BOOL ok = GetOverlappedResult(pipe_, + &input_state_.overlapped, + &bytes_read, + FALSE); + if (!ok || bytes_read == 0) { + DWORD err = GetLastError(); + // TODO(pkasting): http://b/119851 We can get ERR_BROKEN_PIPE here if the + // renderer crashes. We should handle this cleanly. + LOG(ERROR) << "pipe error: " << err; + return false; + } + } else { + // this happens at channel initialization + ResetEvent(input_state_.overlapped.hEvent); + } + + for (;;) { + if (bytes_read == 0) { + // read from pipe... + BOOL ok = ReadFile(pipe_, + input_buf_, + BUF_SIZE, + &bytes_read, + &input_state_.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + MessageLoop::current()->WatchObject(input_state_.overlapped.hEvent, + this); + input_state_.is_pending = true; + return true; + } + LOG(ERROR) << "pipe error: " << err; + return false; + } + } + DCHECK(bytes_read); + + // process messages from input buffer + + const char* p, *end; + if (input_overflow_buf_.empty()) { + p = input_buf_; + end = p + bytes_read; + } else { + if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) { + input_overflow_buf_.clear(); + LOG(ERROR) << "IPC message is too big"; + return false; + } + input_overflow_buf_.append(input_buf_, bytes_read); + p = input_overflow_buf_.data(); + end = p + input_overflow_buf_.size(); + } + + while (p < end) { + const char* message_tail = Message::FindNext(p, end); + if (message_tail) { + int len = static_cast<int>(message_tail - p); + const Message m(p, len); +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "received message on channel @" << this << " with type " << + m.type(); +#endif + if (m.routing_id() == MSG_ROUTING_NONE && + m.type() == HELLO_MESSAGE_TYPE) { + // The Hello message contains only the process id. + listener_->OnChannelConnected(MessageIterator(m).NextInt()); + } else { + listener_->OnMessageReceived(m); + } + p = message_tail; + } else { + // last message is partial + break; + } + } + input_overflow_buf_.assign(p, end - p); + + bytes_read = 0; // get more data + } + + return true; +} + +bool Channel::ProcessOutgoingMessages() { + DCHECK(!waiting_connect_); // Why are we trying to send messages if there's + // no connection? + DWORD bytes_written; + + if (output_state_.is_pending) { + MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent, NULL); + output_state_.is_pending = false; + BOOL ok = GetOverlappedResult(pipe_, + &output_state_.overlapped, + &bytes_written, + FALSE); + if (!ok || bytes_written == 0) { + DWORD err = GetLastError(); + LOG(ERROR) << "pipe error: " << err; + return false; + } + // message was sent + DCHECK(!output_queue_.empty()); + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } + + while (!output_queue_.empty()) { + // write to pipe... + Message* m = output_queue_.front(); + BOOL ok = WriteFile(pipe_, + m->data(), + m->size(), + &bytes_written, + &output_state_.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + MessageLoop::current()->WatchObject(output_state_.overlapped.hEvent, + this); + output_state_.is_pending = true; + +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sent pending message @" << m << " on channel @" << this << + " with type " << m->type(); +#endif + + return true; + } + LOG(ERROR) << "pipe error: " << err; + return false; + } + DCHECK(bytes_written == m->size()); + output_queue_.pop(); + +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sent message @" << m << " on channel @" << this << + " with type " << m->type(); +#endif + + delete m; + } + + return true; +} + +bool Channel::ProcessPendingMessages(DWORD max_wait_msec) { + return false; + // TODO(darin): this code is broken and leads to busy waiting +#if 0 + DCHECK(max_wait_msec <= 0x7FFFFFFF || max_wait_msec == INFINITE); + + HANDLE events[] = { + input_state_.overlapped.hEvent, + output_state_.overlapped.hEvent + }; + // Only deal with output messages if we have a connection on which to send + const int wait_count = waiting_connect_ ? 1 : 2; + DCHECK(wait_count <= _countof(events)); + + if (max_wait_msec) { + DWORD result = WaitForMultipleObjects(wait_count, events, FALSE, + max_wait_msec); + if (result == WAIT_TIMEOUT) + return true; + } + + bool rv = true; + for (int i = 0; i < wait_count; ++i) { + if (WaitForSingleObject(events[i], 0) == WAIT_OBJECT_0) { + if (i == 0 && processing_incoming_) { + rv = false; + DLOG(WARNING) << "Would recurse into ProcessIncomingMessages"; + } else { + OnObjectSignaled(events[i]); + } + } + } + return rv; +#endif +} + +void Channel::OnObjectSignaled(HANDLE object) { + bool ok; + if (object == input_state_.overlapped.hEvent) { + if (waiting_connect_) { + ProcessConnection(); + // We may have some messages queued up to send... + if (!output_queue_.empty() && !output_state_.is_pending) + ProcessOutgoingMessages(); + if (input_state_.is_pending) + return; + // else, fall-through and look for incoming messages... + } + // we don't support recursion through OnMessageReceived yet! + DCHECK(!processing_incoming_); + processing_incoming_ = true; + ok = ProcessIncomingMessages(); + processing_incoming_ = false; + } else { + DCHECK(object == output_state_.overlapped.hEvent); + ok = ProcessOutgoingMessages(); + } + if (!ok) { + Close(); + listener_->OnChannelError(); + } +} + +//------------------------------------------------------------------------------ + +} diff --git a/chrome/common/ipc_channel.h b/chrome/common/ipc_channel.h new file mode 100644 index 0000000..bf71e02 --- /dev/null +++ b/chrome/common/ipc_channel.h @@ -0,0 +1,187 @@ +// 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 CHROME_COMMON_IPC_CHANNEL_H__ +#define CHROME_COMMON_IPC_CHANNEL_H__ + +#include <queue> + +#include "base/message_loop.h" +#include "chrome/common/ipc_message.h" + +namespace IPC { + +//------------------------------------------------------------------------------ + +class Channel : public MessageLoop::Watcher, + public Message::Sender { + // Security tests need access to the pipe handle. + friend class ChannelTest; + + public: + // Implemented by consumers of a Channel to receive messages. + class Listener { + public: + // Called when a message is received. + virtual void OnMessageReceived(const Message& message) = 0; + + // Called when the channel is connected and we have received the internal + // Hello message from the peer. + virtual void OnChannelConnected(int32 peer_pid) {} + + // Called when an error is detected that causes the channel to close. + // This method is not called when a channel is closed normally. + virtual void OnChannelError() {} + }; + + enum Mode { + MODE_SERVER, + MODE_CLIENT + }; + + // The maximum message size in bytes. Attempting to receive a + // message of this size or bigger results in a channel error. + enum { + kMaximumMessageSize = 256 * 1024 * 1024 + }; + + // Initialize a Channel. + // + // @param channel_id + // Identifies the communication Channel. + // @param mode + // Specifies whether this Channel is to operate in server mode or client + // mode. In server mode, the Channel is responsible for setting up the + // IPC object, whereas in client mode, the Channel merely connects + // to the already established IPC object. + // @param listener + // Receives a callback on the current thread for each newly received + // message. + // + Channel(const std::wstring& channel_id, Mode mode, Listener* listener); + + ~Channel() { Close(); } + + // Connect the pipe. On the server side, this will initiate + // waiting for connections. On the client, it attempts to + // connect to a pre-existing pipe. Note, calling Connect() + // will not block the calling thread and may complete + // asynchronously. + bool Connect(); + + // Close this Channel explicitly. May be called multiple times. + void Close(); + + // Modify the Channel's listener. + void set_listener(Listener* listener) { listener_ = listener; } + + // Send a message over the Channel to the listener on the other end. + // + // @param message + // The Message to send, which must be allocated using operator new. This + // object will be deleted once the contents of the Message have been sent. + // + // FIXME bug 551500: the channel does not notice failures, so if the + // renderer crashes, it will silently succeed, leaking the parameter. + // At least the leak will be fixed by... + // + virtual bool Send(Message* message); + + // Process any pending incoming and outgoing messages. Wait for at most + // max_wait_msec for pending messages if there are none. Returns true if + // there were no pending messages or if pending messages were successfully + // processed. Returns false if there are pending messages that cannot be + // processed for some reason (e.g., because ProcessIncomingMessages would be + // re-entered). + // TODO(darin): Need a better way of dealing with the recursion problem. + bool ProcessPendingMessages(DWORD max_wait_msec); + + private: + const std::wstring PipeName(const std::wstring& channel_id) const; + bool CreatePipe(const std::wstring& channel_id, Mode mode); + bool ProcessConnection(); + bool ProcessIncomingMessages(); + bool ProcessOutgoingMessages(); + + // MessageLoop::Watcher implementation + virtual void OnObjectSignaled(HANDLE object); + + private: + enum { + BUF_SIZE = 4096 + }; + + struct State { + State(); + ~State(); + OVERLAPPED overlapped; + bool is_pending; + }; + + State input_state_; + State output_state_; + + HANDLE pipe_; + Listener* listener_; + + // Messages to be sent are queued here. + std::queue<Message*> output_queue_; + + // We read from the pipe into this buffer + char input_buf_[BUF_SIZE]; + + // Large messages that span multiple pipe buffers, get built-up using + // this buffer. + std::string input_overflow_buf_; + + // In server-mode, we have to wait for the client to connect before we + // can begin reading. We make use of the input_state_ when performing + // the connect operation in overlapped mode. + bool waiting_connect_; + + // This flag is set when processing incoming messages. It is used to + // avoid recursing through ProcessIncomingMessages, which could cause + // problems. TODO(darin): make this unnecessary + bool processing_incoming_; + + // The Hello message is internal to the Channel class. It is sent + // by the peer when the channel is connected. The message contains + // just the process id (pid). The message has a special routing_id + // (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE). + enum { + HELLO_MESSAGE_TYPE = MAXWORD // Maximum value of message type (WORD), + // to avoid conflicting with normal + // message types, which are enumeration + // constants starting from 0. + }; +}; + +} + +#endif // CHROME_COMMON_IPC_CHANNEL_H__ diff --git a/chrome/common/ipc_channel_proxy.cc b/chrome/common/ipc_channel_proxy.cc new file mode 100644 index 0000000..9d6b473 --- /dev/null +++ b/chrome/common/ipc_channel_proxy.cc @@ -0,0 +1,286 @@ +// 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/message_loop.h" +#include "base/thread.h" +#include "chrome/common/ipc_channel_proxy.h" +#include "chrome/common/ipc_logging.h" +#include "chrome/common/ipc_message_utils.h" + +namespace IPC { + +//----------------------------------------------------------------------------- + +ChannelProxy::Context::Context(Channel::Listener* listener, + MessageFilter* filter, + MessageLoop* ipc_message_loop) + : listener_message_loop_(MessageLoop::current()), + listener_(listener), + ipc_message_loop_(ipc_message_loop), + channel_(NULL) { + if (filter) + filters_.push_back(filter); +} + +void ChannelProxy::Context::CreateChannel(const std::wstring& id, + const Channel::Mode& mode) { + DCHECK(channel_ == NULL); + channel_id_ = id; + channel_ = new Channel(id, mode, this); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnMessageReceived(const Message& message) { +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging* logger = Logging::current(); + if (logger->Enabled()) + logger->OnPreDispatchMessage(message); +#endif + + for (size_t i = 0; i < filters_.size(); ++i) { + if (filters_[i]->OnMessageReceived(message)) { +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(message, channel_id_); +#endif + return; + } + } + + // NOTE: This code relies on the listener's message loop not going away while + // this thread is active. That should be a reasonable assumption, but it + // feels risky. We may want to invent some more indirect way of referring to + // a MessageLoop if this becomes a problem. + + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchMessage, message)); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) { + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnChannelConnected(peer_pid); + + // See above comment about using listener_message_loop_ here. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchConnected, peer_pid)); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelError() { + // See above comment about using listener_message_loop_ here. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchError)); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnOpenChannel() { + DCHECK(channel_ != NULL); + + // Assume a reference to ourselves on behalf of this thread. This reference + // will be released when we are closed. + AddRef(); + + if (!channel_->Connect()) { + OnChannelError(); + return; + } + + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnFilterAdded(channel_); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnCloseChannel() { + // It's okay for IPC::ChannelProxy::Close to be called more than once, which + // would result in this branch being taken. + if (!channel_) + return; + + for (size_t i = 0; i < filters_.size(); ++i) { + filters_[i]->OnChannelClosing(); + filters_[i]->OnFilterRemoved(); + } + + // We don't need the filters anymore. + filters_.clear(); + + delete channel_; + channel_ = NULL; + + // Balance with the reference taken during startup. This may result in + // self-destruction. + Release(); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnSendMessage(Message* message) { + if (!channel_->Send(message)) + OnChannelError(); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnAddFilter(MessageFilter* filter) { + filters_.push_back(filter); + + // If the channel has already been created, then we need to send this message + // so that the filter gets access to the Channel. + if (channel_) + filter->OnFilterAdded(channel_); + + // Balances the AddRef in ChannelProxy::AddFilter. + filter->Release(); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) { + for (size_t i = 0; i < filters_.size(); ++i) { + if (filters_[i].get() == filter) { + filter->OnFilterRemoved(); + filters_.erase(filters_.begin() + i); + return; + } + } + + NOTREACHED() << "filter to be removed not found"; +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchMessage(const Message& message) { + if (!listener_) + return; + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging* logger = Logging::current(); + if (message.type() == IPC_LOGGING_ID) { + logger->OnReceivedLoggingMessage(message); + return; + } + + if (logger->Enabled()) + logger->OnPreDispatchMessage(message); +#endif + + listener_->OnMessageReceived(message); + +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(message, channel_id_); +#endif +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchConnected(int32 peer_pid) { + if (listener_) + listener_->OnChannelConnected(peer_pid); +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchError() { + if (listener_) + listener_->OnChannelError(); +} + +//----------------------------------------------------------------------------- + +ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, MessageFilter* filter, + MessageLoop* ipc_thread) + : context_(new Context(listener, filter, ipc_thread)) { + Init(channel_id, mode, ipc_thread, true); +} + +ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, MessageFilter* filter, + MessageLoop* ipc_thread, Context* context, + bool create_pipe_now) + : context_(context) { + Init(channel_id, mode, ipc_thread, create_pipe_now); +} + +void ChannelProxy::Init(const std::wstring& channel_id, Channel::Mode mode, + MessageLoop* ipc_thread_loop, bool create_pipe_now) { + if (create_pipe_now) { + // Create the channel immediately. This effectively sets up the + // low-level pipe so that the client can connect. Without creating + // the pipe immediately, it is possible for a listener to attempt + // to connect and get an error since the pipe doesn't exist yet. + context_->CreateChannel(channel_id, mode); + } else { + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::CreateChannel, channel_id, mode)); + } + + // complete initialization on the background thread + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnOpenChannel)); +} + +void ChannelProxy::Close() { + // Clear the backpointer to the listener so that any pending calls to + // Context::OnDispatchMessage or OnDispatchError will be ignored. It is + // possible that the channel could be closed while it is receiving messages! + context_->clear(); + + if (MessageLoop::current() == context_->ipc_message_loop()) { + // We're being destructed on the IPC thread, so no need to use the message + // loop as it might go away. + context_->OnCloseChannel(); + } else { + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnCloseChannel)); + } +} + +bool ChannelProxy::Send(Message* message) { +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::current()->OnSendMessage(message, context_->channel_id()); +#endif + + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnSendMessage, message)); + return true; +} + +void ChannelProxy::AddFilter(MessageFilter* filter) { + // We want to addref the filter to prevent it from + // being destroyed before the OnAddFilter call is invoked. + filter->AddRef(); + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnAddFilter, filter)); +} + +void ChannelProxy::RemoveFilter(MessageFilter* filter) { + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnRemoveFilter, filter)); +} + +//----------------------------------------------------------------------------- + +} // namespace IPC diff --git a/chrome/common/ipc_channel_proxy.h b/chrome/common/ipc_channel_proxy.h new file mode 100644 index 0000000..ca8abb4 --- /dev/null +++ b/chrome/common/ipc_channel_proxy.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 CHROME_COMMON_IPC_CHANNEL_PROXY_H__ +#define CHROME_COMMON_IPC_CHANNEL_PROXY_H__ + +#include <vector> +#include "base/lock.h" +#include "base/ref_counted.h" +#include "chrome/common/ipc_channel.h" + +namespace IPC { + +//----------------------------------------------------------------------------- +// IPC::ChannelProxy +// +// This class is a helper class that is useful when you wish to run an IPC +// channel on a background thread. It provides you with the option of either +// handling IPC messages on that background thread or having them dispatched to +// your main thread (the thread on which the IPC::ChannelProxy is created). +// +// The API for an IPC::ChannelProxy is very similar to that of an IPC::Channel. +// When you send a message to an IPC::ChannelProxy, the message is routed to +// the background thread, where it is then passed to the IPC::Channel's Send +// method. This means that you can send a message from your thread and your +// message will be sent over the IPC channel when possible instead of being +// delayed until your thread returns to its message loop. (Often IPC messages +// will queue up on the IPC::Channel when there is a lot of traffic, and the +// channel will not get cycles to flush its message queue until the thread, on +// which it is running, returns to its message loop.) +// +// An IPC::ChannelProxy can have a MessageFilter associated with it, which will +// be notified of incoming messages on the IPC::Channel's thread. This gives +// the consumer of IPC::ChannelProxy the ability to respond to incoming +// messages on this background thread instead of on their own thread, which may +// be bogged down with other processing. The result can be greatly improved +// latency for messages that can be handled on a background thread. +// +// The consumer of IPC::ChannelProxy is responsible for allocating the Thread +// instance where the IPC::Channel will be created and operated. +// +class ChannelProxy : public Message::Sender { + public: + // A class that receives messages on the thread where the IPC channel is + // running. It can choose to prevent the default action for an IPC message. + class MessageFilter : public base::RefCountedThreadSafe<MessageFilter> { + public: + virtual ~MessageFilter() {} + + // Called on the background thread to provide the filter with access to the + // channel. Called when the IPC channel is initialized or when AddFilter + // is called if the channel is already initialized. + virtual void OnFilterAdded(Channel* channel) {} + + // Called on the background thread when the filter has been removed from + // the ChannelProxy and when the Channel is closing. After a filter is + // removed, it will not be called again. + virtual void OnFilterRemoved() {} + + // Called to inform the filter that the IPC channel is connected and we + // have received the internal Hello message from the peer. + virtual void OnChannelConnected(int32 peer_pid) {} + + // Called to inform the filter that the IPC channel will be destroyed. + // OnFilterRemoved is called immediately after this. + virtual void OnChannelClosing() {} + + // Return true to indicate that the message was handled, or false to let + // the message be handled in the default way. + virtual bool OnMessageReceived(const Message& message) { + return false; + } + }; + + // Initializes a channel proxy. The channel_id and mode parameters are + // passed directly to the underlying IPC::Channel. The listener is called on + // the thread that creates the ChannelProxy. The filter's OnMessageReceived + // method is called on the thread where the IPC::Channel is running. The + // filter may be null if the consumer is not interested in handling messages + // on the background thread. Any message not handled by the filter will be + // dispatched to the listener. The given message loop indicates where the + // IPC::Channel should be created. + ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, MessageFilter* filter, + MessageLoop* ipc_thread_loop); + + ~ChannelProxy() { + Close(); + } + + // Close the IPC::Channel. This operation completes asynchronously, once the + // background thread processes the command to close the channel. It is ok to + // call this method multiple times. Redundant calls are ignored. + // + // WARNING: The MessageFilter object held by the ChannelProxy is also + // released asynchronously, and it may in fact have its final reference + // released on the background thread. The caller should be careful to deal + // with / allow for this possibility. + void Close(); + + // Send a message asynchronously. The message is routed to the background + // thread where it is passed to the IPC::Channel's Send method. + virtual bool Send(Message* message); + + // Used to intercept messages as they are received on the background thread. + // + // Ordinarily, messages sent to the ChannelProxy are routed to the matching + // listener on the worker thread. This API allows code to intercept messages + // before they are sent to the worker thread. + void AddFilter(MessageFilter* filter); + void RemoveFilter(MessageFilter* filter); + + // TODO(darin): kill this + bool ProcessPendingMessages(uint32 max_wait_msec) { + return false; + } + + protected: + Channel::Listener* listener() const { return context_->listener(); } + + class Context; + // A subclass uses this constructor if it needs to add more information + // to the internal state. If create_pipe_now is true, the pipe is created + // immediately. Otherwise it's created on the IO thread. + ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, MessageFilter* filter, + MessageLoop* ipc_thread_loop, Context* context, + bool create_pipe_now); + + // Used internally to hold state that is referenced on the IPC thread. + class Context : public base::RefCountedThreadSafe<Context>, + public Channel::Listener { + public: + Context(Channel::Listener* listener, MessageFilter* filter, + MessageLoop* ipc_thread); + virtual ~Context() { } + + protected: + // IPC::Channel::Listener methods: + virtual void OnMessageReceived(const Message& message); + virtual void OnChannelConnected(int32 peer_pid); + virtual void OnChannelError(); + + Channel::Listener* listener() const { return listener_; } + const std::wstring& channel_id() const { return channel_id_; } + + private: + friend class ChannelProxy; + // Create the Channel + void CreateChannel(const std::wstring& id, const Channel::Mode& mode); + + // Methods called via InvokeLater: + void OnOpenChannel(); + void OnCloseChannel(); + void OnSendMessage(Message* message_ptr); + void OnAddFilter(MessageFilter* filter); + void OnRemoveFilter(MessageFilter* filter); + void OnDispatchMessage(const Message& message); + void OnDispatchConnected(int32 peer_pid); + void OnDispatchError(); + + MessageLoop* ipc_message_loop() const { return ipc_message_loop_; } + + // Called on the consumers thread when the ChannelProxy is closed. At that + // point the consumer is telling us that they don't want to receive any + // more messages, so we honor that wish by forgetting them! + void clear() { listener_ = NULL; } + + MessageLoop* listener_message_loop_; + Channel::Listener* listener_; + + // List of filters. This is only accessed on the IPC thread. + std::vector<scoped_refptr<MessageFilter>> filters_; + MessageLoop* ipc_message_loop_; + Channel* channel_; + std::wstring channel_id_; + }; + + Context* context() { return context_; } + + private: + void Init(const std::wstring& channel_id, Channel::Mode mode, + MessageLoop* ipc_thread_loop, bool create_pipe_now); + + // By maintaining this indirection (ref-counted) to our internal state, we + // can safely be destroyed while the background thread continues to do stuff + // that involves this data. + scoped_refptr<Context> context_; +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_CHANNEL_PROXY_H__ diff --git a/chrome/common/ipc_fuzzing_tests.cc b/chrome/common/ipc_fuzzing_tests.cc new file mode 100644 index 0000000..8d2aebb --- /dev/null +++ b/chrome/common/ipc_fuzzing_tests.cc @@ -0,0 +1,443 @@ +// 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 <stdio.h> +#include <iostream> +#include <string> +#include <sstream> + +#include "chrome/common/ipc_tests.h" + +#include "chrome/common/ipc_channel.h" +#include "chrome/common/ipc_channel_proxy.h" +#include "chrome/common/ipc_message_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(IPCMessageIntegrity, ReadBeyondBufferStr) { + //This was BUG 984408. + uint32 v1 = kuint32max - 1; + int v2 = 666; + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(v1)); + EXPECT_TRUE(m.WriteInt(v2)); + + void* iter = NULL; + std::string vs; + EXPECT_FALSE(m.ReadString(&iter, &vs)); +} + +TEST(IPCMessageIntegrity, ReadBeyondBufferWStr) { + //This was BUG 984408. + uint32 v1 = kuint32max - 1; + int v2 = 777; + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(v1)); + EXPECT_TRUE(m.WriteInt(v2)); + + void* iter = NULL; + std::wstring vs; + EXPECT_FALSE(m.ReadWString(&iter, &vs)); +} + +TEST(IPCMessageIntegrity, ReadBytesBadIterator) { + // This was BUG 1035467. + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(1)); + EXPECT_TRUE(m.WriteInt(2)); + + void* iter = NULL; + const char* data = NULL; + EXPECT_FALSE(m.ReadBytes(&iter, &data, sizeof(int))); +} + +TEST(IPCMessageIntegrity, ReadVectorNegativeSize) { + // A slight variation of BUG 984408. Note that the pickling of vector<char> + // has a specialized template which is not vulnerable to this bug. So here + // try to hit the non-specialized case vector<P>. + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(-1)); // This is the count of elements. + EXPECT_TRUE(m.WriteInt(1)); + EXPECT_TRUE(m.WriteInt(2)); + EXPECT_TRUE(m.WriteInt(3)); + + std::vector<double> vec; + void* iter = 0; + EXPECT_FALSE(ReadParam(&m, &iter, &vec)); +} + +TEST(IPCMessageIntegrity, ReadVectorTooLarge1) { + // This was BUG 1006367. This is the large but positive length case. Again + // we try to hit the non-specialized case vector<P>. + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(0x21000003)); // This is the count of elements. + EXPECT_TRUE(m.WriteInt64(1)); + EXPECT_TRUE(m.WriteInt64(2)); + + std::vector<int64> vec; + void* iter = 0; + EXPECT_FALSE(ReadParam(&m, &iter, &vec)); +} + +TEST(IPCMessageIntegrity, ReadVectorTooLarge2) { + // This was BUG 1006367. This is the large but positive with an additional + // integer overflow when computing the actual byte size. Again we try to hit + // the non-specialized case vector<P>. + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(0x71000000)); // This is the count of elements. + EXPECT_TRUE(m.WriteInt64(1)); + EXPECT_TRUE(m.WriteInt64(2)); + + std::vector<int64> vec; + void* iter = 0; + EXPECT_FALSE(ReadParam(&m, &iter, &vec)); +} + +// Typically the ipc_message_macros files is included twice but here we only +// include it once in 'enum mode' because we want more control of the class +// definitions. +#define IPC_MESSAGE_MACROS_ENUMS +#include "chrome/common/ipc_message_macros.h" + +enum IPCMessageIds { + UNUSED_IPC_TYPE, + SERVER_FIRST_IPC_TYPE, // 1st Test message tag. + SERVER_SECOND_IPC_TYPE, // 2nd Test message tag. + SERVER_THIRD_IPC_TYPE, // 3rd Test message tag. + CLIENT_MALFORMED_IPC, // Sent to client if server detects bad message. + CLIENT_UNHANDLED_IPC // Sent to client if server detects unhanded IPC. +}; + +// Generic message class that is an int followed by a wstring. +class MsgClassIS : public IPC::MessageWithTuple< Tuple2<int, std::wstring> > { + public: + enum { ID = SERVER_FIRST_IPC_TYPE }; + MsgClassIS(const int& arg1, const std::wstring& arg2) + : IPC::MessageWithTuple< Tuple2<int, std::wstring> >( + MSG_ROUTING_CONTROL, ID, MakeTuple(arg1, arg2)) {} +}; + +// Generic message class that is a wstring followed by an int. +class MsgClassSI : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > { + public: + enum { ID = SERVER_SECOND_IPC_TYPE }; + MsgClassSI(const std::wstring& arg1, const int& arg2) + : IPC::MessageWithTuple< Tuple2<std::wstring, int> >( + MSG_ROUTING_CONTROL, ID, MakeTuple(arg1, arg2)) {} +}; + +// Message to create a mutex in the IPC server, using the received name. +class MsgDoMutex : public IPC::MessageWithTuple< Tuple2<std::wstring, int> > { + public: + enum { ID = SERVER_THIRD_IPC_TYPE }; + MsgDoMutex(const std::wstring& mutex_name, const int& unused) + : IPC::MessageWithTuple< Tuple2<std::wstring, int> >( + MSG_ROUTING_CONTROL, ID, MakeTuple(mutex_name, unused)) {} +}; + +class SimpleListener : public IPC::Channel::Listener { + public: + SimpleListener() : other_(NULL) { + } + void Init(IPC::Message::Sender* s) { + other_ = s; + } + protected: + IPC::Message::Sender* other_; +}; + +enum { + FUZZER_ROUTING_ID = 5 +}; + +// The fuzzer server class. It runs in a child process and expects +// only two IPC calls; after that it exits the message loop which +// terminates the child process. +class FuzzerServerListener : public SimpleListener { + public: + FuzzerServerListener() : message_count_(2), pending_messages_(0) { + } + virtual void OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + ++pending_messages_; + IPC_BEGIN_MESSAGE_MAP(FuzzerServerListener, msg) + IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage) + IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage) + IPC_END_MESSAGE_MAP() + if (pending_messages_) { + // Probably a problem de-serializing the message. + ReplyMsgNotHandled(msg.type()); + } + } + } + + private: + void OnMsgClassISMessage(int value, const std::wstring& text) { + UseData(MsgClassIS::ID, value, text); + RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassIS::ID, value); + Cleanup(); + } + + void OnMsgClassSIMessage(const std::wstring& text, int value) { + UseData(MsgClassSI::ID, value, text); + RoundtripAckReply(FUZZER_ROUTING_ID, MsgClassSI::ID, value); + Cleanup(); + } + + bool RoundtripAckReply(int routing, int type_id, int reply) { + IPC::Message* message = new IPC::Message(routing, type_id, + IPC::Message::PRIORITY_NORMAL); + message->WriteInt(reply + 1); + message->WriteInt(reply); + return other_->Send(message); + } + + void Cleanup() { + --message_count_; + --pending_messages_; + if (0 == message_count_) + MessageLoop::current()->Quit(); + } + + void ReplyMsgNotHandled(int type_id) { + RoundtripAckReply(FUZZER_ROUTING_ID, CLIENT_UNHANDLED_IPC, type_id); + Cleanup(); + } + + void UseData(int caller, int value, const std::wstring& text) { + std::wostringstream wos; + wos << L"IPC fuzzer:" << caller << " [" << value << L" " << text << L"]\n"; + std::wstring output = wos.str(); + ::OutputDebugStringW(output.c_str()); + }; + + int message_count_; + int pending_messages_; +}; + +class FuzzerClientListener : public SimpleListener { + public: + FuzzerClientListener() : last_msg_(NULL) { + } + + virtual void OnMessageReceived(const IPC::Message& msg) { + last_msg_ = new IPC::Message(msg); + MessageLoop::current()->Quit(); + } + + bool ExpectMessage(int value, int type_id) { + if (!MsgHandlerInternal(type_id)) + return false; + int msg_value1 = 0; + int msg_value2 = 0; + void* iter = NULL; + if (!last_msg_->ReadInt(&iter, &msg_value1)) + return false; + if (!last_msg_->ReadInt(&iter, &msg_value2)) + return false; + if ((msg_value2 + 1) != msg_value1) + return false; + if (msg_value2 != value) + return false; + + delete last_msg_; + last_msg_ = NULL; + return true; + } + + bool ExpectMsgNotHandled(int type_id) { + return ExpectMessage(type_id, CLIENT_UNHANDLED_IPC); + } + + private: + bool MsgHandlerInternal(int type_id) { + MessageLoop::current()->Run(); + if (NULL == last_msg_) + return false; + if (FUZZER_ROUTING_ID != last_msg_->routing_id()) + return false; + return (type_id == last_msg_->type()); + }; + + IPC::Message* last_msg_; +}; + +bool RunFuzzServer() { + FuzzerServerListener listener; + IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_SERVER, &listener); + chan.Connect(); + listener.Init(&chan); + MessageLoop::current()->Run(); + return true; +} + +// This test makes sure that the FuzzerClientListener and FuzzerServerListener +// are working properly by generating two well formed IPC calls. +TEST(IPCFuzzingTest, SanityTest) { + HANDLE server_process = SpawnChild(FUZZER_SERVER); + ASSERT_TRUE(server_process); + ::Sleep(1000); + FuzzerClientListener listener; + IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_CLIENT, + &listener); + ASSERT_TRUE(chan.Connect()); + listener.Init(&chan); + + IPC::Message* msg = NULL; + int value = 43; + msg = new MsgClassIS(value, L"expect 43"); + chan.Send(msg); + EXPECT_TRUE(listener.ExpectMessage(value, MsgClassIS::ID)); + + msg = new MsgClassSI(L"expect 44", ++value); + chan.Send(msg); + EXPECT_TRUE(listener.ExpectMessage(value, MsgClassSI::ID)); + + ASSERT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(server_process, 5000)); +} + +// This test uses a payload that is smaller than expected. +// This generates an error while unpacking the IPC buffer which in +// In debug this triggers an assertion and in release it is ignored(!!). Right +// after we generate another valid IPC to make sure framing is working +// properly. +#ifdef NDEBUG +TEST(IPCFuzzingTest, MsgBadPayloadShort) { + HANDLE server_process = SpawnChild(FUZZER_SERVER); + ASSERT_TRUE(server_process); + ::Sleep(1000); + FuzzerClientListener listener; + IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_CLIENT, + &listener); + ASSERT_TRUE(chan.Connect()); + listener.Init(&chan); + + IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(666); + chan.Send(msg); + EXPECT_TRUE(listener.ExpectMsgNotHandled(MsgClassIS::ID)); + + msg = new MsgClassSI(L"expect one", 1); + chan.Send(msg); + EXPECT_TRUE(listener.ExpectMessage(1, MsgClassSI::ID)); + + ASSERT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(server_process, 5000)); +} +#endif // NDEBUG + +// This test uses a payload that has the wrong arguments, but so the payload +// size is big enough so the unpacking routine does not generate an error as +// in the case of MsgBadPayloadShort test. +// This test does not pinpoint a flaw (per se) as by design we don't carry +// type information on the IPC message. +TEST(IPCFuzzingTest, MsgBadPayloadArgs) { + HANDLE server_process = SpawnChild(FUZZER_SERVER); + ASSERT_TRUE(server_process); + ::Sleep(1000); + FuzzerClientListener listener; + IPC::Channel chan(kFuzzerChannel, IPC::Channel::MODE_CLIENT, + &listener); + ASSERT_TRUE(chan.Connect()); + listener.Init(&chan); + + IPC::Message* msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(2); + msg->WriteInt(0x64); + msg->WriteInt(0); + msg->WriteInt(0x65); + chan.Send(msg); + EXPECT_TRUE(listener.ExpectMessage(0, MsgClassSI::ID)); + + msg = new MsgClassIS(3, L"expect three"); + chan.Send(msg); + EXPECT_TRUE(listener.ExpectMessage(3, MsgClassIS::ID)); + + ASSERT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(server_process, 5000)); +} + +// This class is for testing the IPC_BEGIN_MESSAGE_MAP_EX macros. +class ServerMacroExTest { + public: + ServerMacroExTest() : unhandled_msgs_(0) { + } + virtual bool OnMessageReceived(const IPC::Message& msg) { + bool msg_is_ok = false; + IPC_BEGIN_MESSAGE_MAP_EX(ServerMacroExTest, msg, msg_is_ok) + IPC_MESSAGE_HANDLER(MsgClassIS, OnMsgClassISMessage) + IPC_MESSAGE_HANDLER(MsgClassSI, OnMsgClassSIMessage) + IPC_MESSAGE_UNHANDLED(++unhandled_msgs_) + IPC_END_MESSAGE_MAP_EX() + return msg_is_ok; + } + + int unhandled_msgs() const { + return unhandled_msgs_; + } + + private: + void OnMsgClassISMessage(int value, const std::wstring& text) { + } + void OnMsgClassSIMessage(const std::wstring& text, int value) { + } + + int unhandled_msgs_; +}; + +TEST(IPCFuzzingTest, MsgMapExMacro) { + IPC::Message* msg = NULL; + ServerMacroExTest server; + + // Test the regular messages. + msg = new MsgClassIS(3, L"text3"); + EXPECT_TRUE(server.OnMessageReceived(*msg)); + delete msg; + msg = new MsgClassSI(L"text2", 2); + EXPECT_TRUE(server.OnMessageReceived(*msg)); + delete msg; + +#ifdef NDEBUG + // Test a bad message. + msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassSI::ID, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(2); + EXPECT_FALSE(server.OnMessageReceived(*msg)); + delete msg; + + msg = new IPC::Message(MSG_ROUTING_CONTROL, MsgClassIS::ID, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(0x64); + msg->WriteInt(0x32); + EXPECT_FALSE(server.OnMessageReceived(*msg)); + delete msg; + + EXPECT_EQ(0, server.unhandled_msgs()); +#endif +} diff --git a/chrome/common/ipc_logging.cc b/chrome/common/ipc_logging.cc new file mode 100644 index 0000000..34c9274 --- /dev/null +++ b/chrome/common/ipc_logging.cc @@ -0,0 +1,296 @@ +// 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 "chrome/common/ipc_logging.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "base/time.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/ipc_sync_message.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/plugin_messages.h" + +#ifdef IPC_MESSAGE_LOG_ENABLED + +namespace IPC { + +const wchar_t kLoggingEventName[] = L"ChromeIPCLog.%d"; +const int kLogSendDelayMs = 100; + +scoped_refptr<Logging> Logging::current_; + +Lock Logging::logger_lock_; + +Logging::Logging() + : logging_event_on_(NULL), + logging_event_off_(NULL), + enabled_(false), + sender_(NULL), + consumer_(NULL), + queue_invoke_later_pending_(false), + main_thread_(MessageLoop::current()) { + // Create an event for this browser instance that's set when logging is + // enabled, so child processes can know when logging is enabled. + int browser_pid; + + CommandLine parsed_command_line; + std::wstring process_type = + parsed_command_line.GetSwitchValue(switches::kProcessType); + if (process_type.empty()) { + browser_pid = GetCurrentProcessId(); + } else { + std::wstring channel_name = + parsed_command_line.GetSwitchValue(switches::kProcessChannelID); + + browser_pid = _wtoi(channel_name.c_str()); + DCHECK(browser_pid != 0); + } + + std::wstring event_name = GetEventName(browser_pid, true); + logging_event_on_ = CreateEvent(NULL, TRUE, FALSE, event_name.c_str()); + + event_name = GetEventName(browser_pid, false); + logging_event_off_ = CreateEvent(NULL, TRUE, FALSE, event_name.c_str()); + + RegisterWaitForEvent(true); +} + +Logging::~Logging() { + CloseHandle(logging_event_on_); + CloseHandle(logging_event_off_); +} + +Logging* Logging::current() { + AutoLock lock(logger_lock_); + + if (!current_.get()) + current_ = new Logging(); + + return current_; +} + +void Logging::RegisterWaitForEvent(bool enabled) { + MessageLoop::current()->WatchObject( + enabled ? logging_event_off_ : logging_event_on_, NULL); + + MessageLoop::current()->WatchObject( + enabled ? logging_event_on_ : logging_event_off_, this); +} + +void Logging::OnObjectSignaled(HANDLE object) { + enabled_ = object == logging_event_on_; + RegisterWaitForEvent(!enabled_); +} + +std::wstring Logging::GetEventName(bool enabled) { + return Logging::current()->GetEventName(GetCurrentProcessId(), enabled); +} + +std::wstring Logging::GetEventName(int browser_pid, bool enabled) { + std::wstring result = StringPrintf(kLoggingEventName, browser_pid); + result += enabled ? L"on" : L"off"; + return result; +} + +void Logging::SetConsumer(Consumer* consumer) { + consumer_ = consumer; +} + +void Logging::Enable() { + ResetEvent(logging_event_off_); + SetEvent(logging_event_on_); +} + +void Logging::Disable() { + ResetEvent(logging_event_on_); + SetEvent(logging_event_off_); +} + +inline bool Logging::Enabled() const { + return enabled_; +} + +void Logging::OnSendLogs() { + queue_invoke_later_pending_ = false; + if (!sender_) + return; + + Message* msg = new Message( + MSG_ROUTING_CONTROL, IPC_LOGGING_ID, Message::PRIORITY_NORMAL); + WriteParam(msg, queued_logs_); + queued_logs_.clear(); + sender_->Send(msg); +} + +void Logging::SetIPCSender(IPC::Message::Sender* sender) { + sender_ = sender; +} + +void Logging::OnReceivedLoggingMessage(const Message& message) { + std::vector<LogData> data; + void* iter = NULL; + if (!ReadParam(&message, &iter, &data)) + return; + + for (size_t i = 0; i < data.size(); ++i) { + Log(data[i]); + } +} + +void Logging::OnSendMessage(Message* message, const std::wstring& channel_id) { + if (!Enabled()) + return; + + if (message->is_reply()) { + LogData* data = message->sync_log_data(); + if (!data) + return; + + // This is actually the delayed reply to a sync message. Create a string + // of the output parameters, add it to the LogData that was earlier stashed + // with the reply, and log the result. + data->channel = channel_id; + GenerateLogData(L"", *message, data); + Log(*data); + delete data; + message->set_sync_log_data(NULL); + } else { + // If the time has already been set (i.e. by ChannelProxy), keep that time + // instead as it's more accurate. + if (!message->sent_time()) + message->set_sent_time(Time::Now().ToInternalValue()); + } +} + +void Logging::OnPreDispatchMessage(const Message& message) { + message.set_received_time(Time::Now().ToInternalValue()); +} + +void Logging::OnPostDispatchMessage(const Message& message, + const std::wstring& channel_id) { + if (!Enabled() || !message.sent_time() || message.dont_log()) + return; + + LogData data; + GenerateLogData(channel_id, message, &data); + + if (MessageLoop::current() == main_thread_) { + Log(data); + } else { + main_thread_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Logging::Log, data)); + } +} + +// static +LogFunction* g_log_function_mapping[16]; +void RegisterMessageLogger(int msg_start, LogFunction* func) { + int msg_class = msg_start >> 12; + if (msg_class > arraysize(g_log_function_mapping)) { + NOTREACHED(); + return; + } + + g_log_function_mapping[msg_class] = func; +} + +void Logging::GetMessageText(uint16 type, std::wstring* name, + const Message* message, + std::wstring* params) { + int message_class = type >> 12; + if (g_log_function_mapping[message_class] != NULL) { + g_log_function_mapping[message_class](type, name, message, params); + } else { + DLOG(INFO) << "No logger function associated with message class " << + message_class; + } +} + +void Logging::Log(const LogData& data) { + if (consumer_) { + // We're in the browser process. + consumer_->Log(data); + } else { + // We're in the renderer or plugin processes. + if (sender_) { + queued_logs_.push_back(data); + if (!queue_invoke_later_pending_) { + queue_invoke_later_pending_ = true; + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod( + this, &Logging::OnSendLogs), kLogSendDelayMs); + } + } + } +} + +void GenerateLogData(const std::wstring& channel, const Message& message, + LogData* data) { + if (message.is_reply()) { + // "data" should already be filled in. + std::wstring params; + Logging::GetMessageText(data->type, NULL, &message, ¶ms); + + if (!data->params.empty() && !params.empty()) + data->params += L", "; + + data->flags += L" DR"; + + data->params += params; + } else { + std::wstring flags; + if (message.is_sync()) + flags = L"S"; + + if (message.is_reply()) + flags += L"R"; + + if (message.is_reply_error()) + flags += L"E"; + + std::wstring params; + Logging::GetMessageText(message.type(), NULL, &message, ¶ms); + + data->channel = channel; + data->type = message.type(); + data->flags = flags; + data->sent = message.sent_time(); + data->receive = message.received_time(); + data->dispatch = Time::Now().ToInternalValue(); + data->params = params; + } +} + +} + +#endif // IPC_MESSAGE_LOG_ENABLED diff --git a/chrome/common/ipc_logging.h b/chrome/common/ipc_logging.h new file mode 100644 index 0000000..d20a265 --- /dev/null +++ b/chrome/common/ipc_logging.h @@ -0,0 +1,125 @@ +// 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 CHROME_COMMON_IPC_LOGGING_H__ +#define CHROME_COMMON_IPC_LOGGING_H__ + +#include <vector> +#include <windows.h> +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "chrome/common/ipc_message_utils.h" + +#ifdef IPC_MESSAGE_LOG_ENABLED + +namespace IPC { + +class Message; + +// One instance per process. Needs to be created on the main thread (the UI +// thread in the browser) but OnPreDispatchMessage/OnPostDispatchMessage +// can be called on other threads. +class Logging : public base::RefCounted<Logging>, + public MessageLoop::Watcher { + public: + // Implemented by consumers of log messages. + class Consumer { + public: + virtual void Log(const IPC::LogData& data) = 0; + }; + + void SetConsumer(Consumer* consumer); + + ~Logging(); + static Logging* current(); + + void Enable(); + void Disable(); + bool inline Enabled() const; + + // Called by child processes to give the logger object the channel to send + // logging data to the browser process. + void SetIPCSender(IPC::Message::Sender* sender); + + // Called in the browser process when logging data from a child process is + // received. + void OnReceivedLoggingMessage(const Message& message); + + void OnSendMessage(Message* message, const std::wstring& channel_id); + void OnPreDispatchMessage(const Message& message); + void OnPostDispatchMessage(const Message& message, + const std::wstring& channel_id); + + // Returns the name of the logging enabled/disabled events so that the + // sandbox can add them to to the policy. If true, gets the name of the + // enabled event, if false, gets the name of the disabled event. + static std::wstring GetEventName(bool enabled); + + // Like the *MsgLog functions declared for each message class, except this + // calls the correct one based on the message type automatically. Defined in + // ipc_logging.cc. + static void GetMessageText(uint16 type, std::wstring* name, + const Message* message, std::wstring* params); + + // MessageLoop::Watcher + void OnObjectSignaled(HANDLE object); + + private: + Logging(); + + std::wstring GetEventName(int browser_pid, bool enabled); + void OnSendLogs(); + void Log(const LogData& data); + + void RegisterWaitForEvent(bool enabled); + + HANDLE logging_event_on_; + HANDLE logging_event_off_; + bool enabled_; + + std::vector<LogData> queued_logs_; + bool queue_invoke_later_pending_; + + IPC::Message::Sender* sender_; + MessageLoop* main_thread_; + + Consumer* consumer_; + + static scoped_refptr<Logging> current_; + + static Lock logger_lock_; +}; + +} + +#endif // IPC_MESSAGE_LOG_ENABLED + +#endif // CHROME_COMMON_IPC_LOGGING_H__ diff --git a/chrome/common/ipc_message.cc b/chrome/common/ipc_message.cc new file mode 100644 index 0000000..c361f31 --- /dev/null +++ b/chrome/common/ipc_message.cc @@ -0,0 +1,97 @@ +// 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 "chrome/common/ipc_message.h" + +#include "base/logging.h" + +namespace IPC { + +//------------------------------------------------------------------------------ + +Message::~Message() { +} + +Message::Message() + : Pickle(sizeof(Header)) { + header()->routing = header()->type = header()->flags = 0; + InitLoggingVariables(); +} + +Message::Message(int32 routing_id, uint16 type, PriorityValue priority) + : Pickle(sizeof(Header)) { + header()->routing = routing_id; + header()->type = type; + header()->flags = priority; + InitLoggingVariables(); +} + +Message::Message(const char* data, int data_len) : Pickle(data, data_len) { + InitLoggingVariables(); +} + +Message::Message(const Message& other) : Pickle(other) { + InitLoggingVariables(); +} + +void Message::InitLoggingVariables() { +#ifdef IPC_MESSAGE_LOG_ENABLED + received_time_ = 0; + dont_log_ = false; + log_data_ = NULL; +#endif +} + +Message& Message::operator=(const Message& other) { + *static_cast<Pickle*>(this) = other; + return *this; +} + +#ifdef IPC_MESSAGE_LOG_ENABLED +void Message::set_sent_time(int64 time) { + DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0); + header()->flags |= HAS_SENT_TIME_BIT; + WriteInt64(time); +} + +int64 Message::sent_time() const { + if ((header()->flags & HAS_SENT_TIME_BIT) == 0) + return 0; + + const char* data = end_of_payload(); + data -= sizeof(int64); + return *(reinterpret_cast<const int64*>(data)); +} + +void Message::set_received_time(int64 time) const { + received_time_ = time; +} +#endif + +} // namespace IPC diff --git a/chrome/common/ipc_message.h b/chrome/common/ipc_message.h new file mode 100644 index 0000000..9eb56e5 --- /dev/null +++ b/chrome/common/ipc_message.h @@ -0,0 +1,263 @@ +// 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 CHROME_COMMON_IPC_MESSAGE_H__ +#define CHROME_COMMON_IPC_MESSAGE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/pickle.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +#ifndef NDEBUG +#define IPC_MESSAGE_LOG_ENABLED +#endif + +namespace IPC { + +//------------------------------------------------------------------------------ + +class Channel; +class Message; +struct LogData; + +class Message : public Pickle { + public: + // Implemented by objects that can send IPC messages across a channel. + class Sender { + public: + // Sends the given IPC message. The implementor takes ownership of the + // given Message regardless of whether or not this method succeeds. This + // is done to make this method easier to use. Returns true on success and + // false otherwise. + virtual bool Send(Message* msg) = 0; + }; + + enum PriorityValue { + PRIORITY_LOW = 1, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + virtual ~Message(); + + Message(); + + // Initialize a message with a user-defined type, priority value, and + // destination WebView ID. + Message(int32 routing_id, uint16 type, PriorityValue priority); + + // Initializes a message from a const block of data. The data is not copied; + // instead the data is merely referenced by this message. Only const methods + // should be used on the message when initialized this way. + Message(const char* data, int data_len); + + Message(const Message& other); + Message& operator=(const Message& other); + + PriorityValue priority() const { + return static_cast<PriorityValue>(header()->flags & PRIORITY_MASK); + } + + // True if this is a synchronous message. + bool is_sync() const { + return (header()->flags & SYNC_BIT) != 0; + } + + // Set this on a reply to a synchronous message. + void set_reply() { + header()->flags |= REPLY_BIT; + } + + bool is_reply() const { + return (header()->flags & REPLY_BIT) != 0; + } + + // Set this on a reply to a synchronous message to indicate that no receiver + // was found. + void set_reply_error() { + header()->flags |= REPLY_ERROR_BIT; + } + + bool is_reply_error() const { + return (header()->flags & REPLY_ERROR_BIT) != 0; + } + + // Normally when a receiver gets a message and they're blocked on a + // synchronous message Send, they buffer a message. Setting this flag causes + // the receiver to be unblocked and the message to be dispatched immediately. + void set_unblock(bool unblock) { + if (unblock) { + header()->flags |= UNBLOCK_BIT; + } else { + header()->flags &= ~UNBLOCK_BIT; + } + } + + bool should_unblock() const { + return (header()->flags & UNBLOCK_BIT) != 0; + } + + // Tells the receiver that the caller is pumping messages while waiting + // for the result. + bool is_caller_pumping_messages() const { + return (header()->flags & PUMPING_MSGS_BIT) != 0; + } + + uint16 type() const { + return header()->type; + } + + int32 routing_id() const { + return header()->routing; + } + + void set_routing_id(int32 new_id) { + header()->routing = new_id; + } + + template<class T> + static bool Dispatch(const Message* msg, T* obj, void (T::*func)()) { + (obj->*func)(); + return true; + } + + template<class T> + static bool Dispatch(const Message* msg, T* obj, void (T::*func)() const) { + (obj->*func)(); + return true; + } + + template<class T> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&)) { + (obj->*func)(*msg); + return true; + } + + template<class T> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&) const) { + (obj->*func)(*msg); + return true; + } + + // Used for async messages with no parameters. + static void Log(const Message* msg, std::wstring* l) { + } + + // Find the end of the message data that starts at range_start. Returns NULL + // if the entire message is not found in the given data range. + static const char* FindNext(const char* range_start, const char* range_end) { + return Pickle::FindNext(sizeof(Header), range_start, range_end); + } + +#ifdef IPC_MESSAGE_LOG_ENABLED + // Adds the outgoing time from Time::Now() at the end of the message and sets + // a bit to indicate that it's been added. + void set_sent_time(int64 time); + int64 sent_time() const; + + void set_received_time(int64 time) const; + int64 received_time() const { return received_time_; } + void set_output_params(const std::wstring& op) const { output_params_ = op; } + const std::wstring& output_params() const { return output_params_; } + // The following four functions are needed so we can log sync messages with + // delayed replies. We stick the log data from the sent message into the + // reply message, so that when it's sent and we have the output parameters + // we can log it. As such, we set a flag on the sent message to not log it. + void set_sync_log_data(LogData* data) const { log_data_ = data; } + LogData* sync_log_data() const { return log_data_; } + void set_dont_log() const { dont_log_ = true; } + bool dont_log() const { return dont_log_; } +#endif + + protected: + friend class Channel; + friend class MessageReplyDeserializer; + friend class SyncMessage; + + void set_sync() { + header()->flags |= SYNC_BIT; + } + + // flags + enum { + PRIORITY_MASK = 0x0003, + SYNC_BIT = 0x0004, + REPLY_BIT = 0x0008, + REPLY_ERROR_BIT = 0x0010, + UNBLOCK_BIT = 0x0020, + PUMPING_MSGS_BIT= 0x0040, + HAS_SENT_TIME_BIT = 0x0080, + }; + +#pragma pack(push, 2) + struct Header : Pickle::Header { + int32 routing; // ID of the view that this message is destined for + uint16 type; // specifies the user-defined message type + uint16 flags; // specifies control flags for the message + }; +#pragma pack(pop) + + Header* header() { + return headerT<Header>(); + } + const Header* header() const { + return headerT<Header>(); + } + + void InitLoggingVariables(); + +#ifdef IPC_MESSAGE_LOG_ENABLED + // Used for logging. + mutable int64 received_time_; + mutable std::wstring output_params_; + mutable LogData* log_data_; + mutable bool dont_log_; +#endif +}; + +//------------------------------------------------------------------------------ + +} // namespace IPC + +enum SpecialRoutingIDs { + // indicates that we don't have a routing ID yet. + MSG_ROUTING_NONE = -2, + + // indicates a general message not sent to a particular tab. + MSG_ROUTING_CONTROL = kint32max, +}; + +#define IPC_REPLY_ID 0xFFF0 // Special message id for replies +#define IPC_LOGGING_ID 0xFFF1 // Special message id for logging + +#endif // CHROME_COMMON_IPC_MESSAGE_H__ diff --git a/chrome/common/ipc_message_macros.h b/chrome/common/ipc_message_macros.h new file mode 100644 index 0000000..a3251f7 --- /dev/null +++ b/chrome/common/ipc_message_macros.h @@ -0,0 +1,1074 @@ +// 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 header is meant to be included in multiple passes, hence no traditional +// header guard. +// +// In the first pass, IPC_MESSAGE_MACROS_ENUMS should be defined, which will +// create enums for each of the messages defined with the IPC_MESSAGE_* macros. +// +// In the second pass, either IPC_MESSAGE_MACROS_DEBUGSTRINGS or +// IPC_MESSAGE_MACROS_CLASSES should be defined (if both, DEBUGSTRINGS takes +// precedence). Only one .cc file should have DEBUGSTRINGS defined, as this +// will create helper functions mapping message types to strings. Having +// CLASSES defined will create classes for each of the messages defined with +// the IPC_MESSAGE_* macros. +// +// "Sync" messages are just synchronous calls, the Send() call doesn't return +// until a reply comes back. Input parameters are first (const TYPE&), and +// To declare a sync message, use the IPC_SYNC_ macros. The numbers at the +// end show how many input/output parameters there are (i.e. 1_2 is 1 in, 2 out). +// The caller does a Send([route id, ], in1, &out1, &out2). +// The receiver's handler function will be +// void OnSyncMessageName(const type1& in1, type2* out1, type3* out2) +// +// +// A caller can also send a synchronous message, while the receiver can respond +// at a later time. This is transparent from the sender's size. The receiver +// needs to use a different handler that takes in a IPC::Message* as the output +// type, stash the message, and when it has the data it can Send the message. +// +// Use the IPC_MESSAGE_HANDLER_DELAY_REPLY macro instead of IPC_MESSAGE_HANDLER +// IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncMessageName, OnSyncMessageName) +// +// The handler function will look like: +// void OnSyncMessageName(const type1& in1, IPC::Message* reply_msg); +// +// Receiver stashes the IPC::Message* pointer, and when it's ready, it does: +// ViewHostMsg_SyncMessageName::WriteReplyParams(reply_msg, out1, out2); +// Send(reply_msg); + +#include "chrome/common/ipc_message_utils.h" + +// Undefine the macros from the previous pass (if any). +#undef IPC_BEGIN_MESSAGES +#undef IPC_END_MESSAGES +#undef IPC_MESSAGE_CONTROL0 +#undef IPC_MESSAGE_CONTROL1 +#undef IPC_MESSAGE_CONTROL2 +#undef IPC_MESSAGE_CONTROL3 +#undef IPC_MESSAGE_CONTROL4 +#undef IPC_MESSAGE_CONTROL5 +#undef IPC_MESSAGE_ROUTED0 +#undef IPC_MESSAGE_ROUTED1 +#undef IPC_MESSAGE_ROUTED2 +#undef IPC_MESSAGE_ROUTED3 +#undef IPC_MESSAGE_ROUTED4 +#undef IPC_MESSAGE_ROUTED5 +#undef IPC_MESSAGE_EMPTY +#undef IPC_SYNC_MESSAGE_CONTROL0_0 +#undef IPC_SYNC_MESSAGE_CONTROL0_1 +#undef IPC_SYNC_MESSAGE_CONTROL0_2 +#undef IPC_SYNC_MESSAGE_CONTROL0_3 +#undef IPC_SYNC_MESSAGE_CONTROL1_0 +#undef IPC_SYNC_MESSAGE_CONTROL1_1 +#undef IPC_SYNC_MESSAGE_CONTROL1_2 +#undef IPC_SYNC_MESSAGE_CONTROL1_3 +#undef IPC_SYNC_MESSAGE_CONTROL2_0 +#undef IPC_SYNC_MESSAGE_CONTROL2_1 +#undef IPC_SYNC_MESSAGE_CONTROL2_2 +#undef IPC_SYNC_MESSAGE_CONTROL2_3 +#undef IPC_SYNC_MESSAGE_CONTROL3_1 +#undef IPC_SYNC_MESSAGE_CONTROL3_2 +#undef IPC_SYNC_MESSAGE_CONTROL3_3 +#undef IPC_SYNC_MESSAGE_CONTROL4_1 +#undef IPC_SYNC_MESSAGE_CONTROL4_2 +#undef IPC_SYNC_MESSAGE_ROUTED0_0 +#undef IPC_SYNC_MESSAGE_ROUTED0_1 +#undef IPC_SYNC_MESSAGE_ROUTED0_2 +#undef IPC_SYNC_MESSAGE_ROUTED0_3 +#undef IPC_SYNC_MESSAGE_ROUTED1_0 +#undef IPC_SYNC_MESSAGE_ROUTED1_1 +#undef IPC_SYNC_MESSAGE_ROUTED1_2 +#undef IPC_SYNC_MESSAGE_ROUTED1_3 +#undef IPC_SYNC_MESSAGE_ROUTED2_0 +#undef IPC_SYNC_MESSAGE_ROUTED2_1 +#undef IPC_SYNC_MESSAGE_ROUTED2_2 +#undef IPC_SYNC_MESSAGE_ROUTED2_3 +#undef IPC_SYNC_MESSAGE_ROUTED3_0 +#undef IPC_SYNC_MESSAGE_ROUTED3_1 +#undef IPC_SYNC_MESSAGE_ROUTED3_2 +#undef IPC_SYNC_MESSAGE_ROUTED3_3 +#undef IPC_SYNC_MESSAGE_ROUTED4_0 +#undef IPC_SYNC_MESSAGE_ROUTED4_1 + +#if defined(IPC_MESSAGE_MACROS_ENUMS) +#undef IPC_MESSAGE_MACROS_ENUMS + +// TODO(jabdelmalek): we're using the lowest 12 bits of type for the message +// id, and the highest 4 bits for the channel type. This constrains us to +// 16 channel types (currently using 8) and 4K messages per type. Should +// really make type be 32 bits, but then we break automation with older Chrome +// builds.. +#define IPC_BEGIN_MESSAGES(label, start) \ + enum label##MsgType { \ + label##Start = start << 12, \ + label##PreStart = (start << 12) - 1, // Do this so that automation messages keep the same id as before + +#define IPC_END_MESSAGES(label) \ + label##End \ + }; + +#define IPC_MESSAGE_CONTROL0(msg_class) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + msg_class##__ID, + +#define IPC_MESSAGE_EMPTY(msg_class) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + msg_class##__ID, + +// Message crackers and handlers. +// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they +// allow you to detect when a message could not be de-serialized. Usage: +// +// void MyClass::OnMessageReceived(const IPC::Message& msg) { +// bool msg_is_good = false; +// IPC_BEGIN_MESSAGE_MAP_EX(MyClass, msg, msg_is_good) +// IPC_MESSAGE_HANDLER(MsgClassOne, OnMsgClassOne) +// ...more handlers here ... +// IPC_MESSAGE_HANDLER(MsgClassTen, OnMsgClassTen) +// IPC_END_MESSAGE_MAP_EX() +// if (!msg_is_good) { +// // Signal error here or terminate offending process. +// } +// } + +#define IPC_DEFINE_MESSAGE_MAP(class_name) \ +void class_name::OnMessageReceived(const IPC::Message& msg) \ + IPC_BEGIN_MESSAGE_MAP(class_name, msg) + +#define IPC_BEGIN_MESSAGE_MAP_EX(class_name, msg, msg_is_ok) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool& msg_is_ok__ = msg_is_ok; \ + switch (ipc_message__.type()) { \ + +#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool msg_is_ok__ = true; \ + switch (ipc_message__.type()) { \ + +#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER(msg_class, member_func) \ + IPC_MESSAGE_FORWARD(msg_class, this, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_class::DispatchDelayReply(&ipc_message__, obj, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER_DELAY_REPLY(msg_class, member_func) \ + IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, this, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code) \ + case msg_class::ID: \ + code; \ + break; + +#define IPC_REPLY_HANDLER(func) \ + case IPC_REPLY_ID: \ + func(ipc_message__); \ + break; + + +#define IPC_MESSAGE_UNHANDLED(code) \ + default: \ + code; \ + break; + +#define IPC_MESSAGE_UNHANDLED_ERROR() \ + IPC_MESSAGE_UNHANDLED(NOTREACHED() << \ + "Invalid message with type = " << \ + ipc_message__.type()) + +#define IPC_END_MESSAGE_MAP() \ + DCHECK(msg_is_ok__); \ + } \ +} + +#define IPC_END_MESSAGE_MAP_EX() \ + } \ +} + +#elif defined(IPC_MESSAGE_MACROS_LOG) +#undef IPC_MESSAGE_MACROS_LOG + +#define IPC_BEGIN_MESSAGES(label, start) \ + void label##MsgLog(uint16 type, std::wstring* name, const IPC::Message* msg, std::wstring* params) { \ + switch (type) { + +#define IPC_END_MESSAGES(label) \ + default: \ + if (name) \ + *name = L"[UNKNOWN " L ## #label L" MSG"; \ + } \ + } + +#define IPC_MESSAGE_LOG(msg_class) \ + case msg_class##__ID: \ + if (name) \ + *name = L ## #msg_class; \ + if (msg && params) \ + msg_class::Log(msg, params); \ + break; + +#define IPC_MESSAGE_CONTROL0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_EMPTY(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#elif defined(IPC_MESSAGE_MACROS_CLASSES) +#undef IPC_MESSAGE_MACROS_CLASSES + +#define IPC_BEGIN_MESSAGES(label, start) +#define IPC_END_MESSAGES(label) + +#define IPC_MESSAGE_CONTROL0(msg_class) \ + class msg_class : public IPC::Message { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class() \ + : IPC::Message(MSG_ROUTING_CONTROL, \ + ID, \ + PRIORITY_NORMAL) {} \ + }; + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + class msg_class : public IPC::MessageWithTuple<type1> { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1) \ + : IPC::MessageWithTuple<type1>(MSG_ROUTING_CONTROL, \ + ID, \ + arg1) {} \ + }; + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + class msg_class : public IPC::MessageWithTuple< Tuple2<type1, type2> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2) \ + : IPC::MessageWithTuple< Tuple2<type1, type2> >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2)) {} \ + }; + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple3<type1, type2, type3> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2, const type3& arg3) \ + : IPC::MessageWithTuple< Tuple3<type1, type2, type3> >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3)) {} \ + }; + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2, const type3& arg3, \ + const type4& arg4) \ + : IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3, arg4)) {} \ + }; + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2, \ + const type3& arg3, const type4& arg4, const type5& arg5) \ + : IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3, arg4, arg5)) {} \ + }; + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + class msg_class : public IPC::Message { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id) \ + : IPC::Message(routing_id, ID, PRIORITY_NORMAL) {} \ + }; + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + class msg_class : public IPC::MessageWithTuple<type1> { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1) \ + : IPC::MessageWithTuple<type1>(routing_id, ID, arg1) {} \ + }; + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + class msg_class : public IPC::MessageWithTuple< Tuple2<type1, type2> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2) \ + : IPC::MessageWithTuple< Tuple2<type1, type2> >( \ + routing_id, ID, MakeTuple(arg1, arg2)) {} \ + }; + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple3<type1, type2, type3> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \ + const type3& arg3) \ + : IPC::MessageWithTuple< Tuple3<type1, type2, type3> >( \ + routing_id, ID, MakeTuple(arg1, arg2, arg3)) {} \ + }; + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \ + const type3& arg3, const type4& arg4) \ + : IPC::MessageWithTuple< Tuple4<type1, type2, type3, type4> >( \ + routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4)) {} \ + }; + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \ + const type3& arg3, const type4& arg4, const type5& arg5) \ + : IPC::MessageWithTuple< Tuple5<type1, type2, type3, type4, type5> >( \ + routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4, arg5)) {} \ + }; + +// Dummy class for now, just to give us the ID field. +#define IPC_MESSAGE_EMPTY(msg_class) \ + class msg_class { \ + public: \ + enum { ID = msg_class##__ID }; \ + static void Log(const IPC::Message* msg, std::wstring* l) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + class msg_class : public IPC::MessageWithReply<Tuple0, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class() \ + : IPC::MessageWithReply<Tuple0, Tuple0 >( \ + MSG_ROUTING_CONTROL, ID, \ + MakeTuple(), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + class msg_class : public IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(type1_out* arg1) \ + : IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(), MakeRefTuple(*arg1)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(type1_out* arg1, type2_out* arg2) \ + : IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple0, \ + Tuple3<type1_out&, type2_out&, type3_out&> >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(type1_out* arg1, type2_out* arg2, type3_out* arg3) \ + : IPC::MessageWithReply<Tuple0, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1) \ + : IPC::MessageWithReply<type1_in, Tuple0 >( \ + MSG_ROUTING_CONTROL, ID, \ + arg1, MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, type1_out* arg2) \ + : IPC::MessageWithReply<type1_in, Tuple1<type1_out&> >( \ + MSG_ROUTING_CONTROL, ID, \ + arg1, MakeRefTuple(*arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3) \ + : IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> >( \ + MSG_ROUTING_CONTROL, ID, \ + arg1, MakeRefTuple(*arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, \ + Tuple3<type1_out&, type2_out&, type3_out&> >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \ + : IPC::MessageWithReply<type1_in, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \ + ID, \ + arg1, MakeRefTuple(*arg2, *arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 >( \ + MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> >( \ + MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \ + : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple1<type1_out&> >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \ + : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \ + : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \ + : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple1<type1_out&> >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6) \ + : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple2<type1_out&, type2_out&> >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + class msg_class : public IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, type1_out* arg1) \ + : IPC::MessageWithReply<Tuple0, Tuple1<type1_out&> >( \ + routing_id, ID, \ + MakeTuple(), MakeRefTuple(*arg1)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + class msg_class : public IPC::MessageWithReply<Tuple0, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id) \ + : IPC::MessageWithReply<Tuple0, Tuple0 >( \ + routing_id, ID, \ + MakeTuple(), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, type1_out* arg1, type2_out* arg2) \ + : IPC::MessageWithReply<Tuple0, Tuple2<type1_out&, type2_out&> >( \ + routing_id, ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple0, \ + Tuple3<type1_out&, type2_out&, type3_out&> >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, type1_out* arg1, type2_out* arg2, type3_out* arg3) \ + : IPC::MessageWithReply<Tuple0, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1) \ + : IPC::MessageWithReply<type1_in, Tuple0 >( \ + routing_id, ID, \ + arg1, MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, type1_out* arg2) \ + : IPC::MessageWithReply<type1_in, Tuple1<type1_out&> >( \ + routing_id, ID, \ + arg1, MakeRefTuple(*arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3) \ + : IPC::MessageWithReply<type1_in, Tuple2<type1_out&, type2_out&> >( \ + routing_id, ID, \ + arg1, MakeRefTuple(*arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<type1_in, \ + Tuple3<type1_out&, type2_out&, type3_out&> >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \ + : IPC::MessageWithReply<type1_in, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \ + arg1, MakeRefTuple(*arg2, *arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple0 >( \ + routing_id, ID, \ + MakeTuple(arg1, arg2), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, Tuple1<type1_out&> >( \ + routing_id, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple2<type1_out&, type2_out&> >(routing_id, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \ + : IPC::MessageWithReply<Tuple2<type1_in, type2_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3) \ + : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, Tuple0>( \ + routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \ + : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple1<type1_out&> >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple2<type1_out&, type2_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \ + : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple2<type1_out&, type2_out&> >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \ + : IPC::MessageWithReply<Tuple3<type1_in, type2_in, type3_in>, \ + Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4) \ + : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple0 >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple1<type1_out&> > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \ + : IPC::MessageWithReply<Tuple4<type1_in, type2_in, type3_in, type4_in>, \ + Tuple1<type1_out&> >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \ + }; + +#endif // #if defined() diff --git a/chrome/common/ipc_message_unittest.cc b/chrome/common/ipc_message_unittest.cc new file mode 100644 index 0000000..54d4e00 --- /dev/null +++ b/chrome/common/ipc_message_unittest.cc @@ -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. + +#include <string.h> + +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Tests that serialize/deserialize correctly understand each other +TEST(IPCMessageTest, Serialize) { + const char* serialize_cases[] = { + "http://www.google.com/", + "http://user:pass@host.com:888/foo;bar?baz#nop", + "#inva://idurl/", + }; + + for (int i = 0; i < arraysize(serialize_cases); i++) { + GURL input(serialize_cases[i]); + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + IPC::ParamTraits<GURL>::Write(&msg, input); + + GURL output; + void* iter = NULL; + EXPECT_TRUE(IPC::ParamTraits<GURL>::Read(&msg, &iter, &output)); + + // We want to test each component individually to make sure its range was + // correctly serialized and deserialized, not just the spec. + EXPECT_EQ(input.possibly_invalid_spec(), output.possibly_invalid_spec()); + EXPECT_EQ(input.is_valid(), output.is_valid()); + EXPECT_EQ(input.scheme(), output.scheme()); + EXPECT_EQ(input.username(), output.username()); + EXPECT_EQ(input.password(), output.password()); + EXPECT_EQ(input.host(), output.host()); + EXPECT_EQ(input.port(), output.port()); + EXPECT_EQ(input.path(), output.path()); + EXPECT_EQ(input.query(), output.query()); + EXPECT_EQ(input.ref(), output.ref()); + } + + // Also test the corrupt case. + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + msg.WriteInt(99); + GURL output; + void* iter = NULL; + EXPECT_FALSE(IPC::ParamTraits<GURL>::Read(&msg, &iter, &output)); +} diff --git a/chrome/common/ipc_message_utils.h b/chrome/common/ipc_message_utils.h new file mode 100644 index 0000000..7599b4fb --- /dev/null +++ b/chrome/common/ipc_message_utils.h @@ -0,0 +1,1414 @@ +// 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 CHROME_COMMON_IPC_MESSAGE_UTILS_H__ +#define CHROME_COMMON_IPC_MESSAGE_UTILS_H__ + +#include <string> +#include <vector> +#include <map> + +#include "base/basictypes.h" +#include "base/gfx/rect.h" +#include "base/gfx/size.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/tuple.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_sync_message.h" +#include "chrome/common/thumbnail_score.h" +#include "googleurl/src/gurl.h" +#include "skia/include/SkBitmap.h" +#include "webkit/glue/cache_manager.h" +#include "webkit/glue/console_message_level.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/window_open_disposition.h" +#include "webkit/glue/webcursor.h" + +namespace IPC { + +// Used by the message macros to register a logging function based on the +// message class. +typedef void (LogFunction)(uint16 type, + std::wstring* name, + const IPC::Message* msg, + std::wstring* params); +void RegisterMessageLogger(int msg_start, LogFunction* func); + + +//----------------------------------------------------------------------------- +// An iterator class for reading the fields contained within a Message. + +class MessageIterator { + public: + MessageIterator(const Message& m) : msg_(m), iter_(NULL) { + } + int NextInt() const { + int val; + if (!msg_.ReadInt(&iter_, &val)) + NOTREACHED(); + return val; + } + intptr_t NextIntPtr() const { + intptr_t val; + if (!msg_.ReadIntPtr(&iter_, &val)) + NOTREACHED(); + return val; + } + const std::string NextString() const { + std::string val; + if (!msg_.ReadString(&iter_, &val)) + NOTREACHED(); + return val; + } + const std::wstring NextWString() const { + std::wstring val; + if (!msg_.ReadWString(&iter_, &val)) + NOTREACHED(); + return val; + } + const void NextData(const char** data, int* length) const { + if (!msg_.ReadData(&iter_, data, length)) { + NOTREACHED(); + } + } + private: + const Message& msg_; + mutable void* iter_; +}; + +//----------------------------------------------------------------------------- +// ParamTraits specializations, etc. + +template <class P> struct ParamTraits {}; + +template <class P> +static inline void WriteParam(Message* m, const P& p) { + ParamTraits<P>::Write(m, p); +} + +template <class P> +static inline bool ReadParam(const Message* m, void** iter, P* p) { + return ParamTraits<P>::Read(m, iter, p); +} + +template <class P> +static inline void LogParam(const P& p, std::wstring* l) { + ParamTraits<P>::Log(p, l); +} + +template <> +struct ParamTraits<bool> { + typedef bool param_type; + static void Write(Message* m, const param_type& p) { + m->WriteBool(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadBool(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(p ? L"true" : L"false"); + } +}; + +template <> +struct ParamTraits<int> { + typedef int param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%d", p)); + } +}; + +template <> +struct ParamTraits<size_t> { + typedef size_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteSize(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadSize(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%u", p)); + } +}; + +template <> +struct ParamTraits<int64> { + typedef int64 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%I64d", p)); + } +}; + +template <> +struct ParamTraits<uint64> { + typedef uint64 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(static_cast<int64>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, reinterpret_cast<int64*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%I64u", p)); + } +}; + +template <> +struct ParamTraits<double> { + typedef double param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(double)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(double)) { + memcpy(r, data, sizeof(double)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"e", p)); + } +}; + +template <> +struct ParamTraits<wchar_t> { + typedef wchar_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(wchar_t)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(wchar_t)) { + memcpy(r, data, sizeof(wchar_t)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%c", p)); + } +}; + +template <> +struct ParamTraits<Time> { + typedef Time param_type; + static void Write(Message* m, const param_type& p) { + ParamTraits<int64>::Write(m, p.ToInternalValue()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int64 value; + if (!ParamTraits<int64>::Read(m, iter, &value)) + return false; + *r = Time::FromInternalValue(value); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + ParamTraits<int64>::Log(p.ToInternalValue(), l); + } +}; + +template <> +struct ParamTraits<LOGFONT> { + typedef LOGFONT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(LOGFONT)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(LOGFONT)) { + memcpy(r, data, sizeof(LOGFONT)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"<LOGFONT>")); + } +}; + +template <> +struct ParamTraits<MSG> { + typedef MSG param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(MSG)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(MSG)) { + memcpy(r, data, sizeof(MSG)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } +}; + +struct SkBitmap_Data { + // The configuration for the bitmap (bits per pixel, etc). + SkBitmap::Config fConfig; + + // The width of the bitmap in pixels. + uint32 fWidth; + + // The height of the bitmap in pixels. + uint32 fHeight; + + // The number of bytes between subsequent rows of the bitmap. + uint32 fRowBytes; + + void InitSkBitmapDataForTransfer(const SkBitmap& bitmap) { + fConfig = bitmap.config(); + fWidth = bitmap.width(); + fHeight = bitmap.height(); + fRowBytes = bitmap.rowBytes(); + } + + void InitSkBitmapFromData(SkBitmap* bitmap, const char* pixels, + size_t total_pixels) const { + if (total_pixels) { + bitmap->setConfig(fConfig, fWidth, fHeight, fRowBytes); + bitmap->allocPixels(); + memcpy(bitmap->getPixels(), pixels, total_pixels); + } + } +}; + +template <> +struct ParamTraits<SkBitmap> { + typedef SkBitmap param_type; + static void Write(Message* m, const param_type& p) { + size_t fixed_size = sizeof(SkBitmap_Data); + SkBitmap_Data bmp_data; + bmp_data.InitSkBitmapDataForTransfer(p); + m->WriteData(reinterpret_cast<const char*>(&bmp_data), + static_cast<int>(fixed_size)); + size_t pixel_size = p.getSize(); + SkAutoLockPixels p_lock(p); + m->WriteData(reinterpret_cast<const char*>(p.getPixels()), + static_cast<int>(pixel_size)); + } + // Note: This function expects parameter |r| to be of type &SkBitmap since + // r->SetConfig() and r->SetPixels() are called. + static bool Read(const Message* m, void** iter, param_type* r) { + const char* fixed_data; + int fixed_data_size = 0; + if (!m->ReadData(iter, &fixed_data, &fixed_data_size) || + (fixed_data_size <= 0)) { + NOTREACHED(); + return false; + } + if (fixed_data_size != sizeof(SkBitmap_Data)) + return false; // Message is malformed. + + const char* variable_data; + int variable_data_size = 0; + if (!m->ReadData(iter, &variable_data, &variable_data_size) || + (variable_data_size < 0)) { + NOTREACHED(); + return false; + } + const SkBitmap_Data* bmp_data = + reinterpret_cast<const SkBitmap_Data*>(fixed_data); + bmp_data->InitSkBitmapFromData(r, variable_data, variable_data_size); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"<SkBitmap>")); + } +}; + +template <> +struct ParamTraits<MONITORINFOEX> { + typedef MONITORINFOEX param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(MONITORINFOEX)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(MONITORINFOEX)) { + memcpy(r, data, sizeof(MONITORINFOEX)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"<MONITORINFOEX>")); + } +}; + +template <> +struct ParamTraits<std::string> { + typedef std::string param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadString(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(UTF8ToWide(p)); + } +}; + +template <> +struct ParamTraits<std::vector<unsigned char> > { + typedef std::vector<unsigned char> param_type; + static void Write(Message* m, const param_type& p) { + if (p.size() == 0) { + m->WriteData(NULL, 0); + } else { + m->WriteData(reinterpret_cast<const char*>(&p.front()), + static_cast<int>(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + for (size_t i = 0; i < p.size(); ++i) + l->push_back(p[i]); + } +}; + +template <> +struct ParamTraits<std::vector<char> > { + typedef std::vector<char> param_type; + static void Write(Message* m, const param_type& p) { + if (p.size() == 0) { + m->WriteData(NULL, 0); + } else { + m->WriteData(&p.front(), static_cast<int>(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + for (size_t i = 0; i < p.size(); ++i) + l->push_back(p[i]); + } +}; + +template <class P> +struct ParamTraits<std::vector<P> > { + typedef std::vector<P> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.size())); + for (size_t i = 0; i < p.size(); i++) + WriteParam(m, p[i]); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + if (!m->ReadLength(iter, &size)) + return false; + // Resizing beforehand is not safe, see BUG 1006367 for details. + if (m->IteratorHasRoomFor(*iter, size * sizeof(P))) { + r->resize(size); + for (int i = 0; i < size; i++) { + if (!ReadParam(m, iter, &(*r)[i])) + return false; + } + } else { + for (int i = 0; i < size; i++) { + P element; + if (!ReadParam(m, iter, &element)) + return false; + r->push_back(element); + } + } + return true; + } + static void Log(const param_type& p, std::wstring* l) { + for (size_t i = 0; i < p.size(); ++i) { + if (i != 0) + l->append(L" "); + + LogParam((p[i]), l); + } + } +}; + +template <class K, class V> +struct ParamTraits<std::map<K, V> > { + typedef std::map<K, V> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.size())); + param_type::const_iterator iter; + for (iter = p.begin(); iter != p.end(); ++iter) { + WriteParam(m, iter->first); + WriteParam(m, iter->second); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + if (!ReadParam(m, iter, &size) || size < 0) + return false; + for (int i = 0; i < size; ++i) { + K k; + if (!ReadParam(m, iter, &k)) + return false; + V& value = (*r)[k]; + if (!ReadParam(m, iter, &value)) + return false; + } + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<std::map>"); + } +}; + +template <> +struct ParamTraits<std::wstring> { + typedef std::wstring param_type; + static void Write(Message* m, const param_type& p) { + m->WriteWString(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadWString(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(p); + } +}; + +template <> +struct ParamTraits<GURL> { + typedef GURL param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString(p.possibly_invalid_spec()); + // TODO(brettw) bug 684583: Add encoding for query params. + } + static bool Read(const Message* m, void** iter, param_type* p) { + std::string s; + if (!m->ReadString(iter, &s)) { + *p = GURL(); + return false; + } + *p = GURL(s); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(UTF8ToWide(p.spec())); + } +}; + +// and, a few more useful types... +template <> +struct ParamTraits<HANDLE> { + typedef HANDLE param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast<intptr_t>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraits<HCURSOR> { + typedef HCURSOR param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast<intptr_t>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraits<HWND> { + typedef HWND param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast<intptr_t>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraits<HRGN> { + typedef HRGN param_type; + static void Write(Message* m, const param_type& p) { + int data_size = GetRegionData(p, 0, NULL); + if (data_size) { + char* bytes = new char[data_size]; + GetRegionData(p, data_size, reinterpret_cast<LPRGNDATA>(bytes)); + m->WriteData(reinterpret_cast<const char*>(bytes), data_size); + delete [] bytes; + } else { + m->WriteData(NULL, 0); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + bool res = FALSE; + const char *data; + int data_size = 0; + res = m->ReadData(iter, &data, &data_size); + if (data_size) { + *r = ExtCreateRegion(NULL, data_size, + reinterpret_cast<CONST RGNDATA*>(data)); + } else { + res = TRUE; + *r = CreateRectRgn(0, 0, 0, 0); + } + return res; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraits<HACCEL> { + typedef HACCEL param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast<intptr_t>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r)); + } +}; + +template <> +struct ParamTraits<POINT> { + typedef POINT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.x); + m->WriteInt(p.y); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int x, y; + if (!m->ReadInt(iter, &x) || !m->ReadInt(iter, &y)) + return false; + r->x = x; + r->y = y; + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d)", p.x, p.y)); + } +}; + +template <> +struct ParamTraits<gfx::Point> { + typedef gfx::Point param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.x()); + m->WriteInt(p.y()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int x, y; + if (!m->ReadInt(iter, &x) || + !m->ReadInt(iter, &y)) + return false; + r->set_x(x); + r->set_y(y); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d)", p.x(), p.y())); + } +}; + +template <> +struct ParamTraits<gfx::Rect> { + typedef gfx::Rect param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.x()); + m->WriteInt(p.y()); + m->WriteInt(p.width()); + m->WriteInt(p.height()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int x, y, w, h; + if (!m->ReadInt(iter, &x) || + !m->ReadInt(iter, &y) || + !m->ReadInt(iter, &w) || + !m->ReadInt(iter, &h)) + return false; + r->set_x(x); + r->set_y(y); + r->set_width(w); + r->set_height(h); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d, %d, %d)", p.x(), p.y(), p.width(), p.height())); + } +}; + +template <> +struct ParamTraits<gfx::Size> { + typedef gfx::Size param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.width()); + m->WriteInt(p.height()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int w, h; + if (!m->ReadInt(iter, &w) || + !m->ReadInt(iter, &h)) + return false; + r->set_width(w); + r->set_height(h); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d)", p.width(), p.height())); + } +}; + +template<> +struct ParamTraits<ThumbnailScore> { + typedef ThumbnailScore param_type; + static void Write(Message* m, const param_type& p) { + IPC::ParamTraits<double>::Write(m, p.boring_score); + IPC::ParamTraits<bool>::Write(m, p.good_clipping); + IPC::ParamTraits<bool>::Write(m, p.at_top); + IPC::ParamTraits<Time>::Write(m, p.time_at_snapshot); + } + static bool Read(const Message* m, void** iter, param_type* r) { + double boring_score; + bool good_clipping, at_top; + Time time_at_snapshot; + if (!IPC::ParamTraits<double>::Read(m, iter, &boring_score) || + !IPC::ParamTraits<bool>::Read(m, iter, &good_clipping) || + !IPC::ParamTraits<bool>::Read(m, iter, &at_top) || + !IPC::ParamTraits<Time>::Read(m, iter, &time_at_snapshot)) + return false; + + r->boring_score = boring_score; + r->good_clipping = good_clipping; + r->at_top = at_top; + r->time_at_snapshot = time_at_snapshot; + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"(%f, %d, %d)", + p.boring_score, p.good_clipping, p.at_top)); + } +}; + +template <> +struct ParamTraits<WindowOpenDisposition> { + typedef WindowOpenDisposition param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int temp; + bool res = m->ReadInt(iter, &temp); + *r = static_cast<WindowOpenDisposition>(temp); + return res; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%d", p)); + } +}; + +template <> +struct ParamTraits<ConsoleMessageLevel> { + typedef ConsoleMessageLevel param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int temp; + bool res = m->ReadInt(iter, &temp); + *r = static_cast<ConsoleMessageLevel>(temp); + return res; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%d", p)); + } +}; + +template <> +struct ParamTraits<CacheManager::ResourceTypeStat> { + typedef CacheManager::ResourceTypeStat param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.count); + WriteParam(m, p.size); + WriteParam(m, p.live_size); + WriteParam(m, p.decoded_size); + } + static bool Read(const Message* m, void** iter, param_type* r) { + bool result = + ReadParam(m, iter, &r->count) && + ReadParam(m, iter, &r->size) && + ReadParam(m, iter, &r->live_size) && + ReadParam(m, iter, &r->decoded_size); + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%d %d %d %d", p.count, p.size, p.live_size, + p.decoded_size)); + } +}; + +template <> +struct ParamTraits<CacheManager::ResourceTypeStats> { + typedef CacheManager::ResourceTypeStats param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.images); + WriteParam(m, p.css_stylesheets); + WriteParam(m, p.scripts); + WriteParam(m, p.xsl_stylesheets); + WriteParam(m, p.fonts); + } + static bool Read(const Message* m, void** iter, param_type* r) { + bool result = + ReadParam(m, iter, &r->images) && + ReadParam(m, iter, &r->css_stylesheets) && + ReadParam(m, iter, &r->scripts) && + ReadParam(m, iter, &r->xsl_stylesheets) && + ReadParam(m, iter, &r->fonts); + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<WebCoreStats>"); + LogParam(p.images, l); + LogParam(p.css_stylesheets, l); + LogParam(p.scripts, l); + LogParam(p.xsl_stylesheets, l); + LogParam(p.fonts, l); + l->append(L"</WebCoreStats>"); + } +}; + +template <> +struct ParamTraits<XFORM> { + typedef XFORM param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(XFORM)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(XFORM)) { + memcpy(r, data, sizeof(XFORM)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<XFORM>"); + } +}; + +struct WebCursor_Data { + WebCursor::Type cursor_type; + int hotspot_x; + int hotspot_y; + SkBitmap_Data bitmap_info; +}; + +template <> +struct ParamTraits<WebCursor> { + typedef WebCursor param_type; + static void Write(Message* m, const param_type& p) { + const SkBitmap& src_bitmap = p.bitmap(); + WebCursor_Data web_cursor_info; + web_cursor_info.cursor_type = p.type(); + web_cursor_info.hotspot_x = p.hotspot_x(); + web_cursor_info.hotspot_y = p.hotspot_y(); + web_cursor_info.bitmap_info.InitSkBitmapDataForTransfer(src_bitmap); + + size_t fixed_data = sizeof(web_cursor_info); + m->WriteData(reinterpret_cast<const char*>(&web_cursor_info), + static_cast<int>(fixed_data)); + size_t pixel_size = src_bitmap.getSize(); + m->WriteBool(pixel_size != 0); + if (pixel_size) { + SkAutoLockPixels src_bitmap_lock(src_bitmap); + m->WriteData(reinterpret_cast<const char*>(src_bitmap.getPixels()), + static_cast<int>(pixel_size)); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char* fixed_data = NULL; + int fixed_data_size = 0; + if (!m->ReadData(iter, &fixed_data, &fixed_data_size) || + (fixed_data_size <= 0)) { + NOTREACHED(); + return false; + } + DCHECK(fixed_data_size == sizeof(WebCursor_Data)); + + const WebCursor_Data* web_cursor_info = + reinterpret_cast<const WebCursor_Data*>(fixed_data); + + bool variable_data_avail; + if (!m->ReadBool(iter, &variable_data_avail)) { + NOTREACHED(); + return false; + } + + // No variable data indicates that this is not a custom cursor. + if (variable_data_avail) { + const char* variable_data = NULL; + int variable_data_size = 0; + if (!m->ReadData(iter, &variable_data, &variable_data_size) || + variable_data_size <= 0) { + NOTREACHED(); + return false; + } + + SkBitmap dest_bitmap; + web_cursor_info->bitmap_info.InitSkBitmapFromData(&dest_bitmap, + variable_data, + variable_data_size); + r->set_bitmap(dest_bitmap); + r->set_hotspot(web_cursor_info->hotspot_x, web_cursor_info->hotspot_y); + } + + r->set_type(web_cursor_info->cursor_type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<WebCursor>"); + } +}; + +struct LogData { + std::wstring channel; + uint16 type; + std::wstring flags; + int64 sent; // Time that the message was sent (i.e. at Send()). + int64 receive; // Time before it was dispatched (i.e. before calling OnMessageReceived). + int64 dispatch; // Time after it was dispatched (i.e. after calling OnMessageReceived). + std::wstring params; +}; + +template <> +struct ParamTraits<LogData> { + typedef LogData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.channel); + WriteParam(m, static_cast<int>(p.type)); + WriteParam(m, p.flags); + WriteParam(m, p.sent); + WriteParam(m, p.receive); + WriteParam(m, p.dispatch); + WriteParam(m, p.params); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int type; + bool result = + ReadParam(m, iter, &r->channel) && + ReadParam(m, iter, &type) && + ReadParam(m, iter, &r->flags) && + ReadParam(m, iter, &r->sent) && + ReadParam(m, iter, &r->receive) && + ReadParam(m, iter, &r->dispatch) && + ReadParam(m, iter, &r->params); + r->type = static_cast<uint16>(type); + return result; + } + static void Log(const param_type& p, std::wstring* l) { + // Doesn't make sense to implement this! + } +}; + +template <> +struct ParamTraits<Tuple0> { + typedef Tuple0 param_type; + static void Write(Message* m, const param_type& p) { + } + static bool Read(const Message* m, void** iter, param_type* r) { + return true; + } + static void Log(const param_type& p, std::wstring* l) { + } +}; + +template <class A> +struct ParamTraits< Tuple1<A> > { + typedef Tuple1<A> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return ReadParam(m, iter, &r->a); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + } +}; + +template <class A, class B> +struct ParamTraits< Tuple2<A, B> > { + typedef Tuple2<A, B> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + } +}; + +template <class A, class B, class C> +struct ParamTraits< Tuple3<A, B, C> > { + typedef Tuple3<A, B, C> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + l->append(L", "); + LogParam(p.c, l); + } +}; + +template <class A, class B, class C, class D> +struct ParamTraits< Tuple4<A, B, C, D> > { + typedef Tuple4<A, B, C, D> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + l->append(L", "); + LogParam(p.c, l); + l->append(L", "); + LogParam(p.d, l); + } +}; + +template <class A, class B, class C, class D, class E> +struct ParamTraits< Tuple5<A, B, C, D, E> > { + typedef Tuple5<A, B, C, D, E> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + WriteParam(m, p.e); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d) && + ReadParam(m, iter, &r->e)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + l->append(L", "); + LogParam(p.c, l); + l->append(L", "); + LogParam(p.d, l); + l->append(L", "); + LogParam(p.e, l); + } +}; + +template <> +struct ParamTraits<webkit_glue::WebApplicationInfo> { + typedef webkit_glue::WebApplicationInfo param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.title); + WriteParam(m, p.description); + WriteParam(m, p.app_url); + WriteParam(m, p.icons.size()); + for (size_t i = 0; i < p.icons.size(); ++i) { + WriteParam(m, p.icons[i].url); + WriteParam(m, p.icons[i].width); + WriteParam(m, p.icons[i].height); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + size_t icon_count; + bool result = + ReadParam(m, iter, &r->title) && + ReadParam(m, iter, &r->description) && + ReadParam(m, iter, &r->app_url) && + ReadParam(m, iter, &icon_count); + if (!result) + return false; + for (size_t i = 0; i < icon_count && result; ++i) { + param_type::IconInfo icon_info; + result = + ReadParam(m, iter, &icon_info.url) && + ReadParam(m, iter, &icon_info.width) && + ReadParam(m, iter, &icon_info.height); + r->icons.push_back(icon_info); + } + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<WebApplicationInfo>"); + } +}; + + +//----------------------------------------------------------------------------- +// Generic message subclasses + +// Used for asynchronous messages. +template <class Param> +class MessageWithTuple : public Message { + public: + MessageWithTuple(int32 routing_id, WORD type, const Param& p) + : Message(routing_id, type, PRIORITY_NORMAL) { + WriteParam(this, p); + } + + static bool Read(const Message* msg, Param* p) { + void* iter = NULL; + bool rv = ReadParam(msg, &iter, p); + DCHECK(rv) << "Error deserializing message " << msg->type(); + return rv; + } + + // Generic dispatcher. Should cover most cases. + template<class T, class Method> + static bool Dispatch(const Message* msg, T* obj, Method func) { + Param p; + if (Read(msg, &p)) { + DispatchToMethod(obj, func, p); + return true; + } + return false; + } + + // The following dispatchers exist for the case where the callback function + // needs the message as well. They assume that "Param" is a type of Tuple + // (except the one arg case, as there is no Tuple1). + template<class T, typename TA> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p); + return true; + } + return false; + } + + template<class T, typename TA, typename TB> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b); + return true; + } + return false; + } + + template<class T, typename TA, typename TB, typename TC> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB, TC)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c); + return true; + } + return false; + } + + template<class T, typename TA, typename TB, typename TC, typename TD> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB, TC, TD)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d); + return true; + } + return false; + } + + template<class T, typename TA, typename TB, typename TC, typename TD, + typename TE> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB, TC, TD, TE)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d, p.e); + return true; + } + return false; + } + + static void Log(const Message* msg, std::wstring* l) { + Param p; + if (Read(msg, &p)) + LogParam(p, l); + } +}; + +// This class assumes that its template argument is a RefTuple (a Tuple with +// reference elements). +template <class RefTuple> +class ParamDeserializer : public MessageReplyDeserializer { + public: + ParamDeserializer(const RefTuple& out) : out_(out) { } + + bool SerializeOutputParameters(const IPC::Message& msg, void* iter) { + return ReadParam(&msg, &iter, &out_); + } + + RefTuple out_; +}; + +// defined in ipc_logging.cc +void GenerateLogData(const std::wstring& channel, const Message& message, + LogData* data); + +// Used for synchronous messages. +template <class SendParam, class ReplyParam> +class MessageWithReply : public SyncMessage { + public: + MessageWithReply(int32 routing_id, WORD type, + const SendParam& send, const ReplyParam& reply) + : SyncMessage(routing_id, type, PRIORITY_NORMAL, + new ParamDeserializer<ReplyParam>(reply)) { + WriteParam(this, send); + } + + static void Log(const Message* msg, std::wstring* l) { + if (msg->is_sync()) { + SendParam p; + void* iter = SyncMessage::GetDataIterator(msg); + ReadParam(msg, &iter, &p); + LogParam(p, l); + + const std::wstring& output_params = msg->output_params(); + if (!l->empty() && !output_params.empty()) + l->append(L", "); + + l->append(output_params); + } else { + // This is an outgoing reply. Now that we have the output parameters, we + // can finally log the message. + ReplyParam::ValueTuple p; + void* iter = SyncMessage::GetDataIterator(msg); + ReadParam(msg, &iter, &p); + LogParam(p, l); + } + } + + template<class T, class Method> + static bool Dispatch(const Message* msg, T* obj, Method func) { + SendParam send_params; + void* iter = GetDataIterator(msg); + Message* reply = GenerateReply(msg); + bool error; + if (ReadParam(msg, &iter, &send_params)) { + ReplyParam::ValueTuple reply_params; + DispatchToMethod(obj, func, send_params, &reply_params); + WriteParam(reply, reply_params); + error = false; +#ifdef IPC_MESSAGE_LOG_ENABLED + if (msg->received_time() != 0) { + std::wstring output_params; + LogParam(reply_params, &output_params); + msg->set_output_params(output_params); + } +#endif + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + error = true; + } + + obj->Send(reply); + return !error; + } + + template<class T, class Method> + static bool DispatchDelayReply(const Message* msg, T* obj, Method func) { + SendParam send_params; + void* iter = GetDataIterator(msg); + Message* reply = GenerateReply(msg); + bool error; + if (ReadParam(msg, &iter, &send_params)) { + Tuple1<Message&> t = MakeRefTuple(*reply); + +#ifdef IPC_MESSAGE_LOG_ENABLED + if (msg->sent_time()) { + // Don't log the sync message after dispatch, as we don't have the + // output parameters at that point. Instead, save its data and log it + // with the outgoing reply message when it's sent. + LogData* data = new LogData; + GenerateLogData(L"", *msg, data); + msg->set_dont_log(); + reply->set_sync_log_data(data); + } +#endif + DispatchToMethod(obj, func, send_params, &t); + error = false; + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + obj->Send(reply); + error = true; + } + return !error; + } + + template<typename TA> + static void WriteReplyParams(Message* reply, TA a) { + ReplyParam p(a); + WriteParam(reply, p); + } + + template<typename TA, typename TB> + static void WriteReplyParams(Message* reply, TA a, TB b) { + ReplyParam p(a, b); + WriteParam(reply, p); + } + + template<typename TA, typename TB, typename TC> + static void WriteReplyParams(Message* reply, TA a, TB b, TC c) { + ReplyParam p(a, b, c); + WriteParam(reply, p); + } + + template<typename TA, typename TB, typename TC, typename TD> + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d) { + ReplyParam p(a, b, c, d); + WriteParam(reply, p); + } + + template<typename TA, typename TB, typename TC, typename TD, typename TE> + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d, TE e) { + ReplyParam p(a, b, c, d, e); + WriteParam(reply, p); + } +}; + +//----------------------------------------------------------------------------- +} + +#endif // CHROME_COMMON_IPC_MESSAGE_UTILS_H__ diff --git a/chrome/common/ipc_sync_channel.cc b/chrome/common/ipc_sync_channel.cc new file mode 100644 index 0000000..bbd7a23 --- /dev/null +++ b/chrome/common/ipc_sync_channel.cc @@ -0,0 +1,470 @@ +// 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 "chrome/common/ipc_sync_channel.h" + +#include "base/logging.h" +#include "base/thread_local_storage.h" +#include "chrome/common/child_process.h" +#include "chrome/common/ipc_logging.h" +#include "chrome/common/ipc_sync_message.h" + + +namespace IPC { +// When we're blocked in a Send(), we need to process incoming synchronous +// messages right away because it could be blocking our reply (either +// directly from the same object we're calling, or indirectly through one or +// more other channels). That means that in SyncContext's OnMessageReceived, +// we need to process sync message right away if we're blocked. However a +// simple check isn't sufficient, because the listener thread can be in the +// process of calling Send. +// To work around this, when SyncChannel filters a sync message, it sets +// an event that the listener thread waits on during its Send() call. This +// allows us to dispatch incoming sync messages when blocked. The race +// condition is handled because if Send is in the process of being called, it +// will check the event. In case the listener thread isn't sending a message, +// we queue a task on the listener thread to dispatch the received messages. +// The messages are stored in this queue object that's shared among all +// SyncChannel objects on the same thread (since one object can receive a +// sync message while another one is blocked). + +// Holds a pointer to the per-thread ReceivedSyncMsgQueue object. +static int g_tls_index = ThreadLocalStorage::Alloc(); + +class SyncChannel::ReceivedSyncMsgQueue : + public base::RefCountedThreadSafe<ReceivedSyncMsgQueue> { + public: + ReceivedSyncMsgQueue() : + blocking_event_(CreateEvent(NULL, FALSE, FALSE, NULL)), + task_pending_(false), + listener_message_loop_(MessageLoop::current()) { + } + + ~ReceivedSyncMsgQueue() { + CloseHandle(blocking_event_); + ThreadLocalStorage::Set(g_tls_index, NULL); + } + + // Called on IPC thread when a synchronous message or reply arrives. + void QueueMessage(const Message& msg, Channel::Listener* listener, + const std::wstring& channel_id) { + bool was_task_pending; + { + AutoLock auto_lock(message_lock_); + + was_task_pending = task_pending_; + task_pending_ = true; + + // We set the event in case the listener thread is blocked (or is about + // to). In case it's not, the PostTask dispatches the messages. + message_queue_.push(ReceivedMessage(new Message(msg), listener, + channel_id)); + } + + SetEvent(blocking_event_); + if (!was_task_pending) { + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &ReceivedSyncMsgQueue::DispatchMessagesTask)); + } + } + + void QueueReply(const Message &msg, SyncChannel::SyncContext* context) { + AutoLock auto_lock(reply_lock_); + + received_replies_.push_back(Reply(new Message(msg), context)); + } + + // Called on the listerner's thread to process any queues synchronous + // messages. + void DispatchMessagesTask() { + { + AutoLock auto_lock(message_lock_); + task_pending_ = false; + } + DispatchMessages(); + } + + void DispatchMessages() { + while (true) { + Message* message = NULL; + std::wstring channel_id; + Channel::Listener* listener = NULL; + { + AutoLock auto_lock(message_lock_); + if (message_queue_.empty()) + break; + + ReceivedMessage& blocking_msg = message_queue_.front(); + message = blocking_msg.message; + listener = blocking_msg.listener; + channel_id = blocking_msg.channel_id; + message_queue_.pop(); + } + +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging* logger = IPC::Logging::current(); + if (logger->Enabled()) + logger->OnPreDispatchMessage(*message); +#endif + + if (listener) + listener->OnMessageReceived(*message); + +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(*message, channel_id); +#endif + + delete message; + } + } + + // Called on the IPC thread when the current sync Send() call is unblocked. + void OnUnblock() { + bool queued_replies = false; + { + AutoLock auto_lock(reply_lock_); + queued_replies = !received_replies_.empty(); + } + + if (queued_replies) { + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &ReceivedSyncMsgQueue::DispatchReplies)); + } + } + + // SyncChannel calls this in its destructor. + void RemoveListener(Channel::Listener* listener) { + AutoLock auto_lock(message_lock_); + + SyncMessageQueue temp_queue; + while (!message_queue_.empty()) { + if (message_queue_.front().listener != listener) { + temp_queue.push(message_queue_.front()); + } else { + delete message_queue_.front().message; + } + + message_queue_.pop(); + } + + while (!temp_queue.empty()) { + message_queue_.push(temp_queue.front()); + temp_queue.pop(); + } + } + + HANDLE blocking_event() { return blocking_event_; } + + private: + // Called on the ipc thread to check if we can unblock any current Send() + // calls based on a queued reply. + void DispatchReplies() { + AutoLock auto_lock(reply_lock_); + + for (size_t i = 0; i < received_replies_.size(); ++i) { + Message* message = received_replies_[i].message; + if (received_replies_[i].context->UnblockListener(message)) { + delete message; + received_replies_.erase(received_replies_.begin() + i); + return; + } + } + } + + // Set when we got a synchronous message that we must respond to as the + // sender needs its reply before it can reply to our original synchronous + // message. + HANDLE blocking_event_; + + MessageLoop* listener_message_loop_; + + // Holds information about a queued synchronous message. + struct ReceivedMessage { + ReceivedMessage(Message* m, Channel::Listener* l, const std::wstring& i) + : message(m), listener(l), channel_id(i) { } + Message* message; + Channel::Listener* listener; + std::wstring channel_id; + }; + + typedef std::queue<ReceivedMessage> SyncMessageQueue; + SyncMessageQueue message_queue_; + Lock message_lock_; + bool task_pending_; + + // Holds information about a queued reply message. + struct Reply { + Reply(Message* m, SyncChannel::SyncContext* c) + : message(m), + context(c) { } + + Message* message; + scoped_refptr<SyncChannel::SyncContext> context; + }; + + std::vector<Reply> received_replies_; + Lock reply_lock_; +}; + + +SyncChannel::SyncContext::SyncContext( + Channel::Listener* listener, + MessageFilter* filter, + MessageLoop* ipc_thread) + : ChannelProxy::Context(listener, filter, ipc_thread), + channel_closed_(false), + reply_deserialize_result_(false) { + // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple + // SyncChannel objects that can block the same thread). + received_sync_msgs_ = static_cast<ReceivedSyncMsgQueue*>( + ThreadLocalStorage::Get(g_tls_index)); + + if (!received_sync_msgs_.get()) { + // Stash a pointer to the listener thread's ReceivedSyncMsgQueue, as we + // need to be able to access it in the IPC thread. + received_sync_msgs_ = new ReceivedSyncMsgQueue(); + ThreadLocalStorage::Set(g_tls_index, received_sync_msgs_.get()); + } +} + +SyncChannel::SyncContext::~SyncContext() { + while (!deserializers_.empty()) + PopDeserializer(); +} + +// Adds information about an outgoing sync message to the context so that +// we know how to deserialize the reply. Returns a handle that's set when +// the reply has arrived. +HANDLE SyncChannel::SyncContext::Push(IPC::SyncMessage* sync_msg) { + PendingSyncMsg pending(IPC::SyncMessage::GetMessageId(*sync_msg), + sync_msg->GetReplyDeserializer(), + CreateEvent(NULL, FALSE, FALSE, NULL)); + AutoLock auto_lock(deserializers_lock_); + deserializers_.push(pending); + + return pending.reply_event; +} + +HANDLE SyncChannel::SyncContext::blocking_event() { + return received_sync_msgs_->blocking_event(); +} + +void SyncChannel::SyncContext::DispatchMessages() { + received_sync_msgs_->DispatchMessages(); +} + +void SyncChannel::SyncContext::RemoveListener(Channel::Listener* listener) { + received_sync_msgs_->RemoveListener(listener); +} + +bool SyncChannel::SyncContext::UnblockListener(const Message* msg) { + bool rv = false; + HANDLE reply_event = NULL; + { + AutoLock auto_lock(deserializers_lock_); + if (channel_closed_) { + // The channel is closed, or we couldn't connect, so cancel all Send() + // calls. + reply_deserialize_result_ = false; + if (!deserializers_.empty()) { + reply_event = deserializers_.top().reply_event; + PopDeserializer(); + } + } else { + if (deserializers_.empty()) + return false; + + if (!IPC::SyncMessage::IsMessageReplyTo(*msg, deserializers_.top().id)) + return false; + + rv = true; + if (msg->is_reply_error()) { + reply_deserialize_result_ = false; + } else { + reply_deserialize_result_ = + deserializers_.top().deserializer->SerializeOutputParameters(*msg); + } + + // Can't CloseHandle the event just yet, since doing so might cause the + // Wait call above to never return. + reply_event = deserializers_.top().reply_event; + PopDeserializer(); + } + } + + if (reply_event) + SetEvent(reply_event); + + // We got a reply to a synchronous Send() call that's blocking the listener + // thread. However, further down the call stack there could be another + // blocking Send() call, whose reply we received after we made this last + // Send() call. So check if we have any queued replies available that + // can now unblock the listener thread. + received_sync_msgs_->OnUnblock(); + + return rv; +} + +// Called on the IPC thread. +void SyncChannel::SyncContext::OnMessageReceived(const Message& msg) { + if (UnblockListener(&msg)) + return; + + if (msg.should_unblock()) { + received_sync_msgs_->QueueMessage(msg, listener(), channel_id()); + return; + } + + if (msg.is_reply()) { + received_sync_msgs_->QueueReply(msg, this); + return; + } + + return Context::OnMessageReceived(msg); +} + +// Called on the IPC thread. +void SyncChannel::SyncContext::OnChannelError() { + channel_closed_ = true; + UnblockListener(NULL); + + Context::OnChannelError(); +} + +void SyncChannel::SyncContext::PopDeserializer() { + delete deserializers_.top().deserializer; + deserializers_.pop(); +} + +SyncChannel::SyncChannel(const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, + MessageLoop* ipc_message_loop, + bool create_pipe_now) + : ChannelProxy(channel_id, mode, listener, NULL, ipc_message_loop, + new SyncContext(listener, NULL, ipc_message_loop), + create_pipe_now), + shutdown_event_(ChildProcess::GetShutDownEvent()) { + DCHECK(shutdown_event_ != NULL); +} + +SyncChannel::~SyncChannel() { + // The listener ensures that its lifetime is greater than SyncChannel. But + // after SyncChannel is destructed there's no guarantee that the listener is + // still around, so we wouldn't want ReceivedSyncMsgQueue to call the + // listener. + sync_context()->RemoveListener(listener()); +} + +bool SyncChannel::Send(IPC::Message* message) { + bool message_is_sync = message->is_sync(); + HANDLE pump_messages_event = NULL; + + HANDLE reply_event = NULL; + if (message_is_sync) { + IPC::SyncMessage* sync_msg = static_cast<IPC::SyncMessage*>(message); + reply_event = sync_context()->Push(sync_msg); + pump_messages_event = sync_msg->pump_messages_event(); + } + + // Send the message using the ChannelProxy + ChannelProxy::Send(message); + if (!message_is_sync) + return true; + + do { + // Wait for reply, or for any other incoming synchronous message. + DCHECK(reply_event != NULL); + HANDLE objects[] = { shutdown_event_, + reply_event, + sync_context()->blocking_event(), + pump_messages_event}; + + DWORD result; + if (pump_messages_event == NULL) { + // No need to pump messages since we didn't get an event to check. + result = WaitForMultipleObjects(3, objects, FALSE, INFINITE); + } else { + // If the event is set, then we pump messages. Otherwise we also wait on + // it so that if it gets set we start pumping messages. + if (WaitForSingleObject(pump_messages_event, 0) == WAIT_OBJECT_0) { + result = MsgWaitForMultipleObjects(3, objects, FALSE, INFINITE, + QS_ALLINPUT); + } else { + result = WaitForMultipleObjects(4, objects, FALSE, INFINITE); + } + } + + if (result == WAIT_OBJECT_0) { + // Process shut down before we can get a reply to a synchronous message. + // Unblock the thread. + // Leak reply_event. Since we're shutting down, it's not a big deal. + return false; + } + + if (result == WAIT_OBJECT_0 + 1) { + // We got the reply to our synchronous message. + CloseHandle(reply_event); + return sync_context()->reply_deserialize_result(); + } + + if (result == WAIT_OBJECT_0 + 2) { + // We're waiting for a reply, but we received a blocking synchronous + // call. We must process it or otherwise a deadlock might occur. + sync_context()->DispatchMessages(); + } else if (result == WAIT_OBJECT_0 + 3) { + // Run a nested messsage loop to pump all the thread's messages. We + // shutdown the nested loop when there are no more messages. + pump_messages_events_.push(pump_messages_event); + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + // Insert a Quit message so that we just flush the MessageLoop without + // letting the MessageLoop wait for more messages. + MessageLoop::current()->Quit(); + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->Run(); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + pump_messages_events_.pop(); + } else { + DCHECK(result == WAIT_OBJECT_0 + 4); + // We were doing a WaitForMultipleObjects, but now the pump messages + // event is set, so the next time we loop we'll use + // MsgWaitForMultipleObjects instead. + } + + // Continue looping until we get the reply to our synchronous message. + } while (true); +} + +bool SyncChannel::UnblockListener(Message* message) { + return sync_context()->UnblockListener(message); +} + +} // namespace IPC diff --git a/chrome/common/ipc_sync_channel.h b/chrome/common/ipc_sync_channel.h new file mode 100644 index 0000000..af6e153 --- /dev/null +++ b/chrome/common/ipc_sync_channel.h @@ -0,0 +1,138 @@ +// 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 CHROME_COMMON_IPC_SYNC_SENDER_H__ +#define CHROME_COMMON_IPC_SYNC_SENDER_H__ + +#include <windows.h> +#include <string> +#include <stack> +#include <queue> +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#include "chrome/common/ipc_channel_proxy.h" + +namespace IPC { + +class SyncMessage; + +// This is similar to IPC::ChannelProxy, with the added feature of supporting +// sending synchronous messages. +// Note that care must be taken that the lifetime of the ipc_thread argument +// is more than this object. If the message loop goes away while this object +// is running and it's used to send a message, then it will use the invalid +// message loop pointer to proxy it to the ipc thread. +class SyncChannel : public ChannelProxy { + public: + SyncChannel(const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, MessageLoop* ipc_message_loop, + bool create_pipe_now); + ~SyncChannel(); + + virtual bool Send(Message* message); + bool UnblockListener(Message* message); + + protected: + class ReceivedSyncMsgQueue; + friend class ReceivedSyncMsgQueue; + + // SyncContext holds the per object data for SyncChannel, so that SyncChannel + // can be deleted while it's being used in a different thread. See + // ChannelProxy::Context for more information. + class SyncContext : public Context { + public: + SyncContext(Channel::Listener* listener, + MessageFilter* filter, + MessageLoop* ipc_thread); + + ~SyncContext(); + + // Adds information about an outgoing sync message to the context so that + // we know how to deserialize the reply. Returns a handle that's set when + // the reply has arrived. + HANDLE Push(IPC::SyncMessage* sync_msg); + + // Returns true if the reply message was deserialized without any errors, + // or false otherwise. + bool reply_deserialize_result() { return reply_deserialize_result_; } + + // Returns an event that's set when an incoming message that's not the reply + // needs to get dispatched (by calling SyncContext::DispatchMessages). + HANDLE blocking_event(); + + void DispatchMessages(); + void RemoveListener(Channel::Listener* listener); + + // Checks if the given message is blocking the listener thread because of a + // synchronous send. If it is, the thread is unblocked and true is returned. + // Otherwise the function returns false. + bool UnblockListener(const Message* msg); + + private: + void OnMessageReceived(const Message& msg); + void OnChannelError(); + + // When sending a synchronous message, this structure contains an object that + // knows how to deserialize the response. + struct PendingSyncMsg { + PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d, HANDLE e) : + id(id), deserializer(d), reply_event(e) { } + int id; + IPC::MessageReplyDeserializer* deserializer; + HANDLE reply_event; + }; + + // Cleanly remove the top deserializer (and throw it away). + void PopDeserializer(); + + typedef std::stack<PendingSyncMsg> PendingSyncMessageQueue; + PendingSyncMessageQueue deserializers_; + Lock deserializers_lock_; + + scoped_refptr<ReceivedSyncMsgQueue> received_sync_msgs_; + + bool channel_closed_; + bool reply_deserialize_result_; + }; + + private: + SyncContext* sync_context() { return reinterpret_cast<SyncContext*>(context()); } + + // Copy of shutdown event that we get in constructor. + HANDLE shutdown_event_; + + std::stack<HANDLE> pump_messages_events_; + + DISALLOW_EVIL_CONSTRUCTORS(SyncChannel); +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_SYNC_SENDER_H__ diff --git a/chrome/common/ipc_sync_channel_unittest.cc b/chrome/common/ipc_sync_channel_unittest.cc new file mode 100644 index 0000000..74f046a --- /dev/null +++ b/chrome/common/ipc_sync_channel_unittest.cc @@ -0,0 +1,625 @@ +// 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. +// +// Unit test for SyncChannel. + +#include <windows.h> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/common/child_process.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_sync_channel.h" +#include "chrome/common/stl_util-inl.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define IPC_MESSAGE_MACROS_ENUMS +#include "chrome/common/ipc_sync_channel_unittest.h" + +// define the classes +#define IPC_MESSAGE_MACROS_CLASSES +#include "chrome/common/ipc_sync_channel_unittest.h" + +using namespace IPC; + +// SyncChannel should only be used in child processes as we don't want to hang +// the browser. So in the unit test we need to have a ChildProcess object. +class TestProcess : public ChildProcess { + public: + explicit TestProcess(const std::wstring& channel_name) {} + static void GlobalInit() { + ChildProcessFactory<TestProcess> factory; + ChildProcess::GlobalInit(L"blah", &factory); + } +}; + +// Wrapper around an event handle. +class Event { + public: + Event() : handle_(CreateEvent(NULL, FALSE, FALSE, NULL)) { } + ~Event() { CloseHandle(handle_); } + void Set() { SetEvent(handle_); } + void Wait() { WaitForSingleObject(handle_, INFINITE); } + HANDLE handle() { return handle_; } + + private: + HANDLE handle_; + + DISALLOW_EVIL_CONSTRUCTORS(Event); +}; + +// Base class for a "process" with listener and IPC threads. +class Worker : public Channel::Listener, public Message::Sender { + public: + // Will create a channel without a name. + Worker(Channel::Mode mode, const std::string& thread_name) + : channel_name_(), + mode_(mode), + ipc_thread_((thread_name + "_ipc").c_str()), + listener_thread_((thread_name + "_listener").c_str()), + overrided_thread_(NULL) { } + + // Will create a named channel and use this name for the threads' name. + Worker(const std::wstring& channel_name, Channel::Mode mode) + : channel_name_(channel_name), + mode_(mode), + ipc_thread_((WideToUTF8(channel_name) + "_ipc").c_str()), + listener_thread_((WideToUTF8(channel_name) + "_listener").c_str()), + overrided_thread_(NULL) { } + + // The IPC thread needs to outlive SyncChannel, so force the correct order of + // destruction. + virtual ~Worker() { + CloseChannel(); + // We must stop the threads and release the channel here. The IPC thread + // must die before the listener thread, otherwise if its in the process of + // sending a message, it will get an error, it will use channel_, which + // references listener_. There are many ways of crashing, depending on + // timing. + // This is a race condition so you may not see it all the time even if you + // reverse the Stop() calls. You may see this bug with AppVerifier only. + ipc_thread_.Stop(); + listener_thread_.Stop(); + channel_.reset(); + } + void AddRef() { } + void Release() { } + bool Send(Message* msg) { return channel_->Send(msg); } + void WaitForChannelCreation() { channel_created_.Wait(); } + void CloseChannel() { channel_.reset(); } + void Start() { + listener_thread_.Start(); + Thread* thread = overrided_thread_ ? overrided_thread_ : &listener_thread_; + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &Worker::OnStart)); + } + void OverrideThread(Thread* overrided_thread) { + DCHECK(overrided_thread_ == NULL); + overrided_thread_ = overrided_thread; + } + Channel::Mode mode() { return mode_; } + HANDLE done_event() { return done_.handle(); } + + protected: + // Derived classes need to call this when they've completed their part of + // the test. + void Done() { done_.Set(); } + + // Functions for dervied classes to implement if they wish. + virtual void Run() { } + virtual void OnDouble(int in, int* out) { NOTREACHED(); } + virtual void OnAnswer(int* answer) { NOTREACHED(); } + virtual void OnAnswerDelay(Message* reply_msg) { + // The message handler map below can only take one entry for + // SyncChannelTestMsg_AnswerToLife, so since some classes want + // the normal version while other want the delayed reply, we + // call the normal version if the derived class didn't override + // this function. + int answer; + OnAnswer(&answer); + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, answer); + Send(reply_msg); + } + + private: + // Called on the listener thread to create the sync channel. + void OnStart() { + ipc_thread_.Start(); + // Link ipc_thread_, listener_thread_ and channel_ altogether. + channel_.reset(new SyncChannel( + channel_name_, mode_, this, ipc_thread_.message_loop(), true)); + channel_created_.Set(); + Run(); + } + + void OnMessageReceived(const Message& message) { + IPC_BEGIN_MESSAGE_MAP(Worker, message) + IPC_MESSAGE_HANDLER(SyncChannelTestMsg_Double, OnDouble) + IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_AnswerToLife, + OnAnswerDelay) + IPC_END_MESSAGE_MAP() + } + + Event done_; + Event channel_created_; + std::wstring channel_name_; + Channel::Mode mode_; + scoped_ptr<SyncChannel> channel_; + Thread ipc_thread_; + Thread listener_thread_; + Thread* overrided_thread_; + + DISALLOW_EVIL_CONSTRUCTORS(Worker); +}; + + +// Starts the test with the given workers. This function deletes the workers +// when it's done. +void RunTest(std::vector<Worker*> workers) { + TestProcess::GlobalInit(); + + // First we create the workers that are channel servers, or else the other + // workers' channel initialization might fail because the pipe isn't created.. + for (size_t i = 0; i < workers.size(); ++i) { + if (workers[i]->mode() == Channel::MODE_SERVER) { + workers[i]->Start(); + workers[i]->WaitForChannelCreation(); + } + } + + // now create the clients + for (size_t i = 0; i < workers.size(); ++i) { + if (workers[i]->mode() == Channel::MODE_CLIENT) + workers[i]->Start(); + } + + // wait for all the workers to finish + std::vector<HANDLE> done_handles; + for (size_t i = 0; i < workers.size(); ++i) + done_handles.push_back(workers[i]->done_event()); + + int count = static_cast<int>(done_handles.size()); + WaitForMultipleObjects(count, &done_handles.front(), TRUE, INFINITE); + STLDeleteContainerPointers(workers.begin(), workers.end()); +} + + +//----------------------------------------------------------------------------- +class SimpleServer : public Worker { + public: + SimpleServer() : Worker(Channel::MODE_SERVER, "simpler_server") { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + Done(); + } +}; + +class SimpleClient : public Worker { + public: + SimpleClient() : Worker(Channel::MODE_CLIENT, "simple_client") { } + + void OnAnswer(int* answer) { + *answer = 42; + Done(); + } +}; + +// Tests basic synchronous call +TEST(IPCSyncChannelTest, Simple) { + std::vector<Worker*> workers; + workers.push_back(new SimpleServer()); + workers.push_back(new SimpleClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class DelayClient : public Worker { + public: + DelayClient() : Worker(Channel::MODE_CLIENT, "delay_client") { } + + void OnAnswerDelay(Message* reply_msg) { + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + Done(); + } +}; + +// Tests that asynchronous replies work +TEST(IPCSyncChannelTest, DelayReply) { + std::vector<Worker*> workers; + workers.push_back(new SimpleServer()); + workers.push_back(new DelayClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class NoHangServer : public Worker { + public: + explicit NoHangServer(Event* got_first_reply) + : Worker(Channel::MODE_SERVER, "no_hang_server"), + got_first_reply_(got_first_reply) { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + got_first_reply_->Set(); + + result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(!result); + Done(); + } + + Event* got_first_reply_; +}; + +class NoHangClient : public Worker { + public: + explicit NoHangClient(Event* got_first_reply) + : Worker(Channel::MODE_CLIENT, "no_hang_client"), + got_first_reply_(got_first_reply) { } + + virtual void OnAnswerDelay(Message* reply_msg) { + // Use the DELAY_REPLY macro so that we can force the reply to be sent + // before this function returns (when the channel will be reset). + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + got_first_reply_->Wait(); + CloseChannel(); + Done(); + } + + Event* got_first_reply_; +}; + +// Tests that caller doesn't hang if receiver dies +TEST(IPCSyncChannelTest, NoHang) { + Event got_first_reply; + + std::vector<Worker*> workers; + workers.push_back(new NoHangServer(&got_first_reply)); + workers.push_back(new NoHangClient(&got_first_reply)); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class RecursiveServer : public Worker { + public: + RecursiveServer() : Worker(Channel::MODE_SERVER, "recursive_server") { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + Done(); + } + + void OnDouble(int in, int* out) { + *out = in * 2; + } +}; + +class RecursiveClient : public Worker { + public: + RecursiveClient() : Worker(Channel::MODE_CLIENT, "recursive_client") { } + + void OnAnswer(int* answer) { + BOOL result = Send(new SyncChannelTestMsg_Double(21, answer)); + DCHECK(result); + Done(); + } +}; + +// Tests that the caller unblocks to answer a sync message from the receiver. +TEST(IPCSyncChannelTest, Recursive) { + std::vector<Worker*> workers; + workers.push_back(new RecursiveServer()); + workers.push_back(new RecursiveClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class MultipleServer1 : public Worker { + public: + MultipleServer1() : Worker(L"test_channel1", Channel::MODE_SERVER) { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_Double(5, &answer)); + DCHECK(result); + DCHECK(answer == 10); + Done(); + } +}; + +class MultipleClient1 : public Worker { + public: + MultipleClient1(Event* client1_msg_received, Event* client1_can_reply) : + Worker(L"test_channel1", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + client1_can_reply_(client1_can_reply) { } + + void OnDouble(int in, int* out) { + client1_msg_received_->Set(); + *out = in * 2; + client1_can_reply_->Wait(); + Done(); + } + + private: + Event *client1_msg_received_, *client1_can_reply_; +}; + +class MultipleServer2 : public Worker { + public: + MultipleServer2() : Worker(L"test_channel2", Channel::MODE_SERVER) { } + + void OnAnswer(int* result) { + *result = 42; + Done(); + } +}; + +class MultipleClient2 : public Worker { + public: + MultipleClient2(Event* client1_msg_received, Event* client1_can_reply) : + Worker(L"test_channel2", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + client1_can_reply_(client1_can_reply) { } + + void Run() { + int answer = 0; + client1_msg_received_->Wait(); + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + client1_can_reply_->Set(); + Done(); + } + + private: + Event *client1_msg_received_, *client1_can_reply_; +}; + +// Tests that multiple SyncObjects on the same listener thread can unblock each +// other. +TEST(IPCSyncChannelTest, Multiple) { + std::vector<Worker*> workers; + + // A shared worker thread so that server1 and server2 run on one thread. + Thread worker_thread("Multiple"); + worker_thread.Start(); + + // Server1 sends a sync msg to client1, which blocks the reply until + // server2 (which runs on the same worker thread as server1) responds + // to a sync msg from client2. + Event client1_msg_received, client1_can_reply; + + Worker* worker; + + worker = new MultipleServer2(); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new MultipleClient2( + &client1_msg_received, &client1_can_reply); + workers.push_back(worker); + + worker = new MultipleServer1(); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new MultipleClient1( + &client1_msg_received, &client1_can_reply); + workers.push_back(worker); + + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class QueuedReplyServer1 : public Worker { + public: + QueuedReplyServer1() : Worker(L"test_channel1", Channel::MODE_SERVER) { } + void Run() { + int answer = 0; + bool result = Send(new SyncChannelTestMsg_Double(5, &answer)); + DCHECK(result); + DCHECK(answer == 10); + Done(); + } +}; + +class QueuedReplyClient1 : public Worker { + public: + QueuedReplyClient1(Event* client1_msg_received, Event* server2_can_reply) : + Worker(L"test_channel1", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + server2_can_reply_(server2_can_reply) { } + + void OnDouble(int in, int* out) { + client1_msg_received_->Set(); + *out = in * 2; + server2_can_reply_->Wait(); + Done(); + } + + private: + Event *client1_msg_received_, *server2_can_reply_; +}; + +class QueuedReplyServer2 : public Worker { + public: + explicit QueuedReplyServer2(Event* server2_can_reply) : + Worker(L"test_channel2", Channel::MODE_SERVER), + server2_can_reply_(server2_can_reply) { } + + void OnAnswer(int* result) { + server2_can_reply_->Set(); + + // give client1's reply time to reach the server listener thread + Sleep(200); + + *result = 42; + Done(); + } + + Event *server2_can_reply_; +}; + +class QueuedReplyClient2 : public Worker { + public: + explicit QueuedReplyClient2(Event* client1_msg_received) : + Worker(L"test_channel2", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received) { } + + void Run() { + int answer = 0; + client1_msg_received_->Wait(); + bool result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + Done(); + } + + private: + Event *client1_msg_received_; +}; + +// While a blocking send is in progress, the listener thread might answer other +// synchronous messages. This tests that if during the response to another +// message the reply to the original messages comes, it is queued up correctly +// and the original Send is unblocked later. +TEST(IPCSyncChannelTest, QueuedReply) { + std::vector<Worker*> workers; + + // A shared worker thread so that server1 and server2 run on one thread. + Thread worker_thread("QueuedReply"); + worker_thread.Start(); + + Event client1_msg_received, server2_can_reply; + + Worker* worker; + + worker = new QueuedReplyServer2(&server2_can_reply); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new QueuedReplyClient2(&client1_msg_received); + workers.push_back(worker); + + worker = new QueuedReplyServer1(); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new QueuedReplyClient1( + &client1_msg_received, &server2_can_reply); + workers.push_back(worker); + + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class BadServer : public Worker { + public: + BadServer() : Worker(Channel::MODE_SERVER, "simpler_server") { } + void Run() { + int answer = 0; + + Message* msg = new SyncMessage(MSG_ROUTING_CONTROL, + SyncChannelTestMsg_Double::ID, + Message::PRIORITY_NORMAL, + NULL); + // Temporarily set the minimum logging very high so that the assertion + // in ipc_message_utils doesn't fire. + int log_level = logging::GetMinLogLevel(); + logging::SetMinLogLevel(kint32max); + bool result = Send(msg); + logging::SetMinLogLevel(log_level); + DCHECK(!result); + + // Need to send another message to get the client to call Done(). + result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + + Done(); + } +}; + +// Tests that if a message is not serialized correctly, the Send() will fail. +TEST(IPCSyncChannelTest, BadMessage) { + std::vector<Worker*> workers; + workers.push_back(new BadServer()); + workers.push_back(new SimpleClient()); + RunTest(workers); +} + + +//----------------------------------------------------------------------------- +class ChattyRecursiveClient : public Worker { + public: + ChattyRecursiveClient() : + Worker(Channel::MODE_CLIENT, "chatty_recursive_client") { } + + void OnAnswer(int* answer) { + // The PostMessage limit is 10k. Send 20% more than that. + const int kMessageLimit = 10000; + const int kMessagesToSend = kMessageLimit * 120 / 100; + for (int i = 0; i < kMessagesToSend; ++i) { + bool result = Send(new SyncChannelTestMsg_Double(21, answer)); + DCHECK(result); + if (!result) + break; + } + Done(); + } +}; + +// Tests http://b/issue?id=1093251 - that sending lots of sync messages while +// the receiver is waiting for a sync reply does not overflow the PostMessage +// queue. +TEST(IPCSyncChannelTest, ChattyServer) { + std::vector<Worker*> workers; + workers.push_back(new RecursiveServer()); + workers.push_back(new ChattyRecursiveClient()); + RunTest(workers); +} diff --git a/chrome/common/ipc_sync_channel_unittest.h b/chrome/common/ipc_sync_channel_unittest.h new file mode 100644 index 0000000..950c656 --- /dev/null +++ b/chrome/common/ipc_sync_channel_unittest.h @@ -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 "chrome/common/ipc_message_macros.h" + +// Messages used for IPC::SyncChannel unit test +IPC_BEGIN_MESSAGES(SyncChannelTest, 9) + IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_NoArgs) + + IPC_SYNC_MESSAGE_CONTROL0_1(SyncChannelTestMsg_AnswerToLife, + int /* answer */) + + IPC_SYNC_MESSAGE_CONTROL1_1(SyncChannelTestMsg_Double, + int /* in */, + int /* out */) + +IPC_END_MESSAGES(SyncChannelTest) diff --git a/chrome/common/ipc_sync_message.cc b/chrome/common/ipc_sync_message.cc new file mode 100644 index 0000000..43d6d5e --- /dev/null +++ b/chrome/common/ipc_sync_message.cc @@ -0,0 +1,140 @@ +// 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 <stack> + +#include "chrome/common/ipc_sync_message.h" +#include "base/logging.h" + +namespace IPC { + +uint32 SyncMessage::next_id_ = 0; +#define kSyncMessageHeaderSize 4 + + +SyncMessage::SyncMessage( + int32 routing_id, + WORD type, + PriorityValue priority, + MessageReplyDeserializer* deserializer) + : Message(routing_id, type, priority), + deserializer_(deserializer), + pump_messages_event_(NULL) { + set_sync(); + set_unblock(true); + + // Add synchronous message data before the message payload. + SyncHeader header; + header.message_id = ++next_id_; + WriteSyncHeader(this, header); +} + +MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() { + MessageReplyDeserializer* rv = deserializer_; + DCHECK(rv); + deserializer_ = NULL; + return rv; +} + +bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) { + if (!msg.is_reply()) + return false; + + return GetMessageId(msg) == request_id; +} + +void* SyncMessage::GetDataIterator(const Message* msg) { + void* iter = const_cast<char*>(msg->payload()); + UpdateIter(&iter, kSyncMessageHeaderSize); + return iter; +} + +int SyncMessage::GetMessageId(const Message& msg) { + if (!msg.is_sync() && !msg.is_reply()) + return 0; + + SyncHeader header; + if (!ReadSyncHeader(msg, &header)) + return 0; + + return header.message_id; +} + +Message* SyncMessage::GenerateReply(const Message* msg) { + DCHECK(msg->is_sync()); + + Message* reply = new Message(msg->routing_id(), IPC_REPLY_ID, msg->priority()); + reply->set_reply(); + + SyncHeader header; + + // use the same message id, but this time reply bit is set + header.message_id = GetMessageId(*msg); + WriteSyncHeader(reply, header); + + return reply; +} + +bool SyncMessage::ReadSyncHeader(const Message& msg, SyncHeader* header) { + DCHECK(msg.is_sync() || msg.is_reply()); + + void* iter = NULL; + bool result = msg.ReadInt(&iter, &header->message_id); + if (!result) { + NOTREACHED(); + return false; + } + + return true; +} + +bool SyncMessage::WriteSyncHeader(Message* msg, const SyncHeader& header) { + DCHECK(msg->is_sync() || msg->is_reply()); + DCHECK(msg->payload_size() == 0); + + void* iter = NULL; + bool result = msg->WriteInt(header.message_id); + if (!result) { + NOTREACHED(); + return false; + } + + // Note: if you add anything here, you need to update kSyncMessageHeaderSize. + DCHECK(kSyncMessageHeaderSize == msg->payload_size()); + + return true; +} + + +bool MessageReplyDeserializer::SerializeOutputParameters(const Message& msg) { + return SerializeOutputParameters(msg, SyncMessage::GetDataIterator(&msg)); +} + +} // namespace IPC diff --git a/chrome/common/ipc_sync_message.h b/chrome/common/ipc_sync_message.h new file mode 100644 index 0000000..bcf47cbe --- /dev/null +++ b/chrome/common/ipc_sync_message.h @@ -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. + +#ifndef CHROME_COMMON_IPC_SYNC_MESSAGE_H__ +#define CHROME_COMMON_IPC_SYNC_MESSAGE_H__ + +#include <windows.h> +#include <string> +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" + +namespace IPC { + +class MessageReplyDeserializer; + +class SyncMessage : public Message { + public: + SyncMessage(int32 routing_id, WORD type, PriorityValue priority, + MessageReplyDeserializer* deserializer); + + // Call this to get a deserializer for the output parameters. + // Note that this can only be called once, and the caller is responsible + // for deleting the deserializer when they're done. + MessageReplyDeserializer* GetReplyDeserializer(); + + // If this message can cause the receiver to block while waiting for user + // input (i.e. by calling MessageBox), then the caller needs to pump window + // messages and dispatch asynchronous messages while waiting for the reply. + // If this handle is passed in, then window messages will be pumped while + // it's set. The handle must be valid until after the Send call returns. + void set_pump_messages_event(HANDLE event) { + pump_messages_event_ = event; + if (event) { + header()->flags |= PUMPING_MSGS_BIT; + } else { + header()->flags &= ~PUMPING_MSGS_BIT; + } + } + + HANDLE pump_messages_event() const { return pump_messages_event_; } + + // Returns true if the message is a reply to the given request id. + static bool IsMessageReplyTo(const Message& msg, int request_id); + + // Given a reply message, returns an iterator to the beginning of the data + // (i.e. skips over the synchronous specific data). + static void* GetDataIterator(const Message* msg); + + // Given a synchronous message (or its reply), returns its id. + static int GetMessageId(const Message& msg); + + // Generates a reply message to the given message. + static Message* GenerateReply(const Message* msg); + + private: + struct SyncHeader { + // unique ID (unique per sender) + int message_id; + }; + + static bool ReadSyncHeader(const Message& msg, SyncHeader* header); + static bool WriteSyncHeader(Message* msg, const SyncHeader& header); + + MessageReplyDeserializer* deserializer_; + HANDLE pump_messages_event_; + + static uint32 next_id_; // for generation of unique ids +}; + +// Used to deserialize parameters from a reply to a synchronous message +class MessageReplyDeserializer { + public: + bool SerializeOutputParameters(const Message& msg); + private: + // Derived classes need to implement this, using the given iterator (which + // is skipped past the header for synchronous messages). + virtual bool SerializeOutputParameters(const Message& msg, void* iter) = 0; +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_SYNC_MESSAGE_H__ diff --git a/chrome/common/ipc_sync_message_unittest.cc b/chrome/common/ipc_sync_message_unittest.cc new file mode 100644 index 0000000..b373b76 --- /dev/null +++ b/chrome/common/ipc_sync_message_unittest.cc @@ -0,0 +1,277 @@ +// 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. +// +// Unit test to make sure that the serialization of synchronous IPC messages +// works. This ensures that the macros and templates were defined correctly. +// Doesn't test the IPC channel mechanism. + +#include <string.h> + +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define IPC_MESSAGE_MACROS_ENUMS +#include "chrome/common/ipc_sync_message_unittest.h" + +// define the classes +#define IPC_MESSAGE_MACROS_CLASSES +#include "chrome/common/ipc_sync_message_unittest.h" + + +static IPC::Message* g_reply; + +class TestMessageReceiver { + public: + + void On_0_1(bool* out1) { + *out1 = false; + } + + void On_0_2(bool* out1, int* out2) { + *out1 = true; + *out2 = 2; + } + + void On_0_3(bool* out1, int* out2, std::string* out3) { + *out1 = false; + *out2 = 3; + *out3 = "0_3"; + } + + void On_1_1(int in1, bool* out1) { + DCHECK(in1 == 1); + *out1 = true; + } + + void On_1_2(bool in1, bool* out1, int* out2) { + DCHECK(!in1); + *out1 = true; + *out2 = 12; + } + + void On_1_3(int in1, std::string* out1, int* out2, bool* out3) { + DCHECK(in1 == 3); + *out1 = "1_3"; + *out2 = 13; + *out3 = false; + } + + void On_2_1(int in1, bool in2, bool* out1) { + DCHECK(in1 == 1 && !in2); + *out1 = true; + } + + void On_2_2(bool in1, int in2, bool* out1, int* out2) { + DCHECK(!in1 && in2 == 2); + *out1 = true; + *out2 = 22; + } + + void On_2_3(int in1, bool in2, std::string* out1, int* out2, bool* out3) { + DCHECK(in1 == 3 && in2); + *out1 = "2_3"; + *out2 = 23; + *out3 = false; + } + + void On_3_1(int in1, bool in2, std::string in3, bool* out1) { + DCHECK(in1 == 1 && !in2 && in3 == "3_1"); + *out1 = true; + } + + void On_3_2(std::string in1, bool in2, int in3, bool* out1, int* out2) { + DCHECK(in1 == "3_2" && !in2 && in3 == 2); + *out1 = true; + *out2 = 32; + } + + void On_3_3(int in1, std::string in2, bool in3, std::string* out1, int* out2, bool* out3) { + DCHECK(in1 == 3 && in2 == "3_3" && in3); + *out1 = "3_3"; + *out2 = 33; + *out3 = false; + } + + bool Send(IPC::Message* message) { + // gets the reply message, stash in global + DCHECK(g_reply == NULL); + g_reply = message; + return true; + } + + void OnMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(TestMessageReceiver, msg) + IPC_MESSAGE_HANDLER(Msg_C_0_1, On_0_1) + IPC_MESSAGE_HANDLER(Msg_C_0_2, On_0_2) + IPC_MESSAGE_HANDLER(Msg_C_0_3, On_0_3) + IPC_MESSAGE_HANDLER(Msg_C_1_1, On_1_1) + IPC_MESSAGE_HANDLER(Msg_C_1_2, On_1_2) + IPC_MESSAGE_HANDLER(Msg_C_1_3, On_1_3) + IPC_MESSAGE_HANDLER(Msg_C_2_1, On_2_1) + IPC_MESSAGE_HANDLER(Msg_C_2_2, On_2_2) + IPC_MESSAGE_HANDLER(Msg_C_2_3, On_2_3) + IPC_MESSAGE_HANDLER(Msg_C_3_1, On_3_1) + IPC_MESSAGE_HANDLER(Msg_C_3_2, On_3_2) + IPC_MESSAGE_HANDLER(Msg_C_3_3, On_3_3) + IPC_MESSAGE_HANDLER(Msg_R_0_1, On_0_1) + IPC_MESSAGE_HANDLER(Msg_R_0_2, On_0_2) + IPC_MESSAGE_HANDLER(Msg_R_0_3, On_0_3) + IPC_MESSAGE_HANDLER(Msg_R_1_1, On_1_1) + IPC_MESSAGE_HANDLER(Msg_R_1_2, On_1_2) + IPC_MESSAGE_HANDLER(Msg_R_1_3, On_1_3) + IPC_MESSAGE_HANDLER(Msg_R_2_1, On_2_1) + IPC_MESSAGE_HANDLER(Msg_R_2_2, On_2_2) + IPC_MESSAGE_HANDLER(Msg_R_2_3, On_2_3) + IPC_MESSAGE_HANDLER(Msg_R_3_1, On_3_1) + IPC_MESSAGE_HANDLER(Msg_R_3_2, On_3_2) + IPC_MESSAGE_HANDLER(Msg_R_3_3, On_3_3) + IPC_END_MESSAGE_MAP() + } + +}; + +void Send(IPC::SyncMessage* msg) { + static TestMessageReceiver receiver; + + IPC::MessageReplyDeserializer* reply_serializer = msg->GetReplyDeserializer(); + DCHECK(reply_serializer != NULL); + + // "send" the message + receiver.OnMessageReceived(*msg); + delete msg; + + // get the reply message from the global, and deserialize the output + // parameters into the output pointers. + DCHECK(g_reply != NULL); + bool result = reply_serializer->SerializeOutputParameters(*g_reply); + DCHECK(result); + delete g_reply; + g_reply = NULL; + delete reply_serializer; +} + +TEST(IPCSyncMessageTest, Main) { + bool bool1 = true; + int int1 = 0; + std::string string1; + + Send(new Msg_C_0_1(&bool1)); + DCHECK(!bool1); + + Send(new Msg_C_0_2(&bool1, &int1)); + DCHECK(bool1 && int1 == 2); + + Send(new Msg_C_0_3(&bool1, &int1, &string1)); + DCHECK(!bool1 && int1 == 3 && string1 == "0_3"); + + bool1 = false; + Send(new Msg_C_1_1(1, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_C_1_2(false, &bool1, &int1)); + DCHECK(bool1 && int1 == 12); + + bool1 = true; + Send(new Msg_C_1_3(3, &string1, &int1, &bool1)); + DCHECK(string1 == "1_3" && int1 == 13 && !bool1); + + bool1 = false; + Send(new Msg_C_2_1(1, false, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_C_2_2(false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 22); + + bool1 = true; + Send(new Msg_C_2_3(3, true, &string1, &int1, &bool1)); + DCHECK(string1 == "2_3" && int1 == 23 && !bool1); + + bool1 = false; + Send(new Msg_C_3_1(1, false, "3_1", &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_C_3_2("3_2", false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 32); + + bool1 = true; + Send(new Msg_C_3_3(3, "3_3", true, &string1, &int1, &bool1)); + DCHECK(string1 == "3_3" && int1 == 33 && !bool1); + + // Routed messages, just a copy of the above but with extra routing paramater + Send(new Msg_R_0_1(0, &bool1)); + DCHECK(!bool1); + + Send(new Msg_R_0_2(0, &bool1, &int1)); + DCHECK(bool1 && int1 == 2); + + Send(new Msg_R_0_3(0, &bool1, &int1, &string1)); + DCHECK(!bool1 && int1 == 3 && string1 == "0_3"); + + bool1 = false; + Send(new Msg_R_1_1(0, 1, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_R_1_2(0, false, &bool1, &int1)); + DCHECK(bool1 && int1 == 12); + + bool1 = true; + Send(new Msg_R_1_3(0, 3, &string1, &int1, &bool1)); + DCHECK(string1 == "1_3" && int1 == 13 && !bool1); + + bool1 = false; + Send(new Msg_R_2_1(0, 1, false, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_R_2_2(0, false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 22); + + bool1 = true; + Send(new Msg_R_2_3(0, 3, true, &string1, &int1, &bool1)); + DCHECK(string1 == "2_3" && int1 == 23 && !bool1); + + bool1 = false; + Send(new Msg_R_3_1(0, 1, false, "3_1", &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_R_3_2(0, "3_2", false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 32); + + bool1 = true; + Send(new Msg_R_3_3(0, 3, "3_3", true, &string1, &int1, &bool1)); + DCHECK(string1 == "3_3" && int1 == 33 && !bool1); +} diff --git a/chrome/common/ipc_sync_message_unittest.h b/chrome/common/ipc_sync_message_unittest.h new file mode 100644 index 0000000..af62e63 --- /dev/null +++ b/chrome/common/ipc_sync_message_unittest.h @@ -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. + +#include "chrome/common/ipc_message_macros.h" + +IPC_BEGIN_MESSAGES(TestMsg, 8) + // out1 is false + IPC_SYNC_MESSAGE_CONTROL0_1(Msg_C_0_1, bool) + + // out1 is true, out2 is 2 + IPC_SYNC_MESSAGE_CONTROL0_2(Msg_C_0_2, bool, int) + + // out1 is false, out2 is 3, out3 is "0_3" + IPC_SYNC_MESSAGE_CONTROL0_3(Msg_C_0_3, bool, int, std::string) + + // in1 must be 1, out1 is true + IPC_SYNC_MESSAGE_CONTROL1_1(Msg_C_1_1, int, bool) + + // in1 must be false, out1 is true, out2 is 12 + IPC_SYNC_MESSAGE_CONTROL1_2(Msg_C_1_2, bool, bool, int) + + // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false + IPC_SYNC_MESSAGE_CONTROL1_3(Msg_C_1_3, int, std::string, int, bool) + + // in1 must be 1, in2 must be false, out1 is true + IPC_SYNC_MESSAGE_CONTROL2_1(Msg_C_2_1, int, bool, bool) + + // in1 must be false, in2 must be 2, out1 is true, out2 is 22 + IPC_SYNC_MESSAGE_CONTROL2_2(Msg_C_2_2, bool, int, bool, int) + + // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false + IPC_SYNC_MESSAGE_CONTROL2_3(Msg_C_2_3, int, bool, std::string, int, bool) + + // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true + IPC_SYNC_MESSAGE_CONTROL3_1(Msg_C_3_1, int, bool, std::string, bool) + + // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2 is 32 + IPC_SYNC_MESSAGE_CONTROL3_2(Msg_C_3_2, std::string, bool, int, bool, int) + + // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is 33, out3 is false + IPC_SYNC_MESSAGE_CONTROL3_3(Msg_C_3_3, int, std::string, bool, std::string, int, bool) + + + // NOTE: routed messages are just a copy of the above... + + // out1 is false + IPC_SYNC_MESSAGE_ROUTED0_1(Msg_R_0_1, bool) + + // out1 is true, out2 is 2 + IPC_SYNC_MESSAGE_ROUTED0_2(Msg_R_0_2, bool, int) + + // out1 is false, out2 is 3, out3 is "0_3" + IPC_SYNC_MESSAGE_ROUTED0_3(Msg_R_0_3, bool, int, std::string) + + // in1 must be 1, out1 is true + IPC_SYNC_MESSAGE_ROUTED1_1(Msg_R_1_1, int, bool) + + // in1 must be false, out1 is true, out2 is 12 + IPC_SYNC_MESSAGE_ROUTED1_2(Msg_R_1_2, bool, bool, int) + + // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false + IPC_SYNC_MESSAGE_ROUTED1_3(Msg_R_1_3, int, std::string, int, bool) + + // in1 must be 1, in2 must be false, out1 is true + IPC_SYNC_MESSAGE_ROUTED2_1(Msg_R_2_1, int, bool, bool) + + // in1 must be false, in2 must be 2, out1 is true, out2 is 22 + IPC_SYNC_MESSAGE_ROUTED2_2(Msg_R_2_2, bool, int, bool, int) + + // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false + IPC_SYNC_MESSAGE_ROUTED2_3(Msg_R_2_3, int, bool, std::string, int, bool) + + // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true + IPC_SYNC_MESSAGE_ROUTED3_1(Msg_R_3_1, int, bool, std::string, bool) + + // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2 is 32 + IPC_SYNC_MESSAGE_ROUTED3_2(Msg_R_3_2, std::string, bool, int, bool, int) + + // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is 33, out3 is false + IPC_SYNC_MESSAGE_ROUTED3_3(Msg_R_3_3, int, std::string, bool, std::string, int, bool) + +IPC_END_MESSAGES(TestMsg) diff --git a/chrome/common/ipc_sync_sender.cc b/chrome/common/ipc_sync_sender.cc new file mode 100644 index 0000000..e502bc5 --- /dev/null +++ b/chrome/common/ipc_sync_sender.cc @@ -0,0 +1,150 @@ +// 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 WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +#include "chrome/common/ipc_sync_sender.h" + +#include "chrome/common/ipc_sync_message.h" +#include "base/logging.h" + +namespace IPC { + +SyncSender::SyncSender(HANDLE shutdown_event) : + shutdown_event_(shutdown_event), + reply_deserialize_result_(false), + reply_event_(CreateEvent(NULL, FALSE, FALSE, NULL)), + blocking_event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { + DCHECK(shutdown_event != NULL); +} + +SyncSender::~SyncSender() { + CloseHandle(reply_event_); + CloseHandle(blocking_event_); + DCHECK(deserializers_.empty()); +} + +bool SyncSender::SendSync(IPC::Message* message) { + bool message_is_sync = false; + + message_is_sync = message->is_sync(); + if (message_is_sync) { + IPC::SyncMessage* sync_msg = static_cast<IPC::SyncMessage*>(message); + PendingSyncMsg pending(IPC::SyncMessage::GetMessageId(*sync_msg), + sync_msg->GetReplyDeserializer()); + deserializers_.push(pending); + } + + // Get the derived class to send the message. + bool send_result = SendPrivate(message); + + if (!message_is_sync) + return send_result; + + if (!send_result) { + delete deserializers_.top().deserializer; + deserializers_.pop(); + return false; + } + + do { + // wait for reply + HANDLE objects[] = { reply_event_, blocking_event_, shutdown_event_ }; + DWORD result = WaitForMultipleObjects(3, objects, FALSE, INFINITE); + if (result == WAIT_OBJECT_0 + 2) { + // Process shut down before we can get a reply to a synchronous message. + // Unblock the thread. + return false; + } + + if (result == WAIT_OBJECT_0 + 1) { + // We're waiting for a reply, but the replier is making a synchronous + // request that we must service or else we deadlock. Or in case this + // process supports processing of any synchronous messages while it's + // blocked waiting for a reply (i.e. because it communicates with + // multiple processes). + while (true) { + blocking_messages_lock_.Acquire(); + size_t size = blocking_messages_.size(); + Message* blocking_message; + if (size) { + blocking_message = blocking_messages_.front(); + blocking_messages_.pop(); + } + + blocking_messages_lock_.Release(); + + if (!size) + break; + + OnDispatchMessage(*blocking_message); + delete blocking_message; + } + + // Continue looping until we get the reply to our synchronous message. + } else { + // We got the reply to our synchronous message. + return reply_deserialize_result_; + } + } while (true); +} + +bool SyncSender::OnFilterMessage(const Message& msg) { + if (deserializers_.empty()) + return false; + + if (IPC::SyncMessage::IsMessageReplyTo(msg, deserializers_.top().id)) { + reply_deserialize_result_ = + deserializers_.top().deserializer->SerializeOutputParameters(msg); + delete deserializers_.top().deserializer; + deserializers_.pop(); + SetEvent(reply_event_); + return true; + } + + if (msg.is_sync()) { + // When we're blocked waiting for a reply we have to respond to other + // synchronous messages as they might be blocking our reply. We also don't + // want to block other processes because one is blocked. + + // Create a copy of this message, as it can be deleted from under us + // if there are more than two synchronous messages in parallel. + // (i.e. A->B, B->A, A->B all synchronous) + blocking_messages_lock_.Acquire(); + blocking_messages_.push(new Message(msg)); + blocking_messages_lock_.Release(); + SetEvent(blocking_event_); + return true; + } + + return false; +} + +} // namespace IPC diff --git a/chrome/common/ipc_sync_sender.h b/chrome/common/ipc_sync_sender.h new file mode 100644 index 0000000..81c783c --- /dev/null +++ b/chrome/common/ipc_sync_sender.h @@ -0,0 +1,123 @@ +// 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 CHROME_COMMON_IPC_SYNC_SENDER_H__ +#define CHROME_COMMON_IPC_SYNC_SENDER_H__ + +#include <windows.h> +#include <string> +#include <stack> +#include <queue> +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/lock.h" + +namespace IPC { + +// Classes which send synchronous messages need to derive from this. +// This class is called on two threads. The first is the main thread that does +// the message processing (and which may be blocked waiting for a reply to a +// synchronous message). The second is the IPC thread that does the filtering +// of messages before passing it to the (maybe) blocked main thread. +// +// To use this class: +// 1) Your Send() must pass all messages to SendSync() +// 2) You must implement SendPrivate(), which SendSync() will call internally +// 3) You must be able to filter incoming messages on the IPC thread, and +// pass them on to OnFilterMessage(). +// 4) You must implement OnDispatchMessage, which is what dispatches +// messages on the main thread. + +class SyncSender { + public: + // shutdown_event is an event that can be waited on so that we don't block if + // if the process is shutting down. If it's NULL, then it's not used. + SyncSender(HANDLE shutdown_event); + ~SyncSender(); + + // These are called on the main thread. + + // The derived class's Send should just passthrough to SendSync. + bool SendSync(IPC::Message* message); + + // SendSync will call your implementation's SendPrivate when it comes + // time to send the message on the channel. + virtual bool SendPrivate(IPC::Message* message) = 0; + + // If a message needs to be dispatched immediately because it's blocking our + // reply, this function will be called. + virtual void OnDispatchMessage(const Message& message) = 0; + + // This is called on the IPC thread. Returns true if the message has been + // consumed (i.e. don't do any more processing). + bool OnFilterMessage(const Message& message); + + private: + // When sending a synchronous message, this structure contains an object that + // knows how to deserialize the response. + struct PendingSyncMsg { + PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d) : + id(id), deserializer(d) { } + int id; + IPC::MessageReplyDeserializer* deserializer; + }; + + // Set when we got a reply for a synchronous message that we sent. + HANDLE reply_event_; + + // Set when we got a synchronous message that we must respond to as the + // sender needs its reply before it can reply to our original synchronous + // message. + HANDLE blocking_event_; + + // Copy of shutdown event that we get in constructor. + HANDLE shutdown_event_; + + typedef std::stack<PendingSyncMsg> PendingSyncMessageQueue; + PendingSyncMessageQueue deserializers_; + bool reply_deserialize_result_; + + // If we're waiting on a reply and the caller sends a synchronous message + // that's blocking the reply, this variable is used to pass the intermediate + // "blocking" message between our two threads. We can store multiple + // messages as a process will want to respond to any synchronous message + // while they're blocked (i.e. because they talk to multiple processes). + typedef std::queue<Message*> BlockingMessageQueue; + BlockingMessageQueue blocking_messages_; + + // Above data structure is used on multiple threads, so it need + // synchronization. + Lock blocking_messages_lock_; + + DISALLOW_EVIL_CONSTRUCTORS(SyncSender); +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_SYNC_SENDER_H__ diff --git a/chrome/common/ipc_tests.cc b/chrome/common/ipc_tests.cc new file mode 100644 index 0000000..a9d998c --- /dev/null +++ b/chrome/common/ipc_tests.cc @@ -0,0 +1,426 @@ +// 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 <stdio.h> +#include <iostream> +#include <string> + +#include "chrome/common/ipc_tests.h" + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug_on_start.h" +#include "base/perftimer.h" +#include "base/process_util.h" +#include "base/thread.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/ipc_channel_proxy.h" +#include "chrome/common/ipc_message_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Define to enable IPC performance testing instead of the regular unit tests +// #define PERFORMANCE_TEST + +const wchar_t kTestClientChannel[] = L"T1"; +const wchar_t kReflectorChannel[] = L"T2"; +const wchar_t kFuzzerChannel[] = L"F3"; + +const wchar_t kChild[] = L"child"; +const wchar_t kReflector[] = L"reflector"; +const wchar_t kFuzzer[] = L"fuzzer"; + +#ifndef PERFORMANCE_TEST + +TEST(IPCChannelTest, BasicMessageTest) { + int v1 = 10; + std::string v2("foobar"); + std::wstring v3(L"hello world"); + + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(v1)); + EXPECT_TRUE(m.WriteString(v2)); + EXPECT_TRUE(m.WriteWString(v3)); + + void* iter = NULL; + + int vi; + std::string vs; + std::wstring vw; + + EXPECT_TRUE(m.ReadInt(&iter, &vi)); + EXPECT_EQ(v1, vi); + + EXPECT_TRUE(m.ReadString(&iter, &vs)); + EXPECT_EQ(v2, vs); + + EXPECT_TRUE(m.ReadWString(&iter, &vw)); + EXPECT_EQ(v3, vw); + + // should fail + EXPECT_FALSE(m.ReadInt(&iter, &vi)); + EXPECT_FALSE(m.ReadString(&iter, &vs)); + EXPECT_FALSE(m.ReadWString(&iter, &vw)); +} + +static void Send(IPC::Message::Sender* sender, const char* text) { + static int message_index = 0; + + IPC::Message* message = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + message->WriteInt(message_index++); + message->WriteString(std::string(text)); + + // make sure we can handle large messages + char junk[50000]; + junk[sizeof(junk)-1] = 0; + message->WriteString(std::string(junk)); + + // DEBUG: printf("[%u] sending message [%s]\n", GetCurrentProcessId(), text); + sender->Send(message); +} + +class MyChannelListener : public IPC::Channel::Listener { + public: + virtual void OnMessageReceived(const IPC::Message& message) { + IPC::MessageIterator iter(message); + + int index = iter.NextInt(); + const std::string data = iter.NextString(); + + if (--messages_left_ == 0) { + MessageLoop::current()->Quit(); + } else { + Send(sender_, "Foo"); + } + } + + void Init(IPC::Message::Sender* s) { + sender_ = s; + messages_left_ = 50; + } + + private: + IPC::Message::Sender* sender_; + int messages_left_; +}; +static MyChannelListener channel_listener; + +TEST(IPCChannelTest, ChannelTest) { + // setup IPC channel + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, + &channel_listener); + chan.Connect(); + + channel_listener.Init(&chan); + + HANDLE process_handle = SpawnChild(TEST_CLIENT); + ASSERT_TRUE(process_handle); + + Send(&chan, "hello from parent"); + + // run message loop + MessageLoop::current()->Run(); + + // cleanup child process + WaitForSingleObject(process_handle, 5000); + CloseHandle(process_handle); +} + +TEST(IPCChannelTest, ChannelProxyTest) { + // The thread needs to out-live the ChannelProxy. + Thread thread("ChannelProxyTestServer"); + thread.Start(); + { + // setup IPC channel proxy + IPC::ChannelProxy chan(kTestClientChannel, IPC::Channel::MODE_SERVER, + &channel_listener, NULL, thread.message_loop()); + + channel_listener.Init(&chan); + + HANDLE process_handle = SpawnChild(TEST_CLIENT); + ASSERT_TRUE(process_handle); + + Send(&chan, "hello from parent"); + + // run message loop + MessageLoop::current()->Run(); + + // cleanup child process + WaitForSingleObject(process_handle, 5000); + CloseHandle(process_handle); + } + thread.Stop(); +} + +static bool RunTestClient() { + // setup IPC channel + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, + &channel_listener); + chan.Connect(); + channel_listener.Init(&chan); + Send(&chan, "hello from child"); + // run message loop + MessageLoop::current()->Run(); + return true; +} + +#endif // !PERFORMANCE_TEST + +#ifdef PERFORMANCE_TEST + +//----------------------------------------------------------------------------- +// Manually performance test +// +// This test times the roundtrip IPC message cycle. It is enabled with a +// special preprocessor define to enable it instead of the standard IPC +// unit tests. This works around some funny termination conditions in the +// regular unit tests. +// +// This test is not automated. To test, you will want to vary the message +// count and message size in TEST to get the numbers you want. +// +// FIXME(brettw): Automate this test and have it run by default. + +// This channel listener just replies to all messages with the exact same +// message. It assumes each message has one string parameter. When the string +// "quit" is sent, it will exit. +class ChannelReflectorListener : public IPC::Channel::Listener { + public: + ChannelReflectorListener(IPC::Channel *channel) : + channel_(channel), + count_messages_(0), + latency_messages_(0) { + std::cout << "Reflector up" << std::endl; + } + + ~ChannelReflectorListener() { + std::cout << "Client Messages: " << count_messages_ << std::endl; + std::cout << "Client Latency: " << latency_messages_ << std::endl; + } + + virtual void OnMessageReceived(const IPC::Message& message) { + count_messages_++; + IPC::MessageIterator iter(message); + int time = iter.NextInt(); + int msgid = iter.NextInt(); + std::string payload = iter.NextString(); + latency_messages_ += GetTickCount() - time; + + // cout << "reflector msg received: " << msgid << endl; + if (payload == "quit") + MessageLoop::current()->Quit(); + + IPC::Message* msg = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(GetTickCount()); + msg->WriteInt(msgid); + msg->WriteString(payload); + channel_->Send(msg); + } + private: + IPC::Channel *channel_; + int count_messages_; + int latency_messages_; +}; + +class ChannelPerfListener : public IPC::Channel::Listener { + public: + ChannelPerfListener(IPC::Channel* channel, int msg_count, int msg_size) : + count_down_(msg_count), + channel_(channel), + count_messages_(0), + latency_messages_(0) { + payload_.resize(msg_size); + for (int i = 0; i < static_cast<int>(payload_.size()); i++) + payload_[i] = 'a'; + std::cout << "perflistener up" << std::endl; + } + + ~ChannelPerfListener() { + std::cout << "Server Messages: " << count_messages_ << std::endl; + std::cout << "Server Latency: " << latency_messages_ << std::endl; + } + + virtual void OnMessageReceived(const IPC::Message& message) { + count_messages_++; + // decode the string so this gets counted in the total time + IPC::MessageIterator iter(message); + int time = iter.NextInt(); + int msgid = iter.NextInt(); + std::string cur = iter.NextString(); + latency_messages_ += GetTickCount() - time; + + // cout << "perflistener got message" << endl; + + count_down_--; + if (count_down_ == 0) { + IPC::Message* msg = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(GetTickCount()); + msg->WriteInt(count_down_); + msg->WriteString("quit"); + channel_->Send(msg); + SetTimer(NULL, 1, 250, (TIMERPROC) PostQuitMessage); + return; + } + + IPC::Message* msg = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(GetTickCount()); + msg->WriteInt(count_down_); + msg->WriteString(payload_); + channel_->Send(msg); + } + + private: + int count_down_; + std::string payload_; + IPC::Channel *channel_; + int count_messages_; + int latency_messages_; +}; + +TEST(IPCChannelTest, Performance) { + // setup IPC channel + IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_SERVER, NULL); + ChannelPerfListener perf_listener(&chan, 10000, 100000); + chan.set_listener(&perf_listener); + chan.Connect(); + + HANDLE process = SpawnChild(TEST_REFLECTOR); + ASSERT_TRUE(process); + + Sleep(1000); + + PerfTimeLogger logger("IPC_Perf"); + + // this initial message will kick-start the ping-pong of messages + IPC::Message* message = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + message->WriteInt(GetTickCount()); + message->WriteInt(-1); + message->WriteString("Hello"); + chan.Send(message); + + // run message loop + MessageLoop::current()->Run(); + + // cleanup child process + WaitForSingleObject(process, 5000); + CloseHandle(process); +} + +// This message loop bounces all messages back to the sender +static bool RunReflector() { + IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_CLIENT, NULL); + ChannelReflectorListener channel_reflector_listener(&chan); + chan.set_listener(&channel_reflector_listener); + chan.Connect(); + + MessageLoop::current()->Run(); + return true; +} + +#endif // PERFORMANCE_TEST + +// All fatal log messages (e.g. DCHECK failures) imply unit test failures +static void IPCTestAssertHandler(const std::string& str) { + FAIL() << str; +} + +// Disable crash dialogs so that it doesn't gum up the buildbot +static void SuppressErrorDialogs() { + UINT new_flags = SEM_FAILCRITICALERRORS | + SEM_NOGPFAULTERRORBOX | + SEM_NOOPENFILEERRORBOX; + + // Preserve existing error mode, as discussed at http://t/dmea + UINT existing_flags = SetErrorMode(new_flags); + SetErrorMode(existing_flags | new_flags); +} + +HANDLE SpawnChild(ChildType child_type) { + // spawn child process + std::wstring cl(GetCommandLineW()); + switch(child_type) { + case TEST_CLIENT: + CommandLine::AppendSwitch(&cl, kChild); + break; + case TEST_REFLECTOR: + CommandLine::AppendSwitch(&cl, kReflector); + break; + case FUZZER_SERVER: + CommandLine::AppendSwitch(&cl, kFuzzer); + break; + default: + return NULL; + } + // kDebugChildren support. + if (CommandLine().HasSwitch(switches::kDebugChildren)) { + CommandLine::AppendSwitch(&cl, switches::kDebugOnStart); + } + HANDLE process = NULL; + if (!process_util::LaunchApp(cl, false, true, &process)) + return NULL; + + return process; +} + +int main(int argc, char** argv) { + MessageLoop main_message_loop; + + // suppress standard crash dialogs and such unless a debugger is present. + if (!IsDebuggerPresent()) { + SuppressErrorDialogs(); + logging::SetLogAssertHandler(IPCTestAssertHandler); + } + +#ifndef PERFORMANCE_TEST + if (CommandLine().HasSwitch(kChild)) + return RunTestClient(); + if (CommandLine().HasSwitch(kFuzzer)) + return RunFuzzServer(); +#else + if (CommandLine().HasSwitch(kReflector)) + return RunReflector(); + + if (!InitPerfLog("ipc_perf_child.log")) + return 1; +#endif + + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/chrome/common/ipc_tests.h b/chrome/common/ipc_tests.h new file mode 100644 index 0000000..c37cc67 --- /dev/null +++ b/chrome/common/ipc_tests.h @@ -0,0 +1,54 @@ +// 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 CHROME_COMMON_IPC_TESTS_H__ +#define CHROME_COMMON_IPC_TESTS_H__ + +// This unit test uses 3 types of child processes, a regular pipe client, +// a client reflector and a IPC server used for fuzzing tests. +enum ChildType { + TEST_CLIENT, + TEST_REFLECTOR, + FUZZER_SERVER +}; + +// The different channel names for the child processes. +extern const wchar_t kTestClientChannel[]; +extern const wchar_t kReflectorChannel[]; +extern const wchar_t kFuzzerChannel[]; + +// Spawns a child process and then runs the code for one of the 3 possible +// child modes. +HANDLE SpawnChild(ChildType child_type); + +// Runs the fuzzing server child mode. Returns true when the preset number +// of messages have been received. +bool RunFuzzServer(); + +#endif // CHROME_COMMON_IPC_TESTS_H__ diff --git a/chrome/common/ipc_tests.vcproj b/chrome/common/ipc_tests.vcproj new file mode 100644 index 0000000..78fae65 --- /dev/null +++ b/chrome/common/ipc_tests.vcproj @@ -0,0 +1,165 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="ipc_tests" + ProjectGUID="{B92AE829-E1CD-4781-824A-DCB1603A1672}" + RootNamespace="ipc_tests" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="1" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\testing\using_gtest.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="shlwapi.lib rpcrt4.lib winmm.lib" + SubSystem="1" + /> + <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="1" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\testing\using_gtest.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="shlwapi.lib rpcrt4.lib winmm.lib" + SubSystem="1" + /> + <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=".\ipc_fuzzing_tests.cc" + > + </File> + <File + RelativePath=".\ipc_tests.cc" + > + </File> + <File + RelativePath=".\ipc_tests.h" + > + </File> + <File + RelativePath="..\..\base\perftimer.cc" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/common/jpeg_codec.cc b/chrome/common/jpeg_codec.cc new file mode 100644 index 0000000..ad36454 --- /dev/null +++ b/chrome/common/jpeg_codec.cc @@ -0,0 +1,545 @@ +// 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 <setjmp.h> + +#include "chrome/common/jpeg_codec.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "SkBitmap.h" + +extern "C" { +#include "jpeglib.h" +} + +// Encoder/decoder shared stuff ------------------------------------------------ + +namespace { + +// used to pass error info through the JPEG library +struct CoderErrorMgr { + jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +void ErrorExit(jpeg_common_struct* cinfo) { + CoderErrorMgr *err = reinterpret_cast<CoderErrorMgr*>(cinfo->err); + + // Return control to the setjmp point. + longjmp(err->setjmp_buffer, false); +} + +} // namespace + +// Encoder --------------------------------------------------------------------- +// +// This code is based on nsJPEGEncoder from Mozilla. +// Copyright 2005 Google Inc. (Brett Wilson, contributor) + +namespace { + +// Initial size for the output buffer in the JpegEncoderState below. +const static int initial_output_buffer_size = 8192; + +struct JpegEncoderState { + JpegEncoderState(std::vector<unsigned char>* o) : out(o), image_buffer_used(0) { + } + + // Output buffer, of which 'image_buffer_used' bytes are actually used (this + // will often be less than the actual size of the vector because we size it + // so that libjpeg can write directly into it. + std::vector<unsigned char>* out; + + // Number of bytes in the 'out' buffer that are actually used (see above). + size_t image_buffer_used; +}; + +// Initializes the JpegEncoderState for encoding, and tells libjpeg about where +// the output buffer is. +// +// From the JPEG library: +// "Initialize destination. This is called by jpeg_start_compress() before +// any data is actually written. It must initialize next_output_byte and +// free_in_buffer. free_in_buffer must be initialized to a positive value." +void InitDestination(jpeg_compress_struct* cinfo) { + JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data); + DCHECK(state->image_buffer_used == 0) << "initializing after use"; + + state->out->resize(initial_output_buffer_size); + state->image_buffer_used = 0; + + cinfo->dest->next_output_byte = &(*state->out)[0]; + cinfo->dest->free_in_buffer = initial_output_buffer_size; +} + +// Resize the buffer that we give to libjpeg and update our and its state. +// +// From the JPEG library: +// "Callback used by libjpeg whenever the buffer has filled (free_in_buffer +// reaches zero). In typical applications, it should write out the *entire* +// buffer (use the saved start address and buffer length; ignore the current +// state of next_output_byte and free_in_buffer). Then reset the pointer & +// count to the start of the buffer, and return TRUE indicating that the +// buffer has been dumped. free_in_buffer must be set to a positive value +// when TRUE is returned. A FALSE return should only be used when I/O +// suspension is desired (this operating mode is discussed in the next +// section)." +boolean EmptyOutputBuffer(jpeg_compress_struct* cinfo) { + JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data); + + // note the new size, the buffer is full + state->image_buffer_used = state->out->size(); + + // expand buffer, just double size each time + state->out->resize(state->out->size() * 2); + + // tell libjpeg where to write the next data + cinfo->dest->next_output_byte = &(*state->out)[state->image_buffer_used]; + cinfo->dest->free_in_buffer = state->out->size() - state->image_buffer_used; + return 1; +} + +// Cleans up the JpegEncoderState to prepare for returning in the final form. +// +// From the JPEG library: +// "Terminate destination --- called by jpeg_finish_compress() after all data +// has been written. In most applications, this must flush any data +// remaining in the buffer. Use either next_output_byte or free_in_buffer to +// determine how much data is in the buffer." +void TermDestination(jpeg_compress_struct* cinfo) { + JpegEncoderState* state = static_cast<JpegEncoderState*>(cinfo->client_data); + DCHECK(state->out->size() >= state->image_buffer_used); + + // update the used byte based on the next byte libjpeg would write to + state->image_buffer_used = cinfo->dest->next_output_byte - &(*state->out)[0]; + DCHECK(state->image_buffer_used < state->out->size()) << + "JPEG library busted, got a bad image buffer size"; + + // update our buffer so that it exactly encompases the desired data + state->out->resize(state->image_buffer_used); +} + +// Converts RGBA to RGB (removing the alpha values) to prepare to send data to +// libjpeg. This converts one row of data in rgba with the given width in +// pixels the the given rgb destination buffer (which should have enough space +// reserved for the final data). +void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb) +{ + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgba[x * 4]; + unsigned char* pixel_out = &rgb[x * 3]; + pixel_out[0] = pixel_in[0]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[2]; + } +} + +// Converts BGRA to RGB by reordering the color components and dropping the +// alpha. This converts one row of data in rgba with the given width in +// pixels the the given rgb destination buffer (which should have enough space +// reserved for the final data). +void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb) +{ + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &bgra[x * 4]; + unsigned char* pixel_out = &rgb[x * 3]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + } +} + +// This class destroys the given jpeg_compress object when it goes out of +// scope. It simplifies the error handling in Encode (and even applies to the +// success case). +class CompressDestroyer { + public: + CompressDestroyer() : cinfo_(NULL) { + } + ~CompressDestroyer() { + DestroyManagedObject(); + } + void SetManagedObject(jpeg_compress_struct* ci) { + DestroyManagedObject(); + cinfo_ = ci; + } + void DestroyManagedObject() { + if (cinfo_) { + jpeg_destroy_compress(cinfo_); + cinfo_ = NULL; + } + } + private: + jpeg_compress_struct* cinfo_; +}; + +} // namespace + +bool JPEGCodec::Encode(const unsigned char* input, ColorFormat format, + int w, int h, int row_byte_width, + int quality, std::vector<unsigned char>* output) { + jpeg_compress_struct cinfo; + CompressDestroyer destroyer; + output->clear(); + + // We set up the normal JPEG error routines, then override error_exit. + // This must be done before the call to create_compress. + CoderErrorMgr errmgr; + cinfo.err = jpeg_std_error(&errmgr.pub); + errmgr.pub.error_exit = ErrorExit; + // Establish the setjmp return context for ErrorExit to use. + if (setjmp(errmgr.setjmp_buffer)) { + // If we get here, the JPEG code has signaled an error. + // MSDN notes: "if you intend your code to be portable, do not rely on + // correct destruction of frame-based objects when executing a nonlocal + // goto using a call to longjmp." So we delete the CompressDestroyer's + // object manually instead. + destroyer.DestroyManagedObject(); + return false; + } + + // The destroyer will destroy() cinfo on exit. + jpeg_create_compress(&cinfo); + destroyer.SetManagedObject(&cinfo); + + cinfo.image_width = w; + cinfo.image_height = h; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + cinfo.data_precision = 8; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100 + + // set up the destination manager + jpeg_destination_mgr destmgr; + destmgr.init_destination = InitDestination; + destmgr.empty_output_buffer = EmptyOutputBuffer; + destmgr.term_destination = TermDestination; + cinfo.dest = &destmgr; + + JpegEncoderState state(output); + cinfo.client_data = &state; + + jpeg_start_compress(&cinfo, 1); + + // feed it the rows, doing necessary conversions for the color format + if (format == FORMAT_RGB) { + // no conversion necessary + while (cinfo.next_scanline < cinfo.image_height) { + const unsigned char* row = &input[cinfo.next_scanline * row_byte_width]; + jpeg_write_scanlines(&cinfo, const_cast<unsigned char**>(&row), 1); + } + } else { + // get the correct format converter + void (*converter)(const unsigned char* in, int w, unsigned char* rgb); + if (format == FORMAT_RGBA) { + converter = StripAlpha; + } else if (format == FORMAT_BGRA) { + converter = BGRAtoRGB; + } else { + NOTREACHED() << "Invalid pixel format"; + return false; + } + + // output row after converting + unsigned char* row = new unsigned char[w * 3]; + + while (cinfo.next_scanline < cinfo.image_height) { + converter(&input[cinfo.next_scanline * row_byte_width], w, row); + jpeg_write_scanlines(&cinfo, &row, 1); + } + delete[] row; + } + + jpeg_finish_compress(&cinfo); + return true; +} + +// Decoder -------------------------------------------------------------------- + +namespace { + +struct JpegDecoderState { + JpegDecoderState(const unsigned char* in, size_t len) + : input_buffer(in), input_buffer_length(len) { + } + + const unsigned char* input_buffer; + size_t input_buffer_length; +}; + +// Callback to initialize the source. +// +// From the JPEG library: +// "Initialize source. This is called by jpeg_read_header() before any data is +// actually read. May leave bytes_in_buffer set to 0 (in which case a +// fill_input_buffer() call will occur immediately)." +void InitSource(j_decompress_ptr cinfo) { + JpegDecoderState* state = static_cast<JpegDecoderState*>(cinfo->client_data); + cinfo->src->next_input_byte = state->input_buffer; + cinfo->src->bytes_in_buffer = state->input_buffer_length; +} + +// Callback to fill the buffer. Since our buffer already contains all the data, +// we should never need to provide more data. If libjpeg thinks it needs more +// data, our input is probably corrupt. +// +// From the JPEG library: +// "This is called whenever bytes_in_buffer has reached zero and more data is +// wanted. In typical applications, it should read fresh data into the buffer +// (ignoring the current state of next_input_byte and bytes_in_buffer), reset +// the pointer & count to the start of the buffer, and return TRUE indicating +// that the buffer has been reloaded. It is not necessary to fill the buffer +// entirely, only to obtain at least one more byte. bytes_in_buffer MUST be +// set to a positive value if TRUE is returned. A FALSE return should only +// be used when I/O suspension is desired." +boolean FillInputBuffer(j_decompress_ptr cinfo) { + return false; +} + +// Skip data in the buffer. Since we have all the data at once, this operation +// is easy. It is not clear if this ever gets called because the JPEG library +// should be able to do the skip itself (it has all the data). +// +// From the JPEG library: +// "Skip num_bytes worth of data. The buffer pointer and count should be +// advanced over num_bytes input bytes, refilling the buffer as needed. This +// is used to skip over a potentially large amount of uninteresting data +// (such as an APPn marker). In some applications it may be possible to +// optimize away the reading of the skipped data, but it's not clear that +// being smart is worth much trouble; large skips are uncommon. +// bytes_in_buffer may be zero on return. A zero or negative skip count +// should be treated as a no-op." +void SkipInputData(j_decompress_ptr cinfo, long num_bytes) { + if (num_bytes > static_cast<long>(cinfo->src->bytes_in_buffer)) { + // Since all our data should be in the buffer, trying to skip beyond it + // means that there is some kind of error or corrupt input data. A 0 for + // bytes left means it will call FillInputBuffer which will then fail. + cinfo->src->next_input_byte += cinfo->src->bytes_in_buffer; + cinfo->src->bytes_in_buffer = 0; + } else if (num_bytes > 0) { + cinfo->src->bytes_in_buffer -= static_cast<size_t>(num_bytes); + cinfo->src->next_input_byte += num_bytes; + } +} + +// Our source doesn't need any cleanup, so this is a NOP. +// +// From the JPEG library: +// "Terminate source --- called by jpeg_finish_decompress() after all data has +// been read to clean up JPEG source manager. NOT called by jpeg_abort() or +// jpeg_destroy()." +void TermSource(j_decompress_ptr cinfo) { +} + +// Converts one row of rgb data to rgba data by adding a fully-opaque alpha +// value. +void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgb[x * 3]; + unsigned char* pixel_out = &rgba[x * 4]; + pixel_out[0] = pixel_in[0]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[2]; + pixel_out[3] = 0xff; + } +} + +// Converts one row of RGB data to BGRA by reordering the color components and +// adding alpha values of 0xff. +void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb) +{ + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &bgra[x * 3]; + unsigned char* pixel_out = &rgb[x * 4]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + pixel_out[3] = 0xff; + } +} + +// This class destroys the given jpeg_decompress object when it goes out of +// scope. It simplifies the error handling in Decode (and even applies to the +// success case). +class DecompressDestroyer { + public: + DecompressDestroyer() : cinfo_(NULL) { + } + ~DecompressDestroyer() { + DestroyManagedObject(); + } + void SetManagedObject(jpeg_decompress_struct* ci) { + DestroyManagedObject(); + cinfo_ = ci; + } + void DestroyManagedObject() { + if (cinfo_) { + jpeg_destroy_decompress(cinfo_); + cinfo_ = NULL; + } + } + private: + jpeg_decompress_struct* cinfo_; +}; + +} // namespace + +bool JPEGCodec::Decode(const unsigned char* input, size_t input_size, + ColorFormat format, std::vector<unsigned char>* output, + int* w, int* h) { + jpeg_decompress_struct cinfo; + DecompressDestroyer destroyer; + output->clear(); + + // We set up the normal JPEG error routines, then override error_exit. + // This must be done before the call to create_decompress. + CoderErrorMgr errmgr; + cinfo.err = jpeg_std_error(&errmgr.pub); + errmgr.pub.error_exit = ErrorExit; + // Establish the setjmp return context for ErrorExit to use. + if (setjmp(errmgr.setjmp_buffer)) { + // If we get here, the JPEG code has signaled an error. + // See note in JPEGCodec::Encode() for why we need to destroy the cinfo + // manually here. + destroyer.DestroyManagedObject(); + return false; + } + + // The destroyer will destroy() cinfo on exit. We don't want to set the + // destroyer's object until cinfo is initialized. + jpeg_create_decompress(&cinfo); + destroyer.SetManagedObject(&cinfo); + + // set up the source manager + jpeg_source_mgr srcmgr; + srcmgr.init_source = InitSource; + srcmgr.fill_input_buffer = FillInputBuffer; + srcmgr.skip_input_data = SkipInputData; + srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine + srcmgr.term_source = TermSource; + cinfo.src = &srcmgr; + + JpegDecoderState state(input, input_size); + cinfo.client_data = &state; + + // fill the file metadata into our buffer + if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK) + return false; + + // we want to always get RGB data out + switch (cinfo.jpeg_color_space) { + case JCS_GRAYSCALE: + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + break; + case JCS_CMYK: + case JCS_YCCK: + default: + // Mozilla errors out on these color spaces, so I presume that the jpeg + // library can't do automatic color space conversion for them. We don't + // care about these anyway. + return false; + } + cinfo.output_components = 3; + + jpeg_calc_output_dimensions(&cinfo); + *w = cinfo.output_width; + *h = cinfo.output_height; + + jpeg_start_decompress(&cinfo); + + // FIXME(brettw) we may want to allow the capability for callers to request + // how to align row lengths as we do for the compressor. + int row_read_stride = cinfo.output_width * cinfo.output_components; + + if (format == FORMAT_RGB) { + // easy case, row needs no conversion + int row_write_stride = row_read_stride; + output->resize(row_write_stride * cinfo.output_height); + + for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) { + unsigned char* rowptr = &(*output)[row * row_write_stride]; + if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + return false; + } + } else { + // Rows need conversion to output format: read into a temporary buffer and + // expand to the final one. Performance: we could avoid the extra + // allocation by doing the expansion in-place. + int row_write_stride; + void (*converter)(const unsigned char* rgb, int w, unsigned char* out); + if (format == FORMAT_RGBA) { + row_write_stride = cinfo.output_width * 4; + converter = AddAlpha; + } else if (format == FORMAT_BGRA) { + row_write_stride = cinfo.output_width * 4; + converter = RGBtoBGRA; + } else { + NOTREACHED() << "Invalid pixel format"; + jpeg_destroy_decompress(&cinfo); + return false; + } + + output->resize(row_write_stride * cinfo.output_height); + + scoped_array<unsigned char> row_data(new unsigned char[row_read_stride]); + unsigned char* rowptr = row_data.get(); + for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) { + if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + return false; + converter(rowptr, *w, &(*output)[row * row_write_stride]); + } + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + return true; +} + +// static +SkBitmap* JPEGCodec::Decode(const unsigned char* input, size_t input_size) { + int w, h; + std::vector<unsigned char> data_vector; + // Use FORMAT_BGRA as that maps to Skia's 32 bit (kARGB_8888_Config) format. + if (!Decode(input, input_size, FORMAT_BGRA, &data_vector, &w, &h)) + return NULL; + + // Skia only handles 32 bit images. + int data_length = w * h * 4; + + SkBitmap* bitmap = new SkBitmap(); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h); + bitmap->allocPixels(); + memcpy(bitmap->getAddr32(0, 0), &data_vector[0], w * h * 4); + + return bitmap; +} diff --git a/chrome/common/jpeg_codec.h b/chrome/common/jpeg_codec.h new file mode 100644 index 0000000..599d748 --- /dev/null +++ b/chrome/common/jpeg_codec.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 CHROME_COMMON_JPEG_CODEC_H__ +#define CHROME_COMMON_JPEG_CODEC_H__ + +#include <vector> + +class SkBitmap; + +// Interface for encoding/decoding JPEG data. This is a wrapper around libjpeg, +// which has an inconvenient interface for callers. This is only used for UI +// elements, WebKit has its own more complicated JPEG decoder which handles, +// among other things, partially downloaded data. +class JPEGCodec { + public: + enum ColorFormat { + // 3 bytes per pixel (packed), in RGB order regardless of endianness. + // This is the native JPEG format. + FORMAT_RGB, + + // 4 bytes per pixel, in RGBA order in mem regardless of endianness. + FORMAT_RGBA, + + // 4 bytes per pixel, in BGRA order in mem regardless of endianness. + // This is the default Windows DIB order. + FORMAT_BGRA + }; + + // Encodes the given raw 'input' data, with each pixel being represented as + // given in 'format'. The encoded JPEG data will be written into the supplied + // vector and true will be returned on success. On failure (false), the + // contents of the output buffer are undefined. + // + // w, h: dimensions of the image + // row_byte_width: the width in bytes of each row. This may be greater than + // w * bytes_per_pixel if there is extra padding at the end of each row + // (often, each row is padded to the next machine word). + // quality: an integer in the range 0-100, where 100 is the highest quality. + static bool Encode(const unsigned char* input, ColorFormat format, + int w, int h, int row_byte_width, + int quality, std::vector<unsigned char>* output); + + // Decodes the JPEG data contained in input of length input_size. The + // decoded data will be placed in *output with the dimensions in *w and *h + // on success (returns true). This data will be written in the'format' + // format. On failure, the values of these output variables is undefined. + static bool Decode(const unsigned char* input, size_t input_size, + ColorFormat format, std::vector<unsigned char>* output, + int* w, int* h); + + // Decodes the JPEG data contained in input of length input_size. If + // successful, a SkBitmap is created and returned. It is up to the caller + // to delete the returned bitmap. + static SkBitmap* Decode(const unsigned char* input, size_t input_size); +}; + +#endif // CHROME_COMMON_JPEG_CODEC_H__ diff --git a/chrome/common/jpeg_codec_unittest.cc b/chrome/common/jpeg_codec_unittest.cc new file mode 100644 index 0000000..9fc0390 --- /dev/null +++ b/chrome/common/jpeg_codec_unittest.cc @@ -0,0 +1,172 @@ +// 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 <math.h> + +#include "chrome/common/jpeg_codec.h" +#include "testing/gtest/include/gtest/gtest.h" + +// out of 100, this indicates how compressed it will be, this should be changed +// with jpeg equality threshold +// static int jpeg_quality = 75; // FIXME(brettw) +static int jpeg_quality = 100; + +// The threshold of average color differences where we consider two images +// equal. This number was picked to be a little above the observed difference +// using the above quality. +static double jpeg_equality_threshold = 1.0; + +// Computes the average difference between each value in a and b. A and b +// should be the same size. Used to see if two images are approximately equal +// in the presence of compression. +static double AveragePixelDelta(const std::vector<unsigned char>& a, + const std::vector<unsigned char>& b) { + // if the sizes are different, say the average difference is the maximum + if (a.size() != b.size()) + return 255.0; + if (a.size() == 0) + return 0; // prevent divide by 0 below + + double acc = 0.0; + for (size_t i = 0; i < a.size(); i++) + acc += fabs(static_cast<double>(a[i]) - static_cast<double>(b[i])); + + return acc / static_cast<double>(a.size()); +} + +static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) { + dat->resize(w * h * 3); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + unsigned char* org_px = &(*dat)[(y * w + x) * 3]; + org_px[0] = x * 3; // r + org_px[1] = x * 3 + 1; // g + org_px[2] = x * 3 + 2; // b + } + } +} + +TEST(JPEGCodec, EncodeDecodeRGB) { + int w = 20, h = 20; + + // create an image with known values + std::vector<unsigned char> original; + MakeRGBImage(w, h, &original); + + // encode, making sure it was compressed some + std::vector<unsigned char> encoded; + EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h, + w * 3, jpeg_quality, &encoded)); + EXPECT_GT(original.size(), encoded.size()); + + // decode, it should have the same size as the original + std::vector<unsigned char> decoded; + int outw, outh; + EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(), + JPEGCodec::FORMAT_RGB, &decoded, + &outw, &outh)); + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original.size(), decoded.size()); + + // Images must be approximately equal (compression will have introduced some + // minor artifacts). + ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded)); +} + +TEST(JPEGCodec, EncodeDecodeRGBA) { + int w = 20, h = 20; + + // create an image with known values, a must be opaque because it will be + // lost during compression + std::vector<unsigned char> original; + original.resize(w * h * 4); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + unsigned char* org_px = &original[(y * w + x) * 4]; + org_px[0] = x * 3; // r + org_px[1] = x * 3 + 1; // g + org_px[2] = x * 3 + 2; // b + org_px[3] = 0xFF; // a (opaque) + } + } + + // encode, making sure it was compressed some + std::vector<unsigned char> encoded; + EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGBA, w, h, + w * 4, jpeg_quality, &encoded)); + EXPECT_GT(original.size(), encoded.size()); + + // decode, it should have the same size as the original + std::vector<unsigned char> decoded; + int outw, outh; + EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(), + JPEGCodec::FORMAT_RGBA, &decoded, + &outw, &outh)); + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original.size(), decoded.size()); + + // Images must be approximately equal (compression will have introduced some + // minor artifacts). + ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded)); +} + +// Test that corrupted data decompression causes failures. +TEST(JPEGCodec, DecodeCorrupted) { + int w = 20, h = 20; + + // some random data (an uncompressed image) + std::vector<unsigned char> original; + MakeRGBImage(w, h, &original); + + // it should fail when given non-JPEG compressed data + std::vector<unsigned char> output; + int outw, outh; + ASSERT_FALSE(JPEGCodec::Decode(&original[0], original.size(), + JPEGCodec::FORMAT_RGB, &output, + &outw, &outh)); + + // make some compressed data + std::vector<unsigned char> compressed; + ASSERT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h, + w * 3, jpeg_quality, &compressed)); + + // try decompressing a truncated version + ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size() / 2, + JPEGCodec::FORMAT_RGB, &output, + &outw, &outh)); + + // corrupt it and try decompressing that + for (int i = 10; i < 30; i++) + compressed[i] = i; + ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size(), + JPEGCodec::FORMAT_RGB, &output, + &outw, &outh)); +}
\ No newline at end of file diff --git a/chrome/common/json_value_serializer.cc b/chrome/common/json_value_serializer.cc new file mode 100644 index 0000000..0049fc6 --- /dev/null +++ b/chrome/common/json_value_serializer.cc @@ -0,0 +1,101 @@ +// 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 "chrome/common/json_value_serializer.h" + +#include "base/json_reader.h" +#include "base/json_writer.h" +#include "base/string_util.h" +#include "chrome/common/logging_chrome.h" + +JSONStringValueSerializer::~JSONStringValueSerializer() {} + +bool JSONStringValueSerializer::Serialize(const Value& root) { + if (!json_string_ || initialized_with_const_string_) + return false; + + JSONWriter::Write(&root, pretty_print_, json_string_); + + return true; +} + +bool JSONStringValueSerializer::Deserialize(Value** root) { + if (!json_string_) + return false; + + return JSONReader::Read(*json_string_, root); +} + +/******* File Serializer *******/ + +bool JSONFileValueSerializer::Serialize(const Value& root) { + std::string json_string; + JSONStringValueSerializer serializer(&json_string); + serializer.set_pretty_print(true); + bool result = serializer.Serialize(root); + if (!result) + return false; + + FILE* file = NULL; + _wfopen_s(&file, json_file_path_.c_str(), L"wb"); + if (!file) + return false; + + size_t amount_written = + fwrite(json_string.data(), 1, json_string.size(), file); + fclose(file); + + return amount_written == json_string.size(); +} + +bool JSONFileValueSerializer::Deserialize(Value** root) { + FILE* file = NULL; + _wfopen_s(&file, json_file_path_.c_str(), L"rb"); + if (!file) + return false; + + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + rewind(file); + + bool result = false; + std::string json_string; + size_t chars_read = fread( + // WriteInto assumes the last character is a null, and it's not in this + // case, so we need to add 1 to our size to ensure the last character + // doesn't get cut off. + WriteInto(&json_string, file_size + 1), 1, file_size, file); + if (chars_read == file_size) { + JSONStringValueSerializer serializer(json_string); + result = serializer.Deserialize(root); + } + + fclose(file); + return result; +} diff --git a/chrome/common/json_value_serializer.h b/chrome/common/json_value_serializer.h new file mode 100644 index 0000000..6f3c067 --- /dev/null +++ b/chrome/common/json_value_serializer.h @@ -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. + +#ifndef CHROME_COMMON_JSON_VALUE_SERIALIZER_H__ +#define CHROME_COMMON_JSON_VALUE_SERIALIZER_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/values.h" + +class JSONStringValueSerializer : public ValueSerializer { + public: + // json_string is the string that will be source of the deserialization + // or the destination of the serialization. The caller of the constructor + // retains ownership of the string. + JSONStringValueSerializer(std::string* json_string) + : json_string_(json_string), + initialized_with_const_string_(false), + pretty_print_(false) { + } + + // This version allows initialization with a const string reference for + // deserialization only. + JSONStringValueSerializer(const std::string& json_string) + : json_string_(&const_cast<std::string&>(json_string)), + initialized_with_const_string_(true) { + } + + ~JSONStringValueSerializer(); + + // Attempt to serialize the data structure represented by Value into + // JSON. If the return value is true, the result will have been written + // into the string passed into the constructor. + bool Serialize(const Value& root); + + // Attempt to deserialize the data structure encoded in the string passed + // in to the constructor into a structure of Value objects. If the return + // value is true, the |root| parameter will be set to point to a new Value + // object that corresponds to the values represented in the string. The + // caller takes ownership of the returned Value objects. + bool Deserialize(Value** root); + + void set_pretty_print(bool new_value) { pretty_print_ = new_value; } + bool pretty_print() { return pretty_print_; } + + private: + std::string* json_string_; + bool initialized_with_const_string_; + bool pretty_print_; // If true, serialization will span multiple lines. + + DISALLOW_EVIL_CONSTRUCTORS(JSONStringValueSerializer); +}; + +class JSONFileValueSerializer : public ValueSerializer { + public: + // json_file_patch is the path of a file that will be source of the + // deserialization or the destination of the serialization. + // When deserializing, the file should exist, but when serializing, the + // serializer will attempt to create the file at the specified location. + JSONFileValueSerializer(const std::wstring& json_file_path) + : json_file_path_(json_file_path) {} + + ~JSONFileValueSerializer() {} + + // DO NOT USE except in unit tests to verify the file was written properly. + // We should never serialize directly to a file since this will block the + // thread. Instead, serialize to a string and write to the file you want on + // the file thread. + // + // Attempt to serialize the data structure represented by Value into + // JSON. If the return value is true, the result will have been written + // into the file whose name was passed into the constructor. + bool Serialize(const Value& root); + + // Attempt to deserialize the data structure encoded in the file passed + // in to the constructor into a structure of Value objects. If the return + // value is true, the |root| parameter will be set to point to a new Value + // object that corresponds to the values represented in the file. The + // caller takes ownership of the returned Value objects. + bool Deserialize(Value** root); + + private: + std::wstring json_file_path_; + + DISALLOW_EVIL_CONSTRUCTORS(JSONFileValueSerializer); +}; + +#endif // CHROME_COMMON_JSON_VALUE_SERIALIZER_H__ diff --git a/chrome/common/json_value_serializer_perftest.cc b/chrome/common/json_value_serializer_perftest.cc new file mode 100644 index 0000000..5df6e96 --- /dev/null +++ b/chrome/common/json_value_serializer_perftest.cc @@ -0,0 +1,116 @@ +// 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 "base/file_util.h" +#include "base/path_service.h" +#include "base/perftimer.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/logging_chrome.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +class JSONValueSerializerTests : public testing::Test { + protected: + virtual void SetUp() { + static const wchar_t* const kTestFilenames[] = { + L"serializer_nested_test.js", + L"serializer_test.js", + L"serializer_test_nowhitespace.js", + }; + + // Load test cases + for (size_t i = 0; i < arraysize(kTestFilenames); ++i) { + std::wstring filename; + EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &filename)); + file_util::AppendToPath(&filename, kTestFilenames[i]); + + std::string test_case; + EXPECT_TRUE(file_util::ReadFileToString(filename, &test_case)); + test_cases_.push_back(test_case); + } + } + + // Holds json strings to be tested. + std::vector<std::string> test_cases_; +}; + +} // namespace + +// Test deserialization of a json string into a Value object. We run the test +// using 3 sample strings for both the current decoder and jsoncpp's decoder. +TEST_F(JSONValueSerializerTests, Reading) { + printf("\n"); + const int kIterations = 100000; + + // Test chrome json implementation + PerfTimeLogger chrome_timer("chrome"); + for (int i = 0; i < kIterations; ++i) { + for (size_t j = 0; j < test_cases_.size(); ++j) { + Value* root = NULL; + JSONStringValueSerializer reader(test_cases_[j]); + ASSERT_TRUE(reader.Deserialize(&root)); + delete root; + } + } + chrome_timer.Done(); +} + +TEST_F(JSONValueSerializerTests, CompactWriting) { + printf("\n"); + const int kIterations = 100000; + // Convert test cases to Value objects. + std::vector<Value*> test_cases; + for (size_t i = 0; i < test_cases_.size(); ++i) { + Value* root = NULL; + JSONStringValueSerializer reader(test_cases_[i]); + ASSERT_TRUE(reader.Deserialize(&root)); + test_cases.push_back(root); + } + + PerfTimeLogger chrome_timer("chrome"); + for (int i = 0; i < kIterations; ++i) { + for (size_t j = 0; j < test_cases.size(); ++j) { + std::string json; + JSONStringValueSerializer reader(&json); + ASSERT_TRUE(reader.Serialize(*test_cases[j])); + } + } + chrome_timer.Done(); + + // Clean up test cases. + for (size_t i = 0; i < test_cases.size(); ++i) { + delete test_cases[i]; + test_cases[i] = NULL; + } +} diff --git a/chrome/common/json_value_serializer_unittest.cc b/chrome/common/json_value_serializer_unittest.cc new file mode 100644 index 0000000..acb2903 --- /dev/null +++ b/chrome/common/json_value_serializer_unittest.cc @@ -0,0 +1,349 @@ +// 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/json_reader.h" +#include "base/json_writer.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/json_value_serializer.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(JSONValueSerializerTest, Roundtrip) { + const std::string original_serialization = + "{\"bool\":true,\"int\":42,\"list\":[1,2],\"null\":null,\"real\":3.14}"; + Value* root = NULL; + JSONStringValueSerializer serializer(original_serialization); + ASSERT_TRUE(serializer.Deserialize(&root)); + ASSERT_TRUE(root); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + + DictionaryValue* root_dict = static_cast<DictionaryValue*>(root); + + Value* null_value = NULL; + ASSERT_TRUE(root_dict->Get(L"null", &null_value)); + ASSERT_TRUE(null_value); + ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL)); + + bool bool_value = false; + ASSERT_TRUE(root_dict->GetBoolean(L"bool", &bool_value)); + ASSERT_TRUE(bool_value); + + int int_value = 0; + ASSERT_TRUE(root_dict->GetInteger(L"int", &int_value)); + ASSERT_EQ(42, int_value); + + double real_value = 0.0; + ASSERT_TRUE(root_dict->GetReal(L"real", &real_value)); + ASSERT_DOUBLE_EQ(3.14, real_value); + + // We shouldn't be able to write using this serializer, since it was + // initialized with a const string. + ASSERT_FALSE(serializer.Serialize(*root_dict)); + + std::string test_serialization = ""; + JSONStringValueSerializer mutable_serializer(&test_serialization); + ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); + ASSERT_EQ(original_serialization, test_serialization); + + mutable_serializer.set_pretty_print(true); + ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); + const std::string pretty_serialization = + "{\r\n" + " \"bool\": true,\r\n" + " \"int\": 42,\r\n" + " \"list\": [ 1, 2 ],\r\n" + " \"null\": null,\r\n" + " \"real\": 3.14\r\n" + "}\r\n"; + ASSERT_EQ(pretty_serialization, test_serialization); + + delete root; +} + +TEST(JSONValueSerializerTest, StringEscape) { + std::wstring all_chars; + for (int i = 1; i < 256; ++i) { + all_chars += static_cast<wchar_t>(i); + } + // Generated in in Firefox using the following js (with an extra backslash for + // double quote): + // var s = ''; + // for (var i = 1; i < 256; ++i) { s += String.fromCharCode(i); } + // uneval(s).replace(/\\/g, "\\\\"); + std::string all_chars_expected = + "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0E\\x0F\\x10" + "\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1A\\x1B\\x1C\\x1D\\x1E" + "\\x1F !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\" + "\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7F\\x80\\x81\\x82\\x83\\x84\\x85" + "\\x86\\x87\\x88\\x89\\x8A\\x8B\\x8C\\x8D\\x8E\\x8F\\x90\\x91\\x92\\x93" + "\\x94\\x95\\x96\\x97\\x98\\x99\\x9A\\x9B\\x9C\\x9D\\x9E\\x9F\\xA0\\xA1" + "\\xA2\\xA3\\xA4\\xA5\\xA6\\xA7\\xA8\\xA9\\xAA\\xAB\\xAC\\xAD\\xAE\\xAF" + "\\xB0\\xB1\\xB2\\xB3\\xB4\\xB5\\xB6\\xB7\\xB8\\xB9\\xBA\\xBB\\xBC\\xBD" + "\\xBE\\xBF\\xC0\\xC1\\xC2\\xC3\\xC4\\xC5\\xC6\\xC7\\xC8\\xC9\\xCA\\xCB" + "\\xCC\\xCD\\xCE\\xCF\\xD0\\xD1\\xD2\\xD3\\xD4\\xD5\\xD6\\xD7\\xD8\\xD9" + "\\xDA\\xDB\\xDC\\xDD\\xDE\\xDF\\xE0\\xE1\\xE2\\xE3\\xE4\\xE5\\xE6\\xE7" + "\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\\xF0\\xF1\\xF2\\xF3\\xF4\\xF5" + "\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF"; + + std::string expected_output = "{\"all_chars\":\"" + all_chars_expected + + "\"}"; + // Test JSONWriter interface + std::string output_js; + DictionaryValue valueRoot; + valueRoot.SetString(L"all_chars", all_chars); + JSONWriter::Write(&valueRoot, false, &output_js); + ASSERT_EQ(expected_output, output_js); + + // Test JSONValueSerializer interface (uses JSONWriter). + JSONStringValueSerializer serializer(&output_js); + ASSERT_TRUE(serializer.Serialize(valueRoot)); + ASSERT_EQ(expected_output, output_js); +} + +TEST(JSONValueSerializerTest, UnicodeStrings) { + // unicode string json -> escaped ascii text + DictionaryValue root; + std::wstring test(L"\x7F51\x9875"); + root.SetString(L"web", test); + + std::string expected = "{\"web\":\"\\u7F51\\u9875\"}"; + + std::string actual; + JSONStringValueSerializer serializer(&actual); + ASSERT_TRUE(serializer.Serialize(root)); + ASSERT_EQ(expected, actual); + + // escaped ascii text -> json + Value* deserial_root = NULL; + JSONStringValueSerializer deserializer(expected); + ASSERT_TRUE(deserializer.Deserialize(&deserial_root)); + DictionaryValue* dict_root = static_cast<DictionaryValue*>(deserial_root); + std::wstring web_value; + ASSERT_TRUE(dict_root->GetString(L"web", &web_value)); + ASSERT_EQ(test, web_value); + delete deserial_root; +} + +TEST(JSONValueSerializerTest, HexStrings) { + // hex string json -> escaped ascii text + DictionaryValue root; + std::wstring test(L"\x01\x02"); + root.SetString(L"test", test); + + std::string expected = "{\"test\":\"\\x01\\x02\"}"; + + std::string actual; + JSONStringValueSerializer serializer(&actual); + ASSERT_TRUE(serializer.Serialize(root)); + ASSERT_EQ(expected, actual); + + // escaped ascii text -> json + Value* deserial_root = NULL; + JSONStringValueSerializer deserializer(expected); + ASSERT_TRUE(deserializer.Deserialize(&deserial_root)); + DictionaryValue* dict_root = static_cast<DictionaryValue*>(deserial_root); + std::wstring test_value; + ASSERT_TRUE(dict_root->GetString(L"test", &test_value)); + ASSERT_EQ(test, test_value); + delete deserial_root; + + // Test converting escaped regular chars + deserial_root = NULL; + std::string escaped_chars = "{\"test\":\"\\x67\\x6f\"}"; + JSONStringValueSerializer deserializer2(escaped_chars); + ASSERT_TRUE(deserializer2.Deserialize(&deserial_root)); + dict_root = static_cast<DictionaryValue*>(deserial_root); + ASSERT_TRUE(dict_root->GetString(L"test", &test_value)); + ASSERT_EQ(std::wstring(L"go"), test_value); + delete deserial_root; +} + +namespace { + +void ValidateJsonList(const std::string& json) { + Value* root = NULL; + ASSERT_TRUE(JSONReader::Read(json, &root)); + ASSERT_TRUE(root && root->IsType(Value::TYPE_LIST)); + ListValue* list = static_cast<ListValue*>(root); + ASSERT_EQ(1, list->GetSize()); + Value* elt = NULL; + ASSERT_TRUE(list->Get(0, &elt)); + int value = 0; + ASSERT_TRUE(elt && elt->GetAsInteger(&value)); + ASSERT_EQ(1, value); + delete root; +} + +} // namespace + +TEST(JSONValueSerializerTest, JSONReaderComments) { + ValidateJsonList("[ // 2, 3, ignore me ] \n1 ]"); + ValidateJsonList("[ /* 2, \n3, ignore me ]*/ \n1 ]"); + ValidateJsonList("//header\n[ // 2, \n// 3, \n1 ]// footer"); + ValidateJsonList("/*\n[ // 2, \n// 3, \n1 ]*/[1]"); + ValidateJsonList("[ 1 /* one */ ] /* end */"); + ValidateJsonList("[ 1 //// ,2\r\n ]"); + + Value* root = NULL; + // It's ok to have a comment in a string. + ASSERT_TRUE(JSONReader::Read("[\"// ok\\n /* foo */ \"]", &root)); + ASSERT_TRUE(root && root->IsType(Value::TYPE_LIST)); + ListValue* list = static_cast<ListValue*>(root); + ASSERT_EQ(1, list->GetSize()); + Value* elt = NULL; + ASSERT_TRUE(list->Get(0, &elt)); + std::wstring value; + ASSERT_TRUE(elt && elt->GetAsString(&value)); + ASSERT_EQ(L"// ok\n /* foo */ ", value); + delete root; + + root = NULL; + // You can't nest comments. + ASSERT_FALSE(JSONReader::Read("/* /* inner */ outer */ [ 1 ]", &root)); + + // Not a open comment token. + ASSERT_FALSE(JSONReader::Read("/ * * / [1]", &root)); +} + +namespace { + class JSONFileValueSerializerTest : public testing::Test { + protected: + virtual void SetUp() { + // Name a subdirectory of the temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + file_util::AppendToPath(&test_dir_, L"JSONFileValueSerializerTest"); + + // Create a fresh, empty copy of this directory. + file_util::Delete(test_dir_, true); + CreateDirectory(test_dir_.c_str(), NULL); + } + virtual void TearDown() { + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + std::wstring test_dir_; + }; +} // anonymous namespace + +TEST_F(JSONFileValueSerializerTest, Roundtrip) { + std::wstring original_file_path; + ASSERT_TRUE( + PathService::Get(chrome::DIR_TEST_DATA, &original_file_path)); + file_util::AppendToPath(&original_file_path, L"serializer_test.js"); + + ASSERT_TRUE(file_util::PathExists(original_file_path)); + + JSONFileValueSerializer deserializer(original_file_path); + Value* root; + ASSERT_TRUE(deserializer.Deserialize(&root)); + + ASSERT_TRUE(root); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + + DictionaryValue* root_dict = static_cast<DictionaryValue*>(root); + + Value* null_value = NULL; + ASSERT_TRUE(root_dict->Get(L"null", &null_value)); + ASSERT_TRUE(null_value); + ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL)); + + bool bool_value = false; + ASSERT_TRUE(root_dict->GetBoolean(L"bool", &bool_value)); + ASSERT_TRUE(bool_value); + + int int_value = 0; + ASSERT_TRUE(root_dict->GetInteger(L"int", &int_value)); + ASSERT_EQ(42, int_value); + + std::wstring string_value; + ASSERT_TRUE(root_dict->GetString(L"string", &string_value)); + ASSERT_EQ(L"hello", string_value); + + // Now try writing. + std::wstring written_file_path = test_dir_; + file_util::AppendToPath(&written_file_path, L"test_output.js"); + + ASSERT_FALSE(file_util::PathExists(written_file_path)); + JSONFileValueSerializer serializer(written_file_path); + ASSERT_TRUE(serializer.Serialize(*root)); + ASSERT_TRUE(file_util::PathExists(written_file_path)); + + // Now compare file contents. + EXPECT_TRUE(file_util::ContentsEqual(original_file_path, written_file_path)); + EXPECT_TRUE(file_util::Delete(written_file_path, false)); + + delete root; +} + +TEST_F(JSONFileValueSerializerTest, RoundtripNested) { + std::wstring original_file_path; + ASSERT_TRUE( + PathService::Get(chrome::DIR_TEST_DATA, &original_file_path)); + file_util::AppendToPath(&original_file_path, L"serializer_nested_test.js"); + + ASSERT_TRUE(file_util::PathExists(original_file_path)); + + JSONFileValueSerializer deserializer(original_file_path); + Value* root; + ASSERT_TRUE(deserializer.Deserialize(&root)); + + // Now try writing. + std::wstring written_file_path = test_dir_; + file_util::AppendToPath(&written_file_path, L"test_output.js"); + + ASSERT_FALSE(file_util::PathExists(written_file_path)); + JSONFileValueSerializer serializer(written_file_path); + ASSERT_TRUE(serializer.Serialize(*root)); + ASSERT_TRUE(file_util::PathExists(written_file_path)); + + // Now compare file contents. + EXPECT_TRUE(file_util::ContentsEqual(original_file_path, written_file_path)); + EXPECT_TRUE(file_util::Delete(written_file_path, false)); + + delete root; +} + +TEST_F(JSONFileValueSerializerTest, NoWhitespace) { + std::wstring source_file_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_file_path)); + file_util::AppendToPath(&source_file_path, + L"serializer_test_nowhitespace.js"); + ASSERT_TRUE(file_util::PathExists(source_file_path)); + JSONFileValueSerializer serializer(source_file_path); + Value* root; + ASSERT_TRUE(serializer.Deserialize(&root)); + ASSERT_TRUE(root); + delete root; +} diff --git a/chrome/common/jstemplate_builder.cc b/chrome/common/jstemplate_builder.cc new file mode 100644 index 0000000..d10104f --- /dev/null +++ b/chrome/common/jstemplate_builder.cc @@ -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. + +// A helper function for using JsTemplate. See jstemplate_builder.h for more +// info. + +#include "chrome/common/jstemplate_builder.h" + +#include "chrome/common/common_resources.h" +#include "chrome/common/json_value_serializer.h" +#include "base/logging.h" +#include "chrome/common/resource_bundle.h" +#include "base/string_util.h" + +namespace jstemplate_builder { + +std::string GetTemplateHtml(const StringPiece& html_template, + const DictionaryValue* json, + const StringPiece& template_id) { + // fetch and cache the pointer of the jstemplate resource source text. + static const StringPiece jstemplate_src( + ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_JSTEMPLATE_JS)); + + if (jstemplate_src.empty()) { + NOTREACHED() << "Unable to get jstemplate src"; + return std::string(); + } + + // Convert the template data to a json string. + DCHECK(json) << "must include json data structure"; + + std::string jstext; + JSONStringValueSerializer serializer(&jstext); + serializer.Serialize(*json); + // </ confuses the HTML parser because it could be a </script> tag. So we + // replace </ with <\/. The extra \ will be ignored by the JS engine. + ReplaceSubstringsAfterOffset(&jstext, 0, "</", "<\\/"); + + std::string output(html_template.data(), html_template.size()); + output.append("<script>"); + output.append(jstemplate_src.data(), jstemplate_src.size()); + output.append("var tp = document.getElementById('"); + output.append(template_id.data(), template_id.size()); + output.append("'); var cx = new JsExprContext("); + output.append(jstext); + output.append("); jstProcess(cx, tp);</script>"); + + return output; +} + +} // namespace jstemplate_builder diff --git a/chrome/common/jstemplate_builder.h b/chrome/common/jstemplate_builder.h new file mode 100644 index 0000000..06925ba --- /dev/null +++ b/chrome/common/jstemplate_builder.h @@ -0,0 +1,54 @@ +// 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 provides some helper methods for building and rendering an +// internal html page. The flow is as follows: +// - instantiate a builder given a webframe that we're going to render content +// into +// - load the template html and load the jstemplate javascript into the frame +// - given a json data object, run the jstemplate javascript which fills in +// template values + +#ifndef CHROME_RENDERER_JSTEMPLATE_BUILDER_H__ +#define CHROME_RENDERER_JSTEMPLATE_BUILDER_H__ + +#include <string> + +#include "base/values.h" +#include "base/string_piece.h" + +namespace jstemplate_builder { + // A helper function that generates a string of HTML to be loaded. The + // string includes the HTML and the javascript code necessary to generate the + // full page. + std::string GetTemplateHtml(const StringPiece& html_template, + const DictionaryValue* json, + const StringPiece& template_id); +} // namespace jstemplate_builder +#endif // CHROME_RENDERER_JSTEMPLATE_BUILDER_H__ diff --git a/chrome/common/l10n_util.cc b/chrome/common/l10n_util.cc new file mode 100644 index 0000000..5a66494 --- /dev/null +++ b/chrome/common/l10n_util.cc @@ -0,0 +1,570 @@ +// 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 "chrome/common/l10n_util.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/views/view.h" +#include "unicode/coll.h" +#include "unicode/locid.h" +#include "unicode/rbbi.h" +#include "unicode/uchar.h" + +namespace { + +// Added to the end of strings that are too big in TrucateString. +static const wchar_t* const kElideString = L"\x2026"; + +// Get language and region from the OS. +void GetLanguageAndRegionFromOS(std::string* lang, std::string* region) { + // Later we may have to change this to be OS-dependent so that + // it's not affected by ICU's default locale. It's all right + // to do this way because SetICUDefaultLocale is internal + // to this file and we know where/when it's called. + Locale locale = Locale::getDefault(); + const char* language = locale.getLanguage(); + const char* country = locale.getCountry(); + DCHECK(language); + *lang = language; + *region = country; +} + +// Convert Chrome locale name (DLL name) to ICU locale name +std::string ICULocaleName(const std::wstring& locale_string) { + // If not Spanish, just return it. + if (locale_string.substr(0, 2) != L"es") + return WideToASCII(locale_string); + // Expand es to es-ES. + if (LowerCaseEqualsASCII(locale_string, "es")) + return "es-ES"; + // Map es-419 (Latin American Spanish) to es-FOO depending on the system + // locale. If it's es-RR other than es-ES, map to es-RR. Otherwise, map + // to es-MX (the most populous in Spanish-speaking Latin America). + if (LowerCaseEqualsASCII(locale_string, "es-419")) { + std::string lang, region; + GetLanguageAndRegionFromOS(&lang, ®ion); + if (LowerCaseEqualsASCII(lang, "es") && + !LowerCaseEqualsASCII(region, "es")) { + lang.append("-"); + lang.append(region); + return lang; + } + return "es-MX"; + } + // Currently, Chrome has only "es" and "es-419", but later we may have + // more specific "es-RR". + return WideToASCII(locale_string); +} + +// Sets the default locale of ICU. +// When the application locale (UI locale) of Chrome is specified with +// '--lang' command line flag or 'intl.app_locale' entry in the "Preferences", +// the default locale of ICU need to be changed to match the application locale +// so that ICU functions work correctly in a locale-dependent manner. +// This is handy in that we don't have to call GetApplicationLocale() +// everytime we call locale-dependent ICU APIs as long as we make sure +// that this is called before any locale-dependent API is called. +UBool SetICUDefaultLocale(const std::wstring& locale_string) { + Locale locale(ICULocaleName(locale_string).c_str()); + UErrorCode error_code = U_ZERO_ERROR; + Locale::setDefault(locale, error_code); + // This return value is actually bogus because Locale object is + // an ID and setDefault seems to always succeed (regardless of the + // presence of actual locale data). However, + // it does not hurt to have it as a sanity check. + return U_SUCCESS(error_code); +} + +// Compares two wstrings and returns true if the first arg is less than the +// second arg. This uses the locale specified in the constructor. +class StringComparator : public std::binary_function<const std::wstring&, + const std::wstring&, + bool> { + public: + explicit StringComparator(Collator* collator) + : collator_(collator) { } + + // Returns true if lhs preceeds rhs. + bool operator() (const std::wstring& lhs, const std::wstring& rhs) { + UErrorCode error = U_ZERO_ERROR; + UCollationResult result = collator_->compare( + static_cast<const UChar*>(lhs.c_str()), static_cast<int>(lhs.length()), + static_cast<const UChar*>(rhs.c_str()), static_cast<int>(rhs.length()), + error); + DCHECK(U_SUCCESS(error)); + + return result == UCOL_LESS; + } + + private: + Collator* collator_; +}; + +// Returns true if |locale_name| has an alias in the ICU data file. +bool IsDuplicateName(const std::string& locale_name) { + static const char* const kDuplicateNames[] = { + "en", + "pt", + "zh", + "zh_hans_cn", + "zh_hant_tw" + }; + + // Skip all 'es_RR'. Currently, we use 'es' for es-ES (Spanish in Spain). + // 'es-419' (Spanish in Latin America) is not available in ICU so that it + // has to be added manually in GetAvailableLocales(). + if (LowerCaseEqualsASCII(locale_name.substr(0,3), "es_")) + return true; + for (int i = 0; i < arraysize(kDuplicateNames); ++i) { + if (_stricmp(kDuplicateNames[i], locale_name.c_str()) == 0) + return true; + } + return false; +} + +bool IsLocaleAvailable(const std::wstring& locale, const std::wstring& locale_path) { + std::wstring test_locale = locale; + // If locale has any illegal characters in it, we don't want to try to + // load it because it may be pointing outside the locale dll directory. + file_util::ReplaceIllegalCharacters(&test_locale, ' '); + if (test_locale != locale) + return false; + + std::wstring test_path = locale_path; + file_util::AppendToPath(&test_path, locale + L".dll"); + return file_util::PathExists(test_path) && SetICUDefaultLocale(locale); +} + +bool CheckAndResolveLocale(const std::wstring& locale, + const std::wstring& locale_path, + std::wstring* resolved_locale) { + if (IsLocaleAvailable(locale, locale_path)) { + *resolved_locale = locale; + return true; + } + // If the locale matches language but not country, use that instead. + // TODO(jungshik) : we need a more extensive resolution (aliasing, + // contraction/expansion) to take care of various edge cases + // (zh-{HK,SG,MK}, en => en-US, es => es-{ES,419}). + // Also, this does not do anything about languages that Chrome + // currently does not support but available on Windows. We fall + // back to en-US in GetApplicationLocale so that it's a not critical, + // but we can do better. + std::wstring::size_type hyphen_pos = locale.find(L'-'); + if (hyphen_pos != std::wstring::npos && hyphen_pos > 0) { + std::wstring lang(locale, 0, hyphen_pos); + std::wstring region(locale, hyphen_pos + 1); + std::wstring tmp_locale(lang); + // Map es-RR other than es-ES to es-419 (Chrome's Latin American Spanish locale). + if (LowerCaseEqualsASCII(lang, "es") && !LowerCaseEqualsASCII(region, "es")) + tmp_locale.append(L"-419"); + if (IsLocaleAvailable(tmp_locale, locale_path)) { + resolved_locale->swap(tmp_locale); + return true; + } + } + return false; +} + +// Get the locale of the operating system. The return value is of the form +// language[-country] (e.g., en-US) where the language is the 2 letter code from +// ISO-639. +std::wstring GetSystemLocale() { + std::string language, region; + GetLanguageAndRegionFromOS(&language, ®ion); + std::string ret; + if (!language.empty()) + ret.append(language); + if (!region.empty()) { + ret.append("-"); + ret.append(region); + } + return ASCIIToWide(ret); +} + +} // namespace + +namespace l10n_util { + +// Represents the locale-specific text direction. +static TextDirection g_text_direction = UNKNOWN_DIRECTION; + +std::wstring GetApplicationLocale(const std::wstring& pref_locale) { + std::wstring locale_path; + PathService::Get(chrome::DIR_LOCALES, &locale_path); + std::wstring resolved_locale; + + // First, check to see if there's a --lang flag. + CommandLine parsed_command_line; + const std::wstring& lang_arg = + parsed_command_line.GetSwitchValue(switches::kLang); + if (!lang_arg.empty()) { + if (CheckAndResolveLocale(lang_arg, locale_path, &resolved_locale)) + return resolved_locale; + } + + // Second, try user prefs. + if (!pref_locale.empty()) { + if (CheckAndResolveLocale(pref_locale, locale_path, &resolved_locale)) + return resolved_locale; + } + + // Next, try the system locale. + const std::wstring system_locale = GetSystemLocale(); + if (CheckAndResolveLocale(system_locale, locale_path, &resolved_locale)) + return resolved_locale; + + // Fallback on en-US. + const std::wstring fallback_locale(L"en-US"); + if (IsLocaleAvailable(fallback_locale, locale_path)) + return fallback_locale; + + // No DLL, we shouldn't get here. + NOTREACHED(); + return std::wstring(); +} + +std::wstring GetLocalName(const std::wstring& locale_code_wstr, + const std::wstring& app_locale_wstr, + bool is_for_ui) { + std::string locale_code_str = WideToASCII(locale_code_wstr); + const std::string app_locale = WideToASCII(app_locale_wstr); + const char* locale_code = locale_code_str.c_str(); + UErrorCode error = U_ZERO_ERROR; + const int buffer_size = 1024; + std::wstring name_local; + int actual_size = uloc_getDisplayName(locale_code, app_locale.c_str(), + WriteInto(&name_local, buffer_size + 1), buffer_size, &error); + DCHECK(U_SUCCESS(error)); + name_local.resize(actual_size); + // Add an RTL mark so parentheses are properly placed. + if (is_for_ui && GetTextDirection() == RIGHT_TO_LEFT) + return name_local + L"\x200f"; + else + return name_local; +} + +std::wstring GetString(int message_id) { + ResourceBundle &rb = ResourceBundle::GetSharedInstance(); + return rb.GetLocalizedString(message_id); +} + +static std::wstring GetStringF(int message_id, + const std::wstring& a, + const std::wstring& b, + const std::wstring& c, + const std::wstring& d, + std::vector<size_t>* offsets) { + const std::wstring& format_string = GetString(message_id); + std::wstring formatted = ReplaceStringPlaceholders(format_string, a, b, c, + d, offsets); + return formatted; +} + +std::wstring GetStringF(int message_id, + const std::wstring& a) { + return GetStringF(message_id, a, std::wstring(), std::wstring(), + std::wstring(), NULL); +} + +std::wstring GetStringF(int message_id, + const std::wstring& a, + const std::wstring& b) { + return GetStringF(message_id, a, b, std::wstring(), std::wstring(), NULL); +} + +std::wstring GetStringF(int message_id, + const std::wstring& a, + const std::wstring& b, + const std::wstring& c) { + return GetStringF(message_id, a, b, c, std::wstring(), NULL); +} + +std::wstring GetStringF(int message_id, + const std::wstring& a, + size_t* offset) { + DCHECK(offset); + std::vector<size_t> offsets; + std::wstring result = GetStringF(message_id, a, std::wstring(), + std::wstring(), std::wstring(), &offsets); + DCHECK(offsets.size() == 1); + *offset = offsets[0]; + return result; +} + +std::wstring GetStringF(int message_id, + const std::wstring& a, + const std::wstring& b, + std::vector<size_t>* offsets) { + return GetStringF(message_id, a, b, std::wstring(), std::wstring(), offsets); +} + +std::wstring GetStringF(int message_id, int a) { + return GetStringF(message_id, IntToWString(a)); +} + +std::wstring GetStringF(int message_id, int64 a) { + return GetStringF(message_id, Int64ToWString(a)); +} + +std::wstring TruncateString(const std::wstring& string, size_t length) { + if (string.size() <= length) + // String fits, return it. + return string; + + if (length == 0) { + // No room for the ellide string, return an empty string. + return std::wstring(L""); + } + size_t max = length - 1; + + if (max == 0) { + // Just enough room for the elide string. + return kElideString; + } + + // Use a line iterator to find the first boundary. + UErrorCode status = U_ZERO_ERROR; + scoped_ptr<RuleBasedBreakIterator> bi(static_cast<RuleBasedBreakIterator*>( + RuleBasedBreakIterator::createLineInstance(Locale::getDefault(), status))); + if (U_FAILURE(status)) + return string.substr(0, max) + kElideString; + bi->setText(string.c_str()); + int32_t index = bi->preceding(static_cast<int32_t>(max)); + if (index == BreakIterator::DONE) { + index = static_cast<int32_t>(max); + } else { + // Found a valid break (may be the beginning of the string). Now use + // a character iterator to find the previous non-whitespace character. + StringCharacterIterator char_iterator(string.c_str()); + if (index == 0) { + // No valid line breaks. Start at the end again. This ensures we break + // on a valid character boundary. + index = static_cast<int32_t>(max); + } + char_iterator.setIndex(index); + while (char_iterator.hasPrevious()) { + char_iterator.previous(); + if (!(u_isspace(char_iterator.current()) || + u_charType(char_iterator.current()) == U_CONTROL_CHAR || + u_charType(char_iterator.current()) == U_NON_SPACING_MARK)) { + // Not a whitespace character. Advance the iterator so that we + // include the current character in the truncated string. + char_iterator.next(); + break; + } + } + if (char_iterator.hasPrevious()) { + // Found a valid break point. + index = char_iterator.getIndex(); + } else { + // String has leading whitespace, return the elide string. + return kElideString; + } + } + return string.substr(0, index) + kElideString; +} + +std::wstring ToLower(const std::wstring& string) { + UnicodeString lower_u_str( + UnicodeString(string.c_str()).toLower(Locale::getDefault())); + std::wstring result; + lower_u_str.extract(0, lower_u_str.length(), + WriteInto(&result, lower_u_str.length() + 1)); + return result; +} + +// Returns the text direction. +// This function retrieves the language corresponding to the default ICU locale +// (assuming that SetICUDefaultLocale is called) and determines the text +// direction by comparing it with "ar" or "he". +// Note that script is better than language here to get a wider coverage. +// Unfortunately, getScript in ICU returns an empty string unless +// the locale is created with an explicit script specified. For now, +// it does not matter much because we only support Hebrew and Arabic. +// (c.f. other languages written in RTL : Farsi, Urdu, Syriac, Azerbaijani +// in Arabic, etc) +// TODO(hbono): Need to find better identification methods than checking +// if the language ID is Arabic or Hebrew. (http://b/issue?id=1054119) +// Use an ICU API when added (see http://bugs.icu-project.org/trac/ticket/6228). +TextDirection GetTextDirection() { + if (g_text_direction == UNKNOWN_DIRECTION) { + const Locale& locale = Locale::getDefault(); + const char* lang = locale.getLanguage(); + // Check only for Arabic and Hebrew languages now. + if (strcmp(lang, "ar") == 0 || strcmp(lang, "he") == 0) { + g_text_direction = RIGHT_TO_LEFT; + } else { + g_text_direction = LEFT_TO_RIGHT; + } + } + return g_text_direction; +} + +bool AdjustStringForLocaleDirection(const std::wstring& text, + std::wstring* localized_text) { + if (GetTextDirection() == LEFT_TO_RIGHT || text.length() == 0) + return false; + + // Marking the string as LTR if the locale is RTL and the string does not + // contain strong RTL characters. Otherwise, mark the string as RTL. + *localized_text = text; + bool has_rtl_chars = StringContainsStrongRTLChars(text); + if (!has_rtl_chars) + WrapStringWithLTRFormatting(localized_text); + else + WrapStringWithRTLFormatting(localized_text); + + return true; +} + +bool StringContainsStrongRTLChars(const std::wstring& text) { + const wchar_t* string = text.c_str(); + int length = static_cast<int>(text.length()); + int position = 0; + while (position < length) { + UChar32 character; + int next_position = position; + U16_NEXT(string, next_position, length, character); + + // Now that we have the character, we use ICU in order to query for the + // appropriate Unicode BiDi character type. + int32_t property = u_getIntPropertyValue(character, UCHAR_BIDI_CLASS); + if ((property == U_RIGHT_TO_LEFT) || (property == U_RIGHT_TO_LEFT_ARABIC)) + return true; + + position = next_position; + } + + return false; +} + +void WrapStringWithLTRFormatting(std::wstring* text) { + // Inserting an LRE (Left-To-Right Embedding) mark as the first character. + text->insert(0, L"\x202A"); + + // Inserting a PDF (Pop Directional Formatting) mark as the last character. + text->append(L"\x202C"); +} + +void WrapStringWithRTLFormatting(std::wstring* text) { + // Inserting an RLE (Right-To-Left Embedding) mark as the first character. + text->insert(0, L"\x202B"); + + // Inserting a PDF (Pop Directional Formatting) mark as the last character. + text->append(L"\x202C"); +} + +// Returns locale-dependent externded window styles. +int GetExtendedStyles() { + return GetTextDirection() == LEFT_TO_RIGHT ? 0 : + WS_EX_LAYOUTRTL | WS_EX_RTLREADING; +} + +int GetExtendedTooltipStyles() { + return GetTextDirection() == LEFT_TO_RIGHT ? 0 : WS_EX_LAYOUTRTL; +} + +int DefaultCanvasTextAlignment() { + if (GetTextDirection() == LEFT_TO_RIGHT) { + return ChromeCanvas::TEXT_ALIGN_LEFT; + } else { + return ChromeCanvas::TEXT_ALIGN_RIGHT; + } +} + +void HWNDSetRTLLayout(HWND hwnd) { + DWORD ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + + // We don't have to do anything if the style is already set for the HWND. + if (!(ex_style & WS_EX_LAYOUTRTL)) { + ex_style |= WS_EX_LAYOUTRTL; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + + // Right-to-left layout changes are not applied to the window immediately + // so we should make sure a WM_PAINT is sent to the window by invalidating + // the entire window rect. + ::InvalidateRect(hwnd, NULL, true); + } +} + +void SortStrings(const std::wstring& locale, + std::vector<std::wstring>* strings) { + UErrorCode error = U_ZERO_ERROR; + Locale loc(WideToUTF8(locale).c_str()); + scoped_ptr<Collator> collator(Collator::createInstance(loc, error)); + if (U_FAILURE(error)) { + // Just do an string sort. + sort(strings->begin(), strings->end()); + return; + } + StringComparator c(collator.get()); + sort(strings->begin(), strings->end(), c); +} + +const std::vector<std::wstring>& GetAvailableLocales() { + static std::vector<std::wstring> locales; + if (locales.empty()) { + int num_locales = uloc_countAvailable(); + for (int i = 0; i < num_locales; ++i) { + std::string locale_name = uloc_getAvailable(i); + // Filter out the names that have aliases. + if (IsDuplicateName(locale_name)) + continue; + // Normalize underscores to hyphens because that's what our locale dlls + // use. + std::replace(locale_name.begin(), locale_name.end(), '_', '-'); + + // Map the Chinese locale names over to zh-CN and zh-TW. + if (LowerCaseEqualsASCII(locale_name, "zh-hans")) { + locale_name = "zh-CN"; + } else if (LowerCaseEqualsASCII(locale_name, "zh-hant")) { + locale_name = "zh-TW"; + } + locales.push_back(ASCIIToWide(locale_name)); + } + + // Manually add 'es-419' to the list. See the comment in IsDuplicateName(). + locales.push_back(L"es-419"); + } + return locales; +} + +} diff --git a/chrome/common/l10n_util.h b/chrome/common/l10n_util.h new file mode 100644 index 0000000..68fb12e --- /dev/null +++ b/chrome/common/l10n_util.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. + +// This file contains utility functions for dealing with localized +// content. + +#ifndef CHROME_COMMON_L10N_UTIL_H__ +#define CHROME_COMMON_L10N_UTIL_H__ + +#include <windows.h> +#include <string> +#include <vector> + +#include "base/basictypes.h" + +class PrefService; + +namespace l10n_util { + +// This method is responsible for determining the locale as defined below. In +// nearly all cases you shouldn't call this, rather use GetApplicationLocale +// defined on browser_process. +// +// Returns the locale used by the Application. First we use the value from the +// command line (--lang), second we try the value in the prefs file (passed in +// as |pref_locale|), finally, we fall back on the system locale. We only return +// a value if there's a corresponding resource DLL for the locale. Otherwise, +// we fall back to en-us. +std::wstring GetApplicationLocale(const std::wstring& pref_locale); + +// This method returns the Local Name of the Locale Code. For example, for +// |local_code_wstr| = "en-US", it returns "English (United States)". +// |app_locale_wstr| can be obtained in the UI thread - for example: +// const std::wstring app_locale_wstr = g_browser_process-> +// GetApplicationLocale(); +// If |is_for_ui| is true, U+200F is appended so that it can be +// rendered properly in a RTL Chrome. +std::wstring GetLocalName(const std::wstring& locale_code_wstr, + const std::wstring& app_locale_wstr, + bool is_for_ui); + +// Pulls resource string from the string bundle and returns it. +std::wstring GetString(int message_id); + +// Get a resource string and replace $1-$2-$3 with |a| and |b| +// respectively. Additionally, $$ is replaced by $. +std::wstring GetStringF(int message_id, + const std::wstring& a); +std::wstring GetStringF(int message_id, + const std::wstring& a, + const std::wstring& b); +std::wstring GetStringF(int message_id, + const std::wstring& a, + const std::wstring& b, + const std::wstring& c); + +// Variants that return the offset(s) of the replaced parameters. The +// vector based version returns offsets ordered by parameter. For example if +// invoked with a and b offsets[0] gives the offset for a and offsets[1] the +// offset of b regardless of where the parameters end up in the string. +std::wstring GetStringF(int message_id, + const std::wstring& a, + size_t* offset); +std::wstring GetStringF(int message_id, + const std::wstring& a, + const std::wstring& b, + std::vector<size_t>* offsets); + +// Convenience formatters for a single number. +std::wstring GetStringF(int message_id, int a); +std::wstring GetStringF(int message_id, int64 a); + +// Truncates the string to length characters. This breaks the string at +// the first word break before length, adding the horizontal ellipsis +// character (unicode character 0x2026) to render ... +// The supplied string is returned if the string has length characters or +// less. +std::wstring TruncateString(const std::wstring& string, size_t length); + +// Returns the lower case equivalent of string. +std::wstring ToLower(const std::wstring& string); + +// Represents the text direction returned by the GetTextDirection() function. +enum TextDirection { + UNKNOWN_DIRECTION, + RIGHT_TO_LEFT, + LEFT_TO_RIGHT, +}; + +// Returns the locale-specific text direction. +// This function retrieves the application locale and determines the text +// direction. Its possible results are listed below: +// * LEFT_TO_RIGHT: Left-To-Right (e.g. English, Chinese, etc.); +// * RIGHT_TO_LEFT: Right-To-Left (e.g. Arabic, Hebrew, etc.), and; +// * UNKNOWN_DIRECTION: unknown (or error). +TextDirection GetTextDirection(); + +// Given the string in |text|, this function creates a copy of the string with +// the appropriate Unicode formatting marks that mark the string direction +// (either left-to-right or right-to-left). The new string is returned in +// |localized_text|. The function checks both the current locale and the +// contents of the string in order to determine the direction of the returned +// string. The function returns true if the string in |text| was properly +// adjusted. +// +// Certain LTR strings are not rendered correctly when the context is RTL. For +// example, the string "Foo!" will appear as "!Foo" if it is rendered as is in +// an RTL context. Calling this function will make sure the returned localized +// string is always treated as a right-to-left string. This is done by +// inserting certain Unicode formatting marks into the returned string. +// +// TODO(idana) bug# 1206120: this function adjusts the string in question only +// if the current locale is right-to-left. The function does not take care of +// the opposite case (an RTL string displayed in an LTR context) since +// adjusting the string involves inserting Unicode formatting characters that +// Windows does not handle well unless right-to-left language support is +// installed. Since the English version of Windows doesn't have right-to-left +// language support installed by default, inserting the direction Unicode mark +// results in Windows displaying squares. +bool AdjustStringForLocaleDirection(const std::wstring& text, + std::wstring* localized_text); + +// Returns true if the string contains at least one character with strong right +// to left directionality; that is, a character with either R or AL Unicode +// BiDi character type. +bool StringContainsStrongRTLChars(const std::wstring& text); + +// Wraps a string with an LRE-PDF pair which essentialy marks the string as a +// Left-To-Right string. Doing this is useful in order to make sure LTR +// strings are rendered properly in an RTL context. +void WrapStringWithLTRFormatting(std::wstring* text); + +// Wraps a string with an RLE-PDF pair which essentialy marks the string as a +// Right-To-Left string. Doing this is useful in order to make sure RTL +// strings are rendered properly in an LTR context. +void WrapStringWithRTLFormatting(std::wstring* text); + +// Returns the locale-dependent extended window styles. +// This function is used for adding locale-dependent extended window styles +// (e.g. WS_EX_LAYOUTRTL, WS_EX_RTLREADING, etc.) when creating a window. +// Callers should OR this value into their extended style value when creating +// a window. +int GetExtendedStyles(); +int GetExtendedTooltipStyles(); + +// Returns the default text alignment to be used when drawing text on a +// ChromeCanvas based on the directionality of the system locale language. This +// function is used by ChromeCanvas::DrawStringInt when the text alignment is +// not specified. +// +// This function returns either ChromeCanvas::TEXT_ALIGN_LEFT or +// ChromeCanvas::TEXT_ALIGN_RIGHT. +int DefaultCanvasTextAlignment(); + +// Give an HWND, this function sets the WS_EX_LAYOUTRTL extended style for the +// underlying window. When this style is set, the UI for the window is going to +// be mirrored. This is generally done for the UI of right-to-left languages +// such as Hebrew. +void HWNDSetRTLLayout(HWND hwnd); + +// In place sorting of strings using collation rules for |locale|. +void SortStrings(const std::wstring& locale, + std::vector<std::wstring>* strings); + +// Returns a vector of available locale codes. E.g., a vector containing +// en-US, es, fr, fi, pt-PT, pt-BR, etc. +const std::vector<std::wstring>& GetAvailableLocales(); + +} + +#endif // CHROME_COMMON_L10N_UTIL_H__ diff --git a/chrome/common/l10n_util_unittest.cc b/chrome/common/l10n_util_unittest.cc new file mode 100644 index 0000000..18baf3c --- /dev/null +++ b/chrome/common/l10n_util_unittest.cc @@ -0,0 +1,156 @@ +// 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 "chrome/common/chrome_paths.h" +#include "chrome/common/l10n_util.h" +#include "chrome/test/data/resource.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "unicode/locid.h" + +namespace { + +class L10nUtilTest: public testing::Test { +}; + +TEST(L10nUtilTest, GetString) { + std::wstring s = l10n_util::GetString(IDS_SIMPLE); + EXPECT_EQ(std::wstring(L"Hello World!"), s); + + s = l10n_util::GetStringF(IDS_PLACEHOLDERS, L"chrome", L"10"); + EXPECT_EQ(std::wstring(L"Hello, chrome. Your number is 10."), s); + + s = l10n_util::GetStringF(IDS_PLACEHOLDERS_2, 20); + EXPECT_EQ(std::wstring(L"You owe me $20."), s); +} + +TEST(L10nUtilTest, TruncateString) { + std::wstring string(L"foooooey bxxxar baz"); + + // Make sure it doesn't modify the string if length > string length. + EXPECT_EQ(string, l10n_util::TruncateString(string, 100)); + + // Test no characters. + EXPECT_EQ(L"", l10n_util::TruncateString(string, 0)); + + // Test 1 character. + EXPECT_EQ(L"\x2026", l10n_util::TruncateString(string, 1)); + + // Test adds ... at right spot when there is enough room to break at a + // word boundary. + EXPECT_EQ(L"foooooey\x2026", l10n_util::TruncateString(string, 14)); + + // Test adds ... at right spot when there is not enough space in first word. + EXPECT_EQ(L"f\x2026", l10n_util::TruncateString(string, 2)); + + // Test adds ... at right spot when there is not enough room to break at a + // word boundary. + EXPECT_EQ(L"foooooey\x2026", l10n_util::TruncateString(string, 11)); + + // Test completely truncates string if break is on initial whitespace. + EXPECT_EQ(L"\x2026", l10n_util::TruncateString(L" ", 2)); +} + +void SetICUDefaultLocale(const std::wstring& locale_string) { + Locale locale(WideToASCII(locale_string).c_str()); + UErrorCode error_code = U_ZERO_ERROR; + Locale::setDefault(locale, error_code); + EXPECT_TRUE(U_SUCCESS(error_code)); +} + +TEST(L10nUtilTest, GetAppLocale) { + // Use a temporary locale dir so we don't have to actually build the locale + // dlls for this test. + std::wstring orig_locale_dir; + PathService::Get(chrome::DIR_LOCALES, &orig_locale_dir); + std::wstring new_locale_dir; + EXPECT_TRUE(file_util::CreateNewTempDirectory(L"l10n_util_test", + &new_locale_dir)); + PathService::Override(chrome::DIR_LOCALES, new_locale_dir); + // Make fake locale files. + const wchar_t* filenames[] = { + L"en-US.dll", + L"en-GB.dll", + L"fr.dll", + L"es-419.dll", + L"es.dll", + }; + for (size_t i = 0; i < arraysize(filenames); ++i) { + std::wstring filename = new_locale_dir; + file_util::AppendToPath(&filename, filenames[i]); + file_util::WriteFile(filename, "", 0); + } + + // Keep a copy of ICU's default locale before we overwrite it. + Locale locale = Locale::getDefault(); + + SetICUDefaultLocale(L"en-US"); + EXPECT_EQ(L"en-US", l10n_util::GetApplicationLocale(L"")); + + SetICUDefaultLocale(L"en-GB"); + EXPECT_EQ(L"en-GB", l10n_util::GetApplicationLocale(L"")); + + SetICUDefaultLocale(L"fr-CA"); + EXPECT_EQ(L"fr", l10n_util::GetApplicationLocale(L"")); + + SetICUDefaultLocale(L"xx"); + EXPECT_EQ(L"en-US", l10n_util::GetApplicationLocale(L"")); + + SetICUDefaultLocale(L"en-US"); + EXPECT_EQ(L"fr", l10n_util::GetApplicationLocale(L"fr")); + EXPECT_EQ(L"fr", l10n_util::GetApplicationLocale(L"fr-CA")); + // es-419 and es-XX (where XX is not Spain) should be + // mapped to es-419 (Latin American Spanish). + EXPECT_EQ(L"es-419", l10n_util::GetApplicationLocale(L"es-419")); + EXPECT_EQ(L"es", l10n_util::GetApplicationLocale(L"es-ES")); + EXPECT_EQ(L"es-419", l10n_util::GetApplicationLocale(L"es-AR")); + + SetICUDefaultLocale(L"es-MX"); + EXPECT_EQ(L"es-419", l10n_util::GetApplicationLocale(L"")); + + SetICUDefaultLocale(L"es-AR"); + EXPECT_EQ(L"es-419", l10n_util::GetApplicationLocale(L"")); + EXPECT_EQ(L"es", l10n_util::GetApplicationLocale(L"es")); + + SetICUDefaultLocale(L"es-ES"); + EXPECT_EQ(L"es", l10n_util::GetApplicationLocale(L"")); + + SetICUDefaultLocale(L"es"); + EXPECT_EQ(L"es", l10n_util::GetApplicationLocale(L"")); + + // Clean up. + PathService::Override(chrome::DIR_LOCALES, orig_locale_dir); + file_util::Delete(new_locale_dir, true); + UErrorCode error_code = U_ZERO_ERROR; + Locale::setDefault(locale, error_code); +} + +} diff --git a/chrome/common/libxml_utils.cc b/chrome/common/libxml_utils.cc new file mode 100644 index 0000000..96b1665 --- /dev/null +++ b/chrome/common/libxml_utils.cc @@ -0,0 +1,167 @@ +// 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 "chrome/common/libxml_utils.h" +#include "base/logging.h" + +#include "libxml/xmlreader.h" + +std::string XmlStringToStdString(const xmlChar* xmlstring) { + // xmlChar*s are UTF-8, so this cast is safe. + if (xmlstring) + return std::string(reinterpret_cast<const char*>(xmlstring)); + else + return ""; +} + +XmlReader::XmlReader() + : reader_(NULL), +#pragma warning(suppress: 4355) // Okay to pass "this" here. + error_func_(this, &XmlReader::GenericErrorCallback) { +} + +XmlReader::~XmlReader() { + if (reader_) + xmlFreeTextReader(reader_); +} + +/* static */ void XmlReader::GenericErrorCallback(void* context, + const char* msg, ...) { + va_list args; + va_start(args, msg); + char buffer[1 << 9]; + vsnprintf_s(buffer, arraysize(buffer), _TRUNCATE, msg, args); + + XmlReader* reader = static_cast<XmlReader*>(context); + reader->errors_.append(buffer); +} + +bool XmlReader::Load(const std::string& input) { + const int kParseOptions = + XML_PARSE_RECOVER | // recover on errors + XML_PARSE_NONET; // forbid network access + // TODO(evanm): Verify it's OK to pass NULL for the URL and encoding. + // The libxml code allows for these, but it's unclear what effect is has. + reader_ = xmlReaderForMemory(input.data(), static_cast<int>(input.size()), + NULL, NULL, kParseOptions); + return reader_ != NULL; +} + +bool XmlReader::LoadFile(const std::string& file_path) { + + const int kParseOptions = + XML_PARSE_RECOVER | // recover on errors + XML_PARSE_NONET; // forbid network access + reader_ = xmlReaderForFile(file_path.c_str(), NULL, kParseOptions); + return reader_ != NULL; +} + +bool XmlReader::NodeAttribute(const char* name, std::string* out) { + xmlChar* value = xmlTextReaderGetAttribute(reader_, BAD_CAST name); + if (!value) + return false; + *out = XmlStringToStdString(value); + xmlFree(value); + return true; +} + +bool XmlReader::ReadElementContent(std::string* content) { + DCHECK(NodeType() == XML_READER_TYPE_ELEMENT); + const int start_depth = Depth(); + + if (xmlTextReaderIsEmptyElement(reader_)) { + // Empty tag. We succesfully read the content, but it's + // empty. + *content = ""; + // Advance past this empty tag. + if (!Read()) + return false; + return true; + } + + // Advance past opening element tag. + if (!Read()) + return false; + + // Read the content. We read up until we hit a closing tag at the + // same level as our starting point. + while (NodeType() != XML_READER_TYPE_END_ELEMENT || Depth() != start_depth) { + *content += XmlStringToStdString(xmlTextReaderConstValue(reader_)); + if (!Read()) + return false; + } + + // Advance past ending element tag. + DCHECK_EQ(NodeType(), XML_READER_TYPE_END_ELEMENT); + if (!Read()) + return false; + + return true; +} + +bool XmlReader::SkipToElement() { + do { + switch (NodeType()) { + case XML_READER_TYPE_ELEMENT: + return true; + case XML_READER_TYPE_END_ELEMENT: + return false; + default: + // Skip all other node types. + continue; + } + } while (Read()); + return false; +} + + +// XmlWriter functions + +XmlWriter::XmlWriter() : + writer_(NULL) {} + +XmlWriter::~XmlWriter() { + if (writer_) + xmlFreeTextWriter(writer_); + if (buffer_) + xmlBufferFree(buffer_); +} + +void XmlWriter::StartWriting() { + buffer_ = xmlBufferCreate(); + writer_ = xmlNewTextWriterMemory(buffer_, 0); + xmlTextWriterSetIndent(writer_, 1); + xmlTextWriterStartDocument(writer_, NULL, NULL, NULL); +} + +void XmlWriter::StopWriting() { + xmlTextWriterEndDocument(writer_); + xmlFreeTextWriter(writer_); + writer_ = NULL; +} diff --git a/chrome/common/libxml_utils.h b/chrome/common/libxml_utils.h new file mode 100644 index 0000000..94ae269 --- /dev/null +++ b/chrome/common/libxml_utils.h @@ -0,0 +1,208 @@ +// 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 CHROME_COMMON_LIBXML_UTILS_H__ +#define CHROME_COMMON_LIBXML_UTILS_H__ + +#include <string> + +#include "libxml/xmlreader.h" +#include "libxml/xmlwriter.h" +#include "base/scoped_ptr.h" + +// Converts a libxml xmlChar* into a UTF-8 std::string. +// NULL inputs produce an empty string. +std::string XmlStringToStdString(const xmlChar* xmlstring); + +// libxml uses a global error function pointer for reporting errors. +// A ScopedXmlErrorFunc object lets you change the global error pointer +// for the duration of the object's lifetime. +class ScopedXmlErrorFunc { + public: + ScopedXmlErrorFunc(void* context, xmlGenericErrorFunc func) { + old_error_func_ = xmlGenericError; + old_error_context_ = xmlGenericErrorContext; + xmlSetGenericErrorFunc(context, func); + } + ~ScopedXmlErrorFunc() { + xmlSetGenericErrorFunc(old_error_context_, old_error_func_); + } + + private: + xmlGenericErrorFunc old_error_func_; + void* old_error_context_; +}; + +// XmlReader is a wrapper class around libxml's xmlReader, +// providing a simplified C++ API. +class XmlReader { + public: + XmlReader(); + ~XmlReader(); + + // Load a document into the reader from memory. |input| must be UTF-8 and + // exist for the lifetime of this object. Returns false on error. + // TODO(evanm): handle encodings other than UTF-8? + bool Load(const std::string& input); + + // Load a document into the reader from a file. Returns false on error. + bool LoadFile(const std::string& file_path); + + // Wrappers around libxml functions ----------------------------------------- + + // Read() advances to the next node. Returns false on EOF or error. + bool Read() { return xmlTextReaderRead(reader_) == 1; } + + // Next(), when pointing at an opening tag, advances to the node after + // the matching closing tag. Returns false on EOF or error. + bool Next() { return xmlTextReaderNext(reader_) == 1; } + + // Return the depth in the tree of the current node. + int Depth() { return xmlTextReaderDepth(reader_); } + + // Returns the "local" name of the current node. + // For a tag like <foo:bar>, this is the string "foo:bar". + std::string NodeName() { + return XmlStringToStdString(xmlTextReaderConstLocalName(reader_)); + } + + // When pointing at a tag, retrieves the value of an attribute. + // Returns false on failure. + // E.g. for <foo bar:baz="a">, NodeAttribute("bar:baz", &value) + // returns true and |value| is set to "a". + bool NodeAttribute(const char* name, std::string* value); + + // Helper functions not provided by libxml ---------------------------------- + + // Return the string content within an element. + // "<foo>bar</foo>" is a sequence of three nodes: + // (1) open tag, (2) text, (3) close tag. + // With the reader currently at (1), this returns the text of (2), + // and advances past (3). + // Returns false on error. + bool ReadElementContent(std::string* content); + + // Skip to the next opening tag, returning false if we reach a closing + // tag or EOF first. + // If currently on an opening tag, doesn't advance at all. + bool SkipToElement(); + + // Returns the errors reported by libxml, if any. + // (libxml normally just dumps these errors to stderr.) + const std::string& errors() const { return errors_; } + + private: + // A callback for libxml to report errors. + static void GenericErrorCallback(void* context, const char* msg, ...); + + // Returns the libxml node type of the current node. + int NodeType() { return xmlTextReaderNodeType(reader_); } + + // The underlying libxml xmlTextReader. + xmlTextReaderPtr reader_; + + // error_func_ is used to reassign libxml's global error function + // to report errors into |errors_| for the lifetime of this object. + ScopedXmlErrorFunc error_func_; + std::string errors_; +}; + +// XmlWriter is a wrapper class around libxml's xmlWriter, +// providing a simplified C++ API. +// StartWriting must be called before other methods, and StopWriting +// must be called before GetWrittenString() will return results. +class XmlWriter { + public: + XmlWriter(); + ~XmlWriter(); + + // Allocates the xmlTextWriter and an xmlBuffer and starts an XML document. + // This must be called before any other functions. By default, indenting is + // set to true. + void StartWriting(); + + // Ends the XML document and frees the xmlTextWriter. + // This must be called before GetWrittenString() is called. + void StopWriting(); + // Wrappers around libxml functions ----------------------------------------- + + // All following elements will be indented to match their depth. + void StartIndenting() { xmlTextWriterSetIndent(writer_, 1); } + + // All follow elements will not be indented. + void StopIndenting() { xmlTextWriterSetIndent(writer_, 0); } + + // Start an element with the given name. All future elements added will be + // children of this element, until it is ended. Returns false on error. + bool StartElement(const std::string& element_name) { + return xmlTextWriterStartElement(writer_, + BAD_CAST element_name.c_str()) >= 0; + } + + // Ends the current open element. Returns false on error. + bool EndElement() { + return xmlTextWriterEndElement(writer_) >= 0; + } + + // Adds an attribute to the current open element. Returns false on error. + bool AddAttribute(const std::string& attribute_name, + const std::string& attribute_value) { + return xmlTextWriterWriteAttribute(writer_, + BAD_CAST attribute_name.c_str(), + BAD_CAST attribute_value.c_str()) >= 0; + } + + // Adds a new element with name |element_name| and content |content| + // to the buffer. Example: <|element_name|>|content|</|element_name|> + // Returns false on errors. + bool WriteElement(const std::string& element_name, + const std::string& content) { + return xmlTextWriterWriteElement(writer_, + BAD_CAST element_name.c_str(), + BAD_CAST content.c_str()) >= 0; + } + + // Helper functions not provided by xmlTextWriter ---------------------------------- + + // Returns the string that has been written to the buffer. + std::string GetWrittenString() { + if (buffer_ == NULL) + return ""; + return XmlStringToStdString(buffer_->content); + } + + private: + // The underlying libxml xmlTextWriter. + xmlTextWriterPtr writer_; + + // Stores the output. + xmlBufferPtr buffer_; +}; + +#endif // CHROME_COMMON_LIBXML_UTILS_H__ diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc new file mode 100644 index 0000000..fbcd26d --- /dev/null +++ b/chrome/common/logging_chrome.cc @@ -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. + +#include <windows.h> + +#include <iostream> +#include <fstream> + +#include "chrome/common/logging_chrome.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/env_util.h" +#include "chrome/common/env_vars.h" + +// When true, this means that error dialogs should not be shown. +static bool dialogs_are_suppressed_ = false; + +// This should be true for exactly the period between the end of +// InitChromeLogging() and the beginning of CleanupChromeLogging(). +static bool chrome_logging_initialized_ = false; + +// Assertion handler for logging errors that occur when dialogs are +// silenced. To record a new error, pass the log string associated +// with that error in the str parameter. +#pragma optimize("", off) +static void SilentRuntimeAssertHandler(const std::string& str) { + __debugbreak(); +} +#pragma optimize("", on) + +// Suppresses error/assertion dialogs and enables the logging of +// those errors into silenced_errors_. +static void SuppressDialogs() { + if (dialogs_are_suppressed_) + return; + + logging::SetLogAssertHandler(SilentRuntimeAssertHandler); + + UINT new_flags = SEM_FAILCRITICALERRORS | + SEM_NOGPFAULTERRORBOX | + SEM_NOOPENFILEERRORBOX; + + // Preserve existing error mode, as discussed at http://t/dmea + UINT existing_flags = SetErrorMode(new_flags); + SetErrorMode(existing_flags | new_flags); + + dialogs_are_suppressed_ = true; +} + +namespace logging { + +void InitChromeLogging(const CommandLine& command_line, + OldFileDeletionState delete_old_log_file) { + DCHECK(!chrome_logging_initialized_) << + "Attempted to initialize logging when it was already initialized."; + + // only use OutputDebugString in debug mode +#ifdef NDEBUG + bool enable_logging = false; + const wchar_t *kInvertLoggingSwitch = switches::kEnableLogging; + const logging::LoggingDestination kDefaultLoggingMode = + logging::LOG_ONLY_TO_FILE; +#else + bool enable_logging = true; + const wchar_t *kInvertLoggingSwitch = switches::kDisableLogging; + const logging::LoggingDestination kDefaultLoggingMode = + logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG; +#endif + + if (command_line.HasSwitch(kInvertLoggingSwitch)) + enable_logging = !enable_logging; + + logging::LoggingDestination log_mode; + if (enable_logging) { + log_mode = kDefaultLoggingMode; + } else { + log_mode = logging::LOG_NONE; + } + + logging::InitLogging(GetLogFileName().c_str(), + log_mode, + logging::LOCK_LOG_FILE, + delete_old_log_file); + + // we want process and thread IDs because we have a lot of things running + logging::SetLogItems(true, true, false, true); + + // We call running in unattended mode "headless", and allow + // headless mode to be configured either by the Environment + // Variable or by the Command Line Switch. This is for + // automated test purposes. + if (env_util::HasEnvironmentVariable(env_vars::kHeadless) || + command_line.HasSwitch(switches::kNoErrorDialogs)) + SuppressDialogs(); + + std::wstring log_filter_prefix = + command_line.GetSwitchValue(switches::kLogFilterPrefix); + logging::SetLogFilterPrefix(WideToUTF8(log_filter_prefix).c_str()); + + chrome_logging_initialized_ = true; +} + +// This is a no-op, but we'll keep it around in case +// we need to do more cleanup in the future. +void CleanupChromeLogging() { + DCHECK(chrome_logging_initialized_) << + "Attempted to clean up logging when it wasn't initialized."; + + CloseLogFile(); + + chrome_logging_initialized_ = false; +} + +std::wstring GetLogFileName() { + wchar_t filename[MAX_PATH]; + unsigned status = GetEnvironmentVariable(env_vars::kLogFileName, + filename, MAX_PATH); + if (status && (status <= MAX_PATH)) + return std::wstring(filename); + + const std::wstring log_filename(L"chrome_debug.log"); + std::wstring log_path; + + if (PathService::Get(chrome::DIR_LOGS, &log_path)) { + file_util::AppendToPath(&log_path, log_filename); + return log_path; + } else { + // error with path service, just use some default file somewhere + return log_filename; + } +} + +bool DialogsAreSuppressed() { + return dialogs_are_suppressed_; +} + +size_t GetFatalAssertions(AssertionList* assertions) { + // In this function, we don't assume that assertions is non-null, so + // that if you just want an assertion count, you can pass in NULL. + if (assertions) + assertions->clear(); + size_t assertion_count = 0; + + std::ifstream log_file; + log_file.open(GetLogFileName().c_str()); + if (!log_file.is_open()) + return 0; + + std::string utf8_line; + std::wstring wide_line; + while(!log_file.eof()) { + getline(log_file, utf8_line); + if (utf8_line.find(":FATAL:") != std::string::npos) { + wide_line = UTF8ToWide(utf8_line); + if (assertions) + assertions->push_back(wide_line); + ++assertion_count; + } + } + log_file.close(); + + return assertion_count; +} + +} // namespace logging diff --git a/chrome/common/logging_chrome.h b/chrome/common/logging_chrome.h new file mode 100644 index 0000000..1caacdc --- /dev/null +++ b/chrome/common/logging_chrome.h @@ -0,0 +1,82 @@ +// 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 CHROME_COMMON_LOGGING_CHROME_H__ +#define CHROME_COMMON_LOGGING_CHROME_H__ + +#include <string> +#include <vector> + +#include "base/logging.h" + +class CommandLine; + +namespace logging { + +// Call to initialize logging for Chrome. This sets up the chrome-specific +// logfile naming scheme and might do other things like log modules and +// setting levels in the future. +// +// The main process might want to delete any old log files on startup by +// setting delete_old_log_file, but the renderer processes should not, or +// they will delete each others' logs. +// +// XXX +// Setting suppress_error_dialogs to true disables any dialogs that would +// normally appear for assertions and crashes, and makes any catchable +// errors (namely assertions) available via GetSilencedErrorCount() +// and GetSilencedError(). +void InitChromeLogging(const CommandLine& command_line, + OldFileDeletionState delete_old_log_file); + +// Call when done using logging for Chrome. +void CleanupChromeLogging(); + +// Returns the fully-qualified name of the log file. +std::wstring GetLogFileName(); + +// Returns true when error/assertion dialogs are to be shown, +// false otherwise. +bool DialogsAreSuppressed(); + +typedef std::vector<std::wstring> AssertionList; + +// Gets the list of fatal assertions in the current log file, and +// returns the number of fatal assertions. (If you don't care +// about the actual list of assertions, you can pass in NULL.) +// NOTE: Since this reads the log file to determine the assertions, +// this operation is O(n) over the length of the log. +// NOTE: This can fail if the file is locked for writing. However, +// this is unlikely as this function is most useful after +// the program writing the log has terminated. +size_t GetFatalAssertions(AssertionList* assertions); + +} // namespace logging + +#endif diff --git a/chrome/common/logging_chrome_uitest.cc b/chrome/common/logging_chrome_uitest.cc new file mode 100644 index 0000000..5e3e873 --- /dev/null +++ b/chrome/common/logging_chrome_uitest.cc @@ -0,0 +1,162 @@ +// 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 <windows.h> + +#include "base/command_line.h" +#include "base/basictypes.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/env_vars.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/test/ui/ui_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + class ChromeLoggingTest : public testing::Test { + public: + // Stores the current value of the log file name environment + // variable and sets the variable to new_value. + void SaveEnvironmentVariable(std::wstring new_value) { + unsigned status = GetEnvironmentVariable(env_vars::kLogFileName, + environment_filename_, + MAX_PATH); + if (!status) { + wcscpy_s(environment_filename_, L""); + } + + SetEnvironmentVariable(env_vars::kLogFileName, new_value.c_str()); + } + + // Restores the value of the log file nave environment variable + // previously saved by SaveEnvironmentVariable(). + void RestoreEnvironmentVariable() { + SetEnvironmentVariable(env_vars::kLogFileName, environment_filename_); + } + + private: + wchar_t environment_filename_[MAX_PATH]; // Saves real environment value. + }; +}; + +// Tests the log file name getter without an environment variable. +TEST_F(ChromeLoggingTest, LogFileName) { + SaveEnvironmentVariable(std::wstring()); + + std::wstring filename = logging::GetLogFileName(); + ASSERT_NE(std::wstring::npos, filename.find(L"chrome_debug.log")); + + RestoreEnvironmentVariable(); +} + +// Tests the log file name getter with an environment variable. +TEST_F(ChromeLoggingTest, EnvironmentLogFileName) { + SaveEnvironmentVariable(std::wstring(L"test value")); + + std::wstring filename = logging::GetLogFileName(); + ASSERT_EQ(std::wstring(L"test value"), filename); + + RestoreEnvironmentVariable(); +} + +#ifndef NDEBUG // We don't have assertions in release builds. +// Tests whether we correctly fail on browser assertions during tests. +class AssertionTest : public UITest { + protected: + AssertionTest() : UITest() + { + // Initial loads will never complete due to assertion. + wait_for_initial_loads_ = false; + + // We're testing the renderer rather than the browser assertion here, + // because the browser assertion would flunk the test during SetUp() + // (since TAU wouldn't be able to find the browser window). + CommandLine::AppendSwitch(&launch_arguments_, + switches::kRendererAssertTest); + } +}; + +// Launch the app in assertion test mode, then close the app. +TEST_F(AssertionTest, Assertion) { + if (UITest::in_process_renderer()) { + // in process mode doesn't do the crashing. + expected_errors_ = 0; + expected_crashes_ = 0; + } else { + expected_errors_ = 1; + expected_crashes_ = 1; + } +} +#endif // NDEBUG + +// Tests whether we correctly fail on browser crashes during UI Tests. +class RendererCrashTest : public UITest { + protected: + RendererCrashTest() : UITest() + { + // Initial loads will never complete due to crash. + wait_for_initial_loads_ = false; + + CommandLine::AppendSwitch(&launch_arguments_, + switches::kRendererCrashTest); + } +}; + +// Launch the app in renderer crash test mode, then close the app. +TEST_F(RendererCrashTest, Crash) { + if (UITest::in_process_renderer()) { + // in process mode doesn't do the crashing. + expected_crashes_ = 0; + } else { + // Wait while the process is writing the crash dump. + Sleep(5000); + expected_crashes_ = 1; + } +} + +// Tests whether we correctly fail on browser crashes during UI Tests. +class BrowserCrashTest : public UITest { + protected: + BrowserCrashTest() : UITest() + { + // Initial loads will never complete due to crash. + wait_for_initial_loads_ = false; + + CommandLine::AppendSwitch(&launch_arguments_, + switches::kBrowserCrashTest); + } +}; + +// Launch the app in browser crash test mode. +// This test is disabled. See bug 1198934. +TEST_F(BrowserCrashTest, DISABLED_Crash) { + // Wait while the process is writing the crash dump. + Sleep(5000); + expected_crashes_ = 1; +} diff --git a/chrome/common/message_router.cc b/chrome/common/message_router.cc new file mode 100644 index 0000000..dbfd63c --- /dev/null +++ b/chrome/common/message_router.cc @@ -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. + +#include "chrome/common/message_router.h" +#include "chrome/common/render_messages.h" + +void MessageRouter::OnControlMessageReceived(const IPC::Message& msg) { + NOTREACHED() << "should override in subclass if you care about control messages"; +} + +bool MessageRouter::Send(IPC::Message* msg) { + NOTREACHED() << "should override in subclass if you care about sending messages"; + return false; +} + +void MessageRouter::AddRoute(int32 routing_id, + IPC::Channel::Listener* listener) { + routes_.AddWithID(listener, routing_id); +} + +void MessageRouter::RemoveRoute(int32 routing_id) { + routes_.Remove(routing_id); +} + +void MessageRouter::OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + OnControlMessageReceived(msg); + } else { + RouteMessage(msg); + } +} + +bool MessageRouter::RouteMessage(const IPC::Message& msg) { + IPC::Channel::Listener* listener = routes_.Lookup(msg.routing_id()); + if (!listener) + return false; + + listener->OnMessageReceived(msg); + return true; +} diff --git a/chrome/common/message_router.h b/chrome/common/message_router.h new file mode 100644 index 0000000..a5fabf4 --- /dev/null +++ b/chrome/common/message_router.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 CHROME_COMMON_MESSAGE_ROUTER_H__ +#define CHROME_COMMON_MESSAGE_ROUTER_H__ + +#include "chrome/common/ipc_channel.h" + +// The MessageRouter handles all incoming messages sent to it by routing them +// to the correct listener. Routing is based on the Message's routing ID. +// Since routing IDs are typically assigned asynchronously by the browser +// process, the MessageRouter has the notion of pending IDs for listeners that +// have not yet been assigned a routing ID. +// +// When a message arrives, the routing ID is used to index the set of routes to +// find a listener. If a listener is found, then the message is passed to it. +// Otherwise, the message is ignored if its routing ID is not equal to +// MSG_ROUTING_CONTROL. +// +// The MessageRouter supports the IPC::Message::Sender interface for outgoing +// messages, but does not define a meaningful implementation of it. The +// subclass of MessageRouter is intended to provide that if appropriate. +// +// The MessageRouter can be used as a concrete class provided its Send method +// is not called and it does not receive any control messages. + +class MessageRouter : public IPC::Channel::Listener, + public IPC::Message::Sender { + public: + MessageRouter() {} + virtual ~MessageRouter() {} + + // Implemented by subclasses to handle control messages + virtual void OnControlMessageReceived(const IPC::Message& msg); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + + // Like OnMessageReceived, except it only handles routed messages. Returns + // true if the message was dispatched, or false if there was no listener for + // that route id. + virtual bool RouteMessage(const IPC::Message& msg); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + // Called to add/remove a listener for a particular message routing ID. + void AddRoute(int32 routing_id, IPC::Channel::Listener* listener); + void RemoveRoute(int32 routing_id); + + private: + // A list of all listeners with assigned routing IDs. + IDMap<IPC::Channel::Listener> routes_; + + DISALLOW_EVIL_CONSTRUCTORS(MessageRouter); +}; + +#endif // CHROME_COMMON_MESSAGE_ROUTER_H__ diff --git a/chrome/common/mru_cache.h b/chrome/common/mru_cache.h new file mode 100644 index 0000000..93f8059 --- /dev/null +++ b/chrome/common/mru_cache.h @@ -0,0 +1,256 @@ +// 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 a template for a Most Recently Used cache that allows +// constant-time access to items using a key, but easy identification of the +// least-recently-used items for removal. Each key can only be associated with +// one payload item at a time. +// +// The key object will be stored twice, so it should support efficient copying. +// +// NOTE: While all operations are O(1), this code is written for +// legibility rather than optimality. If future profiling identifies this as +// a bottleneck, there is room for smaller values of 1 in the O(1). :] + +#ifndef CHROME_COMMON_MRU_CACHE_H__ +#define CHROME_COMMON_MRU_CACHE_H__ + +#include <list> +#include <map> +#include <utility> + +#include "base/basictypes.h" +#include "chrome/common/logging_chrome.h" + +// MRUCacheBase ---------------------------------------------------------------- + +// Base class for the MRU cache specializations defined below. +// The deletor will get called on all payloads that are being removed or +// replaced. +template <class KeyType, class PayloadType, class DeletorType> +class MRUCacheBase { + public: + // The payload of the list. This maintains a copy of the key so we can + // efficiently delete things given an element of the list. + typedef std::pair<KeyType, PayloadType> value_type; + + private: + typedef std::list<value_type> PayloadList; + typedef std::map<KeyType, typename PayloadList::iterator> KeyIndex; + + public: + typedef typename PayloadList::size_type size_type; + + typedef typename PayloadList::iterator iterator; + typedef typename PayloadList::const_iterator const_iterator; + typedef typename PayloadList::reverse_iterator reverse_iterator; + typedef typename PayloadList::const_reverse_iterator const_reverse_iterator; + + enum { NO_AUTO_EVICT = 0 }; + + // The max_size is the size at which the cache will prune its members to when + // a new item is inserted. If the caller wants to manager this itself (for + // example, maybe it has special work to do when something is evicted), it + // can pass NO_AUTO_EVICT to not restrict the cache size. + MRUCacheBase(size_type max_size) : max_size_(max_size) { + } + + virtual ~MRUCacheBase() { + iterator i = begin(); + while (i != end()) + i = Erase(i); + } + + // Inserts a payload item with the given key. If an existing item has + // the same key, it is removed prior to insertion. An iterator indicating the + // inserted item will be returned (this will always be the front of the list). + // + // The payload will be copied. In the case of an OwningMRUCache, this function + // will take ownership of the pointer. + iterator Put(const KeyType& key, const PayloadType& payload) { + // Remove any existing payload with that key. + KeyIndex::iterator index_iter = index_.find(key); + if (index_iter != index_.end()) { + // Erase the reference to it. This will call the deletor on the removed + // element. The index reference will be replaced in the code below. + Erase(index_iter->second); + } else if (max_size_ != NO_AUTO_EVICT) { + // New item is being inserted which might make it larger than the maximum + // size: kick the oldest thing out if necessary. + ShrinkToSize(max_size_ - 1); + } + + ordering_.push_front(value_type(key, payload)); + index_[key] = ordering_.begin(); + return ordering_.begin(); + } + + // Retrieves the contents of the given key, or end() if not found. This method + // has the side effect of moving the requested item to the front of the + // recency list. + // + // TODO(brettw) We may want a const version of this function in the future. + iterator Get(const KeyType& key) { + KeyIndex::iterator index_iter = index_.find(key); + if (index_iter == index_.end()) + return end(); + PayloadList::iterator iter = index_iter->second; + + // Move the touched item to the front of the recency ordering. + ordering_.splice(ordering_.begin(), ordering_, iter); + return ordering_.begin(); + } + + // Retrieves the payload associated with a given key and returns it via + // result without affecting the ordering (unlike Get). + // + // TODO(brettw) We may want a const version of this function in the future. + iterator Peek(const KeyType& key) { + KeyIndex::const_iterator index_iter = index_.find(key); + if (index_iter == index_.end()) + return end(); + return index_iter->second; + } + + // Erases the item referenced by the given iterator. An iterator to the item + // following it will be returned. The iterator must be valid. + iterator Erase(iterator pos) { + deletor_(pos->second); + index_.erase(pos->first); + return ordering_.erase(pos); + } + + // MRUCache entries are often processed in reverse order, so we add this + // convenience function (not typically defined by STL containers). + reverse_iterator Erase(reverse_iterator pos) { + // We have to actually give it the incremented iterator to delete, since + // the forward iterator that base() returns is actually one past the item + // being iterated over. + return reverse_iterator(Erase((++pos).base())); + } + + // Shrinks the cache so it only holds |new_size| items. If |new_size| is + // bigger or equal to the current number of items, this will do nothing. + void ShrinkToSize(size_type new_size) { + for (size_type i = size(); i > new_size; i--) + Erase(rbegin()); + } + + // Returns the number of elements in the cache. + size_type size() const { + // We don't use ordering_.size() for the return value because + // (as a linked list) it can be O(n). + DCHECK(index_.size() == ordering_.size()); + return index_.size(); + } + + // Allows iteration over the list. Forward iteration starts with the most + // recent item and works backwards. + // + // Note that since these iterators are actually iterators over a list, you + // can keep them as you insert or delete things (as long as you don't delete + // the one you are pointing to) and they will still be valid. + iterator begin() { return ordering_.begin(); } + const_iterator begin() const { ordering_.begin(); } + iterator end() { return ordering_.end(); } + const_iterator end() const { return ordering_.end(); } + + reverse_iterator rbegin() { return ordering_.rbegin(); } + const_reverse_iterator rbegin() const { ordering_.rbegin(); } + reverse_iterator rend() { return ordering_.rend(); } + const_reverse_iterator rend() const { return ordering_.rend(); } + + private: + PayloadList ordering_; + KeyIndex index_; + + size_type max_size_; + + DeletorType deletor_; + + DISALLOW_EVIL_CONSTRUCTORS(MRUCacheBase); +}; + +// MRUCache -------------------------------------------------------------------- + +// A functor that does nothing. Used by the MRUCache. +template<class PayloadType> +class MRUCacheNullDeletor { + public: + void operator()(PayloadType& payload) { + } +}; + +// A container that does not do anything to free its data. Use this when storing +// value types (as opposed to pointers) in the list. +template <class KeyType, class PayloadType> +class MRUCache : public MRUCacheBase<KeyType, + PayloadType, + MRUCacheNullDeletor<PayloadType> > { + public: + // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. + MRUCache(size_type max_size) : MRUCacheBase(max_size) { + } + virtual ~MRUCache() { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(MRUCache); +}; + +// OwningMRUCache -------------------------------------------------------------- + +template<class PayloadType> +class MRUCachePointerDeletor { + public: + void operator()(PayloadType& payload) { + delete payload; + } +}; + +// A cache that owns the payload type, which must be a non-const pointer type. +// The pointers will be deleted when they are removed, replaced, or when the +// cache is destroyed. +template <class KeyType, class PayloadType> +class OwningMRUCache + : public MRUCacheBase<KeyType, + PayloadType, + MRUCachePointerDeletor<PayloadType> > { + public: + // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. + OwningMRUCache(size_type max_size) : MRUCacheBase(max_size) { + } + virtual ~OwningMRUCache() { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(OwningMRUCache); +}; + +#endif // CHROME_COMMON_MRU_CACHE_H__ diff --git a/chrome/common/mru_cache_unittest.cc b/chrome/common/mru_cache_unittest.cc new file mode 100644 index 0000000..1fbf111 --- /dev/null +++ b/chrome/common/mru_cache_unittest.cc @@ -0,0 +1,261 @@ +// 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 "chrome/common/mru_cache.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +int cached_item_live_count = 0; + +struct CachedItem { + CachedItem() : value(0) { + cached_item_live_count++; + } + + CachedItem(int new_value) : value(new_value) { + cached_item_live_count++; + } + + CachedItem(const CachedItem& other) : value(other.value) { + cached_item_live_count++; + } + + ~CachedItem() { + cached_item_live_count--; + } + + int value; +}; + +} // namespace + +TEST(MRUCacheTest, Basic) { + typedef MRUCache<int, CachedItem> Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + // Check failure conditions + { + CachedItem test_item; + EXPECT_TRUE(cache.Get(0) == cache.end()); + EXPECT_TRUE(cache.Peek(0) == cache.end()); + } + + static const int kItem1Key = 5; + CachedItem item1(10); + Cache::iterator inserted_item = cache.Put(kItem1Key, item1); + EXPECT_EQ(1, cache.size()); + + // Check that item1 was properly inserted. + { + Cache::iterator found = cache.Get(kItem1Key); + EXPECT_TRUE(inserted_item == cache.begin()); + EXPECT_TRUE(found != cache.end()); + + found = cache.Peek(kItem1Key); + EXPECT_TRUE(found != cache.end()); + + EXPECT_EQ(kItem1Key, found->first); + EXPECT_EQ(item1.value, found->second.value); + } + + static const int kItem2Key = 7; + CachedItem item2(12); + cache.Put(kItem2Key, item2); + EXPECT_EQ(2, cache.size()); + + // Check that item1 is the oldest since item2 was added afterwards. + { + Cache::reverse_iterator oldest = cache.rbegin(); + ASSERT_TRUE(oldest != cache.rend()); + EXPECT_EQ(kItem1Key, oldest->first); + EXPECT_EQ(item1.value, oldest->second.value); + } + + // Check that item1 is still accessible by key. + { + Cache::iterator test_item = cache.Get(kItem1Key); + ASSERT_TRUE(test_item != cache.end()); + EXPECT_EQ(kItem1Key, test_item->first); + EXPECT_EQ(item1.value, test_item->second.value); + } + + // Check that retrieving item1 pushed item2 to oldest. + { + Cache::reverse_iterator oldest = cache.rbegin(); + ASSERT_TRUE(oldest != cache.rend()); + EXPECT_EQ(kItem2Key, oldest->first); + EXPECT_EQ(item2.value, oldest->second.value); + } + + // Remove the oldest item and check that item1 is now the only member. + { + Cache::reverse_iterator next = cache.Erase(cache.rbegin()); + + EXPECT_EQ(1, cache.size()); + + EXPECT_TRUE(next == cache.rbegin()); + EXPECT_EQ(kItem1Key, next->first); + EXPECT_EQ(item1.value, next->second.value); + + cache.Erase(cache.begin()); + EXPECT_EQ(0, cache.size()); + } +} + +TEST(MRUCacheTest, GetVsPeek) { + typedef MRUCache<int, CachedItem> Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + static const int kItem1Key = 1; + CachedItem item1(10); + cache.Put(kItem1Key, item1); + + static const int kItem2Key = 2; + CachedItem item2(20); + cache.Put(kItem2Key, item2); + + // This should do nothing since the size is bigger than the number of items. + cache.ShrinkToSize(100); + + // Check that item1 starts out as oldest + { + Cache::reverse_iterator iter = cache.rbegin(); + ASSERT_TRUE(iter != cache.rend()); + EXPECT_EQ(kItem1Key, iter->first); + EXPECT_EQ(item1.value, iter->second.value); + } + + // Check that Peek doesn't change ordering + { + Cache::iterator peekiter = cache.Peek(kItem1Key); + ASSERT_TRUE(peekiter != cache.end()); + + Cache::reverse_iterator iter = cache.rbegin(); + ASSERT_TRUE(iter != cache.rend()); + EXPECT_EQ(kItem1Key, iter->first); + EXPECT_EQ(item1.value, iter->second.value); + } +} + +TEST(MRUCacheTest, KeyReplacement) { + typedef MRUCache<int, CachedItem> Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + static const int kItem1Key = 1; + CachedItem item1(10); + cache.Put(kItem1Key, item1); + + static const int kItem2Key = 2; + CachedItem item2(20); + cache.Put(kItem2Key, item2); + + static const int kItem3Key = 3; + CachedItem item3(30); + cache.Put(kItem3Key, item3); + + static const int kItem4Key = 4; + CachedItem item4(40); + cache.Put(kItem4Key, item4); + + CachedItem item5(50); + cache.Put(kItem3Key, item5); + + EXPECT_EQ(4, cache.size()); + for (int i = 0; i < 3; ++i) { + Cache::reverse_iterator iter = cache.rbegin(); + ASSERT_TRUE(iter != cache.rend()); + } + + // Make it so only the most important element is there. + cache.ShrinkToSize(1); + + Cache::iterator iter = cache.begin(); + EXPECT_EQ(kItem3Key, iter->first); + EXPECT_EQ(item5.value, iter->second.value); +} + +// Make sure that the owning version release its pointers properly. +TEST(MRUCacheTest, Owning) { + typedef OwningMRUCache<int, CachedItem*> Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + int initial_count = cached_item_live_count; + + // First insert and item and then overwrite it. + static const int kItem1Key = 1; + cache.Put(kItem1Key, new CachedItem(20)); + cache.Put(kItem1Key, new CachedItem(22)); + + // There should still be one item, and one extra live item. + Cache::iterator iter = cache.Get(kItem1Key); + EXPECT_EQ(1, cache.size()); + EXPECT_TRUE(iter != cache.end()); + EXPECT_EQ(initial_count + 1, cached_item_live_count); + + // Now remove it. + cache.Erase(cache.begin()); + EXPECT_EQ(initial_count, cached_item_live_count); + + // Now try another cache that goes out of scope to make sure its pointers + // go away. + { + Cache cache2(Cache::NO_AUTO_EVICT); + cache2.Put(1, new CachedItem(20)); + cache2.Put(2, new CachedItem(20)); + } + + // There should be no objects leaked. + EXPECT_EQ(initial_count, cached_item_live_count); +} + +TEST(MRUCacheTest, AutoEvict) { + typedef OwningMRUCache<int, CachedItem*> Cache; + static const Cache::size_type kMaxSize = 3; + + int initial_count = cached_item_live_count; + + { + Cache cache(kMaxSize); + + static const int kItem1Key = 1, kItem2Key = 2, kItem3Key = 3, kItem4Key = 4; + cache.Put(kItem1Key, new CachedItem(20)); + cache.Put(kItem2Key, new CachedItem(21)); + cache.Put(kItem3Key, new CachedItem(22)); + cache.Put(kItem4Key, new CachedItem(23)); + + // The cache should only have kMaxSize items in it even though we inserted + // more. + EXPECT_EQ(kMaxSize, cache.size()); + } + + // There should be no objects leaked. + EXPECT_EQ(initial_count, cached_item_live_count); +}
\ No newline at end of file diff --git a/chrome/common/navigation_types.h b/chrome/common/navigation_types.h new file mode 100644 index 0000000..8e1ef44 --- /dev/null +++ b/chrome/common/navigation_types.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 CHROME_COMMON_NAVIGATION_TYPES_H__ +#define CHROME_COMMON_NAVIGATION_TYPES_H__ + +// An enum for the type of navigation. This is used when calling +// the NotifyDidNavigate method on the active TabContents +enum NavigationType { + // This is a new navigation resulting in a new entry in the session history + NAVIGATION_NEW = 0, + // Back or forward navigation within the session history + NAVIGATION_BACK_FORWARD = 1, + // This navigation simply replaces the URL of an existing entry in the + // seesion history + NAVIGATION_REPLACE = 2, +}; + +#endif // CHROME_COMMON_NAVIGATION_TYPES_H__ diff --git a/chrome/common/net/cache_uitest.cc b/chrome/common/net/cache_uitest.cc new file mode 100644 index 0000000..36ad388 --- /dev/null +++ b/chrome/common/net/cache_uitest.cc @@ -0,0 +1,201 @@ +// 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 "base/string_util.h" +#include "chrome/test/ui/ui_test.h" +#include "chrome/test/automation/automation_proxy.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "net/url_request/url_request_unittest.h" + +// The CacheTest class extends the UITest class and provides functions to +// get a new tab and to run the tests on a particular path +// +// Typical usage: +// +// 1. Provide this class as the TestCase for TEST_F macro +// 2. Then run the cache test on a specific path using the function +// RunCacheTest +// +// For example: +// +// TEST_F(CacheTest, NoCacheMaxAge) { +// RunCacheTest(L"nocachetime/maxage", false, false); +// } +// +// Note that delays used in running the test is initialized to defaults +class CacheTest : public UITest { + protected: + + // Runs the cache test for the specified path. + // Can specify the test to check if a new tab is loaded from the cache + // and also if a delayed reload is required. A true value passed to the + // third parameter causes a delayed reload of the path in a new tab. + // The amount of delay is set by a class constant. + void RunCacheTest(const std::wstring &url, + bool expect_new_tab_cached, + bool expect_delayed_reload); + + private: + // Class constants + static const int kWaitForCacheUpdateMsec = 2000; + static const int kCacheWaitMultiplier = 4; // Used to increase delay + + // Appends a new tab to the test chrome window and loads the specified + // URL. The new tab will try to get the URL from the cache before requesting + // the server for it. + void GetNewTab(AutomationProxy* automationProxy, const GURL& tab_url); +}; + +// Runs the cache test for the specified path. +void CacheTest::RunCacheTest(const std::wstring &url, + bool expect_new_tab_cached, + bool expect_delayed_reload) { + TestServer server(L"chrome/test/data"); + GURL test_page(server.TestServerPageW(url)); + + NavigateToURL(test_page); + std::wstring original_time = GetActiveTabTitle(); + + Sleep(kWaitForCacheUpdateMsec); + + GetNewTab(automation(), test_page); + std::wstring revisit_time = GetActiveTabTitle(); + + if (expect_new_tab_cached) { + EXPECT_EQ(original_time, revisit_time); + }else { + EXPECT_NE(original_time, revisit_time); + } + + Sleep(kWaitForCacheUpdateMsec); + + // Force reload, overriding the caching behavior + NavigateToURL(test_page); + std::wstring reload_time = GetActiveTabTitle(); + + EXPECT_NE(revisit_time, reload_time); + + if (expect_delayed_reload) { + Sleep(kWaitForCacheUpdateMsec * kCacheWaitMultiplier); + + GetNewTab(automation(), test_page); + revisit_time = GetActiveTabTitle(); + + EXPECT_NE(reload_time, revisit_time); + } +} + +// Appends a new tab to the test chrome window and loads the specified URL. +void CacheTest::GetNewTab(AutomationProxy* automationProxy, + const GURL& tab_url) { + scoped_ptr<BrowserProxy> window_proxy(automationProxy->GetBrowserWindow(0)); + ASSERT_TRUE(window_proxy.get()); + ASSERT_TRUE(window_proxy->AppendTab(tab_url)); +} + +// Tests that a cached copy of the page is not used when max-age=0 headers +// are specified. +TEST_F(CacheTest, NoCacheMaxAge) { + RunCacheTest(L"nocachetime/maxage", false, false); +} + +// Tests that a cached copy of the page is not used when no-cache header +// is specified. +TEST_F(CacheTest, NoCache) { + RunCacheTest(L"nocachetime", false, false); +} + +// Tests that a cached copy of a page is used when its headers specify +// that it should be cached for 60 seconds. +TEST_F(CacheTest, Cache) { + RunCacheTest(L"cachetime", true, false); +} + +// Tests that a cached copy of the page is used when expires header +// specifies that the page has not yet expired. +TEST_F(CacheTest, Expires) { + RunCacheTest(L"cache/expires", true, false); +} + +// Tests that a cached copy of the page is used when proxy-revalidate header +// is specified and the page has not yet expired. +TEST_F(CacheTest, ProxyRevalidate) { + RunCacheTest(L"cache/proxy-revalidate", true, false); +} + +// Tests that a cached copy of the page is used when private header +// is specified and the page has not yet expired. +TEST_F(CacheTest, Private) { + RunCacheTest(L"cache/private", true, true); +} + +// Tests that a cached copy of the page is used when public header +// is specified and the page has not yet expired. +TEST_F(CacheTest, Public) { + RunCacheTest(L"cache/public", true, true); +} + +// Tests that a cached copy of the page is not used when s-maxage header +// is specified. +TEST_F(CacheTest, SMaxAge) { + RunCacheTest(L"cache/s-maxage", false, false); +} + +// Tests that a cached copy of the page is not used when must-revalidate header +// is specified. +TEST_F(CacheTest, MustRevalidate) { + RunCacheTest(L"cache/must-revalidate", false, false); +} + +// Tests that a cached copy of the page is not used when must-revalidate header +// is specified, even though the page has not yet expired. +TEST_F(CacheTest, MustRevalidateMaxAge) { + RunCacheTest(L"cache/must-revalidate/max-age", false, false); +} + +// Tests that a cached copy of the page is not used when no-store header +// is specified. +TEST_F(CacheTest, NoStore) { + RunCacheTest(L"cache/no-store", false, false); +} + +// Tests that a cached copy of the page is not used when no-store header +// is specified, even though the page has not yet expired. +TEST_F(CacheTest, NoStoreMaxAge) { + RunCacheTest(L"cache/no-store/max-age", false, false); +} + +// Tests that a cached copy of the page is not transformed when no-transform +// header is specified. +TEST_F(CacheTest, NoTransform) { + RunCacheTest(L"cache/no-transform", false, false); +} diff --git a/chrome/common/net/cookie_monster_sqlite.cc b/chrome/common/net/cookie_monster_sqlite.cc new file mode 100644 index 0000000..259b8f2 --- /dev/null +++ b/chrome/common/net/cookie_monster_sqlite.cc @@ -0,0 +1,378 @@ +// 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 "chrome/common/net/cookie_monster_sqlite.h" + +#include "base/logging.h" +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/common/sqlite_compiled_statement.h" +#include "chrome/common/sqlite_utils.h" + +// This class is designed to be shared between any calling threads and the +// database thread. It batches operations and commits them on a timer. +class SQLitePersistentCookieStore::Backend + : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> { + public: + // The passed database pointer must be already-initialized. This object will + // take ownership. + explicit Backend(sqlite3* db, MessageLoop* loop) + : db_(db), + background_loop_(loop), + cache_(new SqliteStatementCache(db)), + num_pending_(0) { + DCHECK(db_) << "Database must exist."; + } + + // You should call Close() before destructing this object. + ~Backend() { + DCHECK(!db_) << "Close should have already been called."; + DCHECK(num_pending_ == 0 && pending_.empty()); + } + + // Batch a cookie add + void AddCookie(const std::string& key, + const CookieMonster::CanonicalCookie& cc); + // Batch a cookie delete + void DeleteCookie(const CookieMonster::CanonicalCookie& cc); + // Commit and pending operations and close the database, must be called + // before the object is destructed. + void Close(); + + private: + class PendingOperation { + public: + typedef enum { + COOKIE_ADD, + COOKIE_DELETE, + } OperationType; + + PendingOperation(OperationType op, + const std::string& key, + const CookieMonster::CanonicalCookie& cc) + : op_(op), key_(key), cc_(cc) { } + + OperationType op() const { return op_; } + const std::string& key() const { return key_; } + const CookieMonster::CanonicalCookie& cc() const { return cc_; } + + private: + OperationType op_; + std::string key_; // Only used for OP_ADD + CookieMonster::CanonicalCookie cc_; + }; + + private: + // Batch a cookie operation (add or delete) + void BatchOperation(PendingOperation::OperationType op, + const std::string& key, + const CookieMonster::CanonicalCookie& cc); + // Commit our pending operations to the database. + void Commit(); + // Close() executed on the background thread. + void InternalBackgroundClose(); + + sqlite3* db_; + MessageLoop* background_loop_; + SqliteStatementCache* cache_; + + typedef std::list<PendingOperation*> PendingOperationsList; + PendingOperationsList pending_; + PendingOperationsList::size_type num_pending_; + Lock pending_lock_; // Guard pending_ and num_pending_ + + DISALLOW_EVIL_CONSTRUCTORS(Backend); +}; + +void SQLitePersistentCookieStore::Backend::AddCookie( + const std::string& key, + const CookieMonster::CanonicalCookie& cc) { + BatchOperation(PendingOperation::COOKIE_ADD, key, cc); +} + +void SQLitePersistentCookieStore::Backend::DeleteCookie( + const CookieMonster::CanonicalCookie& cc) { + BatchOperation(PendingOperation::COOKIE_DELETE, std::string(), cc); +} + +void SQLitePersistentCookieStore::Backend::BatchOperation( + PendingOperation::OperationType op, + const std::string& key, + const CookieMonster::CanonicalCookie& cc) { + // Commit every 30 seconds. + static const int kCommitIntervalMs = 30 * 1000; + // Commit right away if we have more than 512 outstanding operations. + static const int kCommitAfterBatchSize = 512; + DCHECK(MessageLoop::current() != background_loop_); + + // We do a full copy of the cookie here, and hopefully just here. + scoped_ptr<PendingOperation> po(new PendingOperation(op, key, cc)); + CHECK(po.get()); + + PendingOperationsList::size_type num_pending; + { + AutoLock locked(pending_lock_); + pending_.push_back(po.release()); + num_pending = ++num_pending_; + } + + // TODO(abarth): What if the DB thread is being destroyed on the UI thread? + if (num_pending == 1) { + // We've gotten our first entry for this batch, fire off the timer. + background_loop_->PostDelayedTask(FROM_HERE, + NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs); + } else if (num_pending == kCommitAfterBatchSize) { + // We've reached a big enough batch, fire off a commit now. + background_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &Backend::Commit)); + } +} + +void SQLitePersistentCookieStore::Backend::Commit() { + DCHECK(MessageLoop::current() == background_loop_); + PendingOperationsList ops; + { + AutoLock locked(pending_lock_); + pending_.swap(ops); + num_pending_ = 0; + } + + // Maybe an old timer fired or we are already Close()'ed. + if (!db_ || ops.empty()) + return; + + SQLITE_UNIQUE_STATEMENT(add_smt, *cache_, + "INSERT INTO cookies VALUES (?,?,?,?,?,?,?,?)"); + if (!add_smt.is_valid()) { + NOTREACHED(); + return; + } + SQLITE_UNIQUE_STATEMENT(del_smt, *cache_, + "DELETE FROM cookies WHERE creation_utc=?"); + if (!del_smt.is_valid()) { + NOTREACHED(); + return; + } + + SQLTransaction transaction(db_); + transaction.Begin(); + for (PendingOperationsList::iterator it = ops.begin(); + it != ops.end(); ++it) { + // Free the cookies as we commit them to the database. + scoped_ptr<PendingOperation> po(*it); + switch (po->op()) { + case PendingOperation::COOKIE_ADD: + add_smt->reset(); + add_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); + add_smt->bind_string(1, po->key()); + add_smt->bind_string(2, po->cc().Name()); + add_smt->bind_string(3, po->cc().Value()); + add_smt->bind_string(4, po->cc().Path()); + add_smt->bind_int64(5, po->cc().ExpiryDate().ToInternalValue()); + add_smt->bind_int(6, po->cc().IsSecure()); + add_smt->bind_int(7, po->cc().IsHttpOnly()); + if (add_smt->step() != SQLITE_DONE) { + NOTREACHED() << "Could not add a cookie to the DB."; + } + break; + case PendingOperation::COOKIE_DELETE: + del_smt->reset(); + del_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); + if (del_smt->step() != SQLITE_DONE) { + NOTREACHED() << "Could not delete a cookie from the DB."; + } + break; + default: + NOTREACHED(); + break; + } + } + transaction.Commit(); +} + +// Fire off a close message to the background thread. We could still have a +// pending commit timer that will be holding a reference on us, but if/when +// this fires we will already have been cleaned up and it will be ignored. +void SQLitePersistentCookieStore::Backend::Close() { + DCHECK(MessageLoop::current() != background_loop_); + // Must close the backend on the background thread. + // TODO(abarth): What if the DB thread is being destroyed on the UI thread? + background_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &Backend::InternalBackgroundClose)); +} + +void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() { + DCHECK(MessageLoop::current() == background_loop_); + // Commit any pending operations + Commit(); + // We must destroy the cache before closing the database. + delete cache_; + cache_ = NULL; + sqlite3_close(db_); + db_ = NULL; +} + +SQLitePersistentCookieStore::SQLitePersistentCookieStore( + const std::wstring& path, MessageLoop* background_loop) + : path_(path), + background_loop_(background_loop) { + DCHECK(background_loop) << "SQLitePersistentCookieStore needs a MessageLoop"; +} + +SQLitePersistentCookieStore::~SQLitePersistentCookieStore() { + if (backend_.get()) { + backend_->Close(); + // Release our reference, it will probably still have a reference if the + // background thread has not run Close() yet. + backend_ = NULL; + } +} + +// Version number of the database. +static const int kCurrentVersionNumber = 2; + +namespace { + +// Initializes the cookies table, returning true on success. +bool InitTable(sqlite3* db) { + if (!DoesSqliteTableExist(db, "cookies")) { + if (sqlite3_exec(db, "CREATE TABLE cookies (" + "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY," + "host_key TEXT NOT NULL," + "name TEXT NOT NULL," + "value TEXT NOT NULL," + "path TEXT NOT NULL," + // We only store persistent, so we know it expires + "expires_utc INTEGER NOT NULL," + "secure INTEGER NOT NULL," + "httponly INTEGER NOT NULL)", + NULL, NULL, NULL) != SQLITE_OK) + return false; + } + + // Try to create the index every time. Older versions did not have this index, + // so we want those people to get it. Ignore errors, since it may exist. + sqlite3_exec(db, "CREATE INDEX cookie_times ON cookies (creation_utc)", + NULL, NULL, NULL); + + return true; +} + +} // namespace + +bool SQLitePersistentCookieStore::Load( + std::vector<CookieMonster::KeyedCanonicalCookie>* cookies) { + DCHECK(!path_.empty()); + sqlite3* db; + if (sqlite3_open(WideToUTF8(path_).c_str(), &db) != SQLITE_OK) { + NOTREACHED() << "Unable to open cookie DB."; + return false; + } + + if (!EnsureDatabaseVersion(db) || !InitTable(db)) { + NOTREACHED() << "Unable to initialize cookie DB."; + sqlite3_close(db); + return false; + } + + // Slurp all the cookies into the out-vector. + SQLStatement smt; + if (smt.prepare(db, "SELECT * FROM cookies") != SQLITE_OK) { + NOTREACHED() << "select statement prep failed"; + sqlite3_close(db); + return false; + } + + // Step the statement and do the preload operation. Preload() requires that a + // statement be open on the database, so we oblige. We don't bother keeping + // this and reset the statement so we can read all the rows. + // + // The Preload call makes the read faster, since we will be reading most of + // the database, it's better to read it in one chunk than have a lot of seeks + // bringing it in organically. + if (smt.step() != SQLITE_ROW) + sqlite3Preload(db); + smt.reset(); + + while (smt.step() == SQLITE_ROW) { + std::string key = smt.column_string(1); + scoped_ptr<CookieMonster::CanonicalCookie> cc( + new CookieMonster::CanonicalCookie( + smt.column_string(2), // name + smt.column_string(3), // value + smt.column_string(4), // path + smt.column_int(6) != 0, // secure + smt.column_int(7) != 0, // httponly + Time::FromInternalValue(smt.column_int64(0)), // creation_utc + true, // has_expires + Time::FromInternalValue(smt.column_int64(5)))); // expires_utc + // Memory allocation failed. + if (!cc.get()) + break; + + DLOG_IF(WARNING, + cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; + cookies->push_back( + CookieMonster::KeyedCanonicalCookie(smt.column_string(1), + cc.release())); + } + + // Create the backend, this will take ownership of the db pointer. + backend_ = new Backend(db, background_loop_); + + return true; +} + +bool SQLitePersistentCookieStore::EnsureDatabaseVersion(sqlite3* db) { + // Version check. + if (!meta_table_.Init(std::string(), kCurrentVersionNumber, db)) + return false; + + int compat_version = meta_table_.GetCompatibleVersionNumber(); + if (compat_version > kCurrentVersionNumber) { + DLOG(WARNING) << "Cookie DB version from the future: " << compat_version; + return false; + } + + return true; +} + +void SQLitePersistentCookieStore::AddCookie( + const std::string& key, + const CookieMonster::CanonicalCookie& cc) { + if (backend_.get()) + backend_->AddCookie(key, cc); +} + +void SQLitePersistentCookieStore::DeleteCookie( + const CookieMonster::CanonicalCookie& cc) { + if (backend_.get()) + backend_->DeleteCookie(cc); +} diff --git a/chrome/common/net/cookie_monster_sqlite.h b/chrome/common/net/cookie_monster_sqlite.h new file mode 100644 index 0000000..49de96a --- /dev/null +++ b/chrome/common/net/cookie_monster_sqlite.h @@ -0,0 +1,76 @@ +// 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 sqlite implementation of a cookie monster persistent store. + +#ifndef CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ +#define CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ + +#include <string> +#include <vector> + +#include "base/ref_counted.h" +#include "base/message_loop.h" +#include "chrome/browser/meta_table_helper.h" +#include "net/base/cookie_monster.h" + +struct sqlite3; + +class SQLitePersistentCookieStore + : public CookieMonster::PersistentCookieStore { + public: + SQLitePersistentCookieStore(const std::wstring& path, + MessageLoop* background_loop); + ~SQLitePersistentCookieStore(); + + virtual bool Load(std::vector<CookieMonster::KeyedCanonicalCookie>*); + + virtual void AddCookie(const std::string&, + const CookieMonster::CanonicalCookie&); + virtual void DeleteCookie(const CookieMonster::CanonicalCookie&); + + private: + class Backend; + + // Database upgrade statements. + bool EnsureDatabaseVersion(sqlite3* db); + bool UpdateSchemaToVersion2(sqlite3* db); + + std::wstring path_; + scoped_refptr<Backend> backend_; + + // Background MessageLoop on which to access the backend_; + MessageLoop* background_loop_; + + MetaTableHelper meta_table_; + + DISALLOW_EVIL_CONSTRUCTORS(SQLitePersistentCookieStore); +}; + +#endif // CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ diff --git a/chrome/common/net/dns.h b/chrome/common/net/dns.h new file mode 100644 index 0000000..9a178e3 --- /dev/null +++ b/chrome/common/net/dns.h @@ -0,0 +1,49 @@ +// 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 has shared types used across IPC between render_dns_master.cc +// and dns_master.cc + + +#ifndef CHROME_COMMON_DNS_H__ +#define CHROME_COMMON_DNS_H__ + +#include <vector> + +namespace chrome_common_net{ + +// IPC messages are passed from the renderer to the browser in the form of +// Namelist instances. +// Each element of this vector is a hostname that needs to be looked up. +// The hostnames should never be empty strings. +typedef std::vector<std::string> NameList; + +} + +#endif // CHROME_COMMON_DNS_H__
\ No newline at end of file diff --git a/chrome/common/net/url_request_intercept_job.cc b/chrome/common/net/url_request_intercept_job.cc new file mode 100644 index 0000000..4da58d0c --- /dev/null +++ b/chrome/common/net/url_request_intercept_job.cc @@ -0,0 +1,234 @@ +// 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 job type handles Chrome plugin network interception. When a plugin +// wants to intercept a request, a job of this type is created. The intercept +// job communicates with the plugin to retrieve the response headers and data. + +#include "chrome/common/net/url_request_intercept_job.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "net/base/net_errors.h" + +// +// URLRequestInterceptJob +// + +URLRequestInterceptJob::URLRequestInterceptJob(URLRequest* request, + ChromePluginLib* plugin, + ScopableCPRequest* cprequest) + : URLRequestJob(request), + cprequest_(cprequest), + plugin_(plugin), + read_buffer_(NULL), + got_headers_(false) { + cprequest_->data = this; // see FromCPRequest(). + + NotificationService::current()->AddObserver( + this, NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(plugin_)); +} + +URLRequestInterceptJob::~URLRequestInterceptJob() { + if (plugin_) { + plugin_->functions().request_funcs->end_request(cprequest_.get(), + CPERR_SUCCESS); + DetachPlugin(); + } +} + +void URLRequestInterceptJob::DetachPlugin() { + NotificationService::current()->RemoveObserver( + this, NOTIFY_CHROME_PLUGIN_UNLOADED, + Source<ChromePluginLib>(plugin_)); + plugin_ = NULL; +} + +void URLRequestInterceptJob::Start() { + // Start reading asynchronously so that all error reporting and data + // callbacks happen as they would for network requests. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestInterceptJob::StartAsync)); +} + +void URLRequestInterceptJob::Kill() { + if (plugin_) { + plugin_->functions().request_funcs->end_request(cprequest_.get(), + CPERR_CANCELLED); + DetachPlugin(); + } + URLRequestJob::Kill(); +} + +bool URLRequestInterceptJob::ReadRawData(char* dest, int dest_size, + int* bytes_read) { + DCHECK_NE(dest_size, 0); + DCHECK(bytes_read); + + if (!plugin_) + return false; + + int rv = plugin_->functions().request_funcs->read(cprequest_.get(), + dest, dest_size); + if (rv >= 0) { + *bytes_read = rv; + return true; + } + + if (rv == CPERR_IO_PENDING) { + read_buffer_ = dest; + read_buffer_size_ = dest_size; + SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); + } else { + // TODO(mpcomplete): better error code + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED)); + } + + return false; +} + +bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) { + return request_->response_headers()->GetMimeType(mime_type); +} + +bool URLRequestInterceptJob::GetCharset(std::string* charset) { + return request_->response_headers()->GetCharset(charset); +} + +bool URLRequestInterceptJob::GetContentEncoding(std::string* encoding_type) { + // TODO(darin): what if there are multiple content encodings? + return request_->response_headers()->EnumerateHeader(NULL, "Content-Encoding", + encoding_type); +} + +void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) { + if (!plugin_) + return; + + std::string raw_headers; + int size = plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0); + int rv = size < 0 ? size : + plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, + WriteInto(&raw_headers, size+1), size); + if (rv != CPERR_SUCCESS) { + // TODO(mpcomplete): what should we do here? + raw_headers = "HTTP/1.1 404 Not Found" + '\0'; + } + + info->headers = new net::HttpResponseHeaders(raw_headers); + + if (request_->url().SchemeIsSecure()) { + // Make up a fake certificate for intercepted data since we don't have + // access to the real SSL info. + // TODO(mpcomplete): we should probably have the interception API transmit + // the SSL info, but the only consumer of this API (Gears) doesn't keep that + // around. We should change that. + const char* kCertIssuer = "Chrome Internal"; + const int kLifetimeDays = 100; + + DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL " + << request_->url(); + + info->ssl_info.cert = + new X509Certificate(request_->url().GetWithEmptyPath().spec(), + kCertIssuer, + Time::Now(), + Time::Now() + TimeDelta::FromDays(kLifetimeDays)); + info->ssl_info.cert_status = 0; + info->ssl_info.security_bits = 0; + } +} + +int URLRequestInterceptJob::GetResponseCode() { + if (!plugin_) + return -1; + + int status = 200; + plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status)); + + return status; +} + +bool URLRequestInterceptJob::IsRedirectResponse(GURL* location, + int* http_status_code) { + if (!request_->response_headers()) + return false; + + std::string value; + if (!request_->response_headers()->IsRedirect(&value)) + return false; + + *location = request_->url().Resolve(value); + *http_status_code = request_->response_headers()->response_code(); + return true; +} + +void URLRequestInterceptJob::StartAsync() { + // We may have been orphaned... + if (!request_ || !plugin_) + return; + + int rv = plugin_->functions().request_funcs->start_request(cprequest_.get()); + if (rv != CPERR_IO_PENDING) + OnStartCompleted(rv); +} + +void URLRequestInterceptJob::OnStartCompleted(int result) { + if (result != CPERR_SUCCESS) { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, + net::ERR_CONNECTION_FAILED)); + return; + } + + NotifyHeadersComplete(); +} + +void URLRequestInterceptJob::OnReadCompleted(int bytes_read) { + if (bytes_read < 0) { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED)); + return; + } + + SetStatus(URLRequestStatus()); // clear the async flag + NotifyReadComplete(bytes_read); +} + +void URLRequestInterceptJob::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NOTIFY_CHROME_PLUGIN_UNLOADED); + DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr()); + + DetachPlugin(); +} diff --git a/chrome/common/net/url_request_intercept_job.h b/chrome/common/net/url_request_intercept_job.h new file mode 100644 index 0000000..9eea7a6 --- /dev/null +++ b/chrome/common/net/url_request_intercept_job.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 CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__ +#define CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__ + +#include "base/basictypes.h" +#include "net/url_request/url_request.h" +#include "net/url_request/url_request_job.h" +#include "chrome/browser/chrome_plugin_host.h" +#include "chrome/common/chrome_plugin_api.h" +#include "chrome/common/chrome_plugin_util.h" +#include "chrome/common/notification_service.h" + +class ChromePluginLib; + +// A request job that handles network requests intercepted by a Chrome plugin. +class URLRequestInterceptJob + : public URLRequestJob, public NotificationObserver { + public: + static URLRequestInterceptJob* FromCPRequest(CPRequest* request) { + return ScopableCPRequest::GetData<URLRequestInterceptJob*>(request); + } + + URLRequestInterceptJob(URLRequest* request, ChromePluginLib* plugin, + ScopableCPRequest* cprequest); + virtual ~URLRequestInterceptJob(); + + // Plugin callbacks. + void OnStartCompleted(int result); + void OnReadCompleted(int bytes_read); + + // URLRequestJob + virtual void Start(); + virtual void Kill(); + virtual bool ReadRawData(char* buf, int buf_size, int* bytes_read); + virtual bool GetMimeType(std::string* mime_type); + virtual bool GetCharset(std::string* charset); + virtual void GetResponseInfo(net::HttpResponseInfo* info); + virtual int GetResponseCode(); + virtual bool GetContentEncoding(std::string* encoding_type); + virtual bool IsRedirectResponse(GURL* location, int* http_status_code); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + private: + void StartAsync(); + void DetachPlugin(); + + scoped_ptr<ScopableCPRequest> cprequest_; + ChromePluginLib* plugin_; + bool got_headers_; + char* read_buffer_; + int read_buffer_size_; + + DISALLOW_EVIL_CONSTRUCTORS(URLRequestInterceptJob); +}; + + +#endif // CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H__ diff --git a/chrome/common/net/url_util_unittest.cc b/chrome/common/net/url_util_unittest.cc new file mode 100644 index 0000000..28606f1 --- /dev/null +++ b/chrome/common/net/url_util_unittest.cc @@ -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. + +#include "base/basictypes.h" +#include "googleurl/src/url_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(URLUtil, Scheme) { + enum SchemeType { + NO_SCHEME = 0, + SCHEME = 1 << 0, + STANDARD_SCHEME = 1 << 1, + }; + const struct { + const wchar_t* url; + const char* try_scheme; + const bool scheme_matches; + const int flags; + } tests[] = { + {L" ", "hello", false, NO_SCHEME}, + {L"foo", NULL, false, NO_SCHEME}, + {L"google.com/foo:bar", NULL, false, NO_SCHEME}, + {L"Garbage:foo.com", "garbage", true, SCHEME}, + {L"Garbage:foo.com", "trash", false, SCHEME}, + {L"gopher:", "gopher", true, SCHEME | STANDARD_SCHEME}, + {L"About:blank", "about", true, SCHEME}, + {L"http://foo.com:123", "foo", false, SCHEME | STANDARD_SCHEME}, + {L"file://c/", "file", true, SCHEME | STANDARD_SCHEME}, + }; + + for (size_t i = 0; i < arraysize(tests); i++) { + int url_len = static_cast<int>(wcslen(tests[i].url)); + + url_parse::Component parsed_scheme; + bool has_scheme = url_parse::ExtractScheme(tests[i].url, url_len, + &parsed_scheme); + + EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME), + url_util::IsStandard(tests[i].url, url_len)); + EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME), + url_util::IsStandardScheme(tests[i].url, parsed_scheme.len)); + + url_parse::Component found_scheme; + EXPECT_EQ(tests[i].scheme_matches, + url_util::FindAndCompareScheme(tests[i].url, url_len, + tests[i].try_scheme, + &found_scheme)); + EXPECT_EQ(parsed_scheme.begin, found_scheme.begin); + EXPECT_EQ(parsed_scheme.len, found_scheme.len); + } +} diff --git a/chrome/common/notification_details.h b/chrome/common/notification_details.h new file mode 100644 index 0000000..9c7fdff --- /dev/null +++ b/chrome/common/notification_details.h @@ -0,0 +1,80 @@ +// 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 the type used to provide details for NotificationService +// notifications. + +#ifndef CHROME_COMMON_NOTIFICATION_DETAILS_H__ +#define CHROME_COMMON_NOTIFICATION_DETAILS_H__ + +#include "base/basictypes.h" + +// Do not declare a NotificationDetails directly--use either +// "Details<detailsclassname>(detailsclasspointer)" or +// NotificationService::NoDetails(). +class NotificationDetails { + public: + NotificationDetails() : ptr_(NULL) {} + ~NotificationDetails() {} + + // NotificationDetails can be used as the index for a map; this method + // returns the pointer to the current details as an identifier, for use as a + // map index. + uintptr_t map_key() const { return reinterpret_cast<uintptr_t>(ptr_); } + + bool operator!=(const NotificationDetails& other) const { + return ptr_ != other.ptr_; + } + + bool operator==(const NotificationDetails& other) const { + return ptr_ == other.ptr_; + } + + protected: + NotificationDetails(void* ptr) : ptr_(ptr) {} + NotificationDetails(const NotificationDetails& other) : ptr_(other.ptr_) {} + + void* ptr_; + +private: + void operator=(const NotificationDetails&); +}; + +template <class T> +class Details : public NotificationDetails { + public: + Details(T* ptr) : NotificationDetails(ptr) {} + Details(const NotificationDetails& other) + : NotificationDetails(other) {} + + T* operator->() const { return ptr(); } + T* ptr() const { return static_cast<T*>(ptr_); } +}; + +#endif // CHROME_COMMON_NOTIFICATION_DETAILS_H__ diff --git a/chrome/common/notification_service.cc b/chrome/common/notification_service.cc new file mode 100644 index 0000000..672052f --- /dev/null +++ b/chrome/common/notification_service.cc @@ -0,0 +1,140 @@ +// 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 "chrome/common/notification_service.h" + +int NotificationService::tls_index_ = ThreadLocalStorage::Alloc(); + +/* static */ +bool NotificationService::HasKey(const NotificationSourceMap& map, + const NotificationSource& source) { + return map.find(source.map_key()) != map.end(); +} + +NotificationService::NotificationService() { + DCHECK(current() == NULL); +#ifndef NDEBUG + memset(observer_counts_, 0, sizeof(observer_counts_)); +#endif + + ThreadLocalStorage::Set(tls_index_, this); +} + +void NotificationService::AddObserver(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source) { + DCHECK(type < NOTIFICATION_TYPE_COUNT); + + NotificationObserverList* observer_list; + if (HasKey(observers_[type], source)) { + observer_list = observers_[type][source.map_key()]; + } else { + observer_list = new NotificationObserverList; + observers_[type][source.map_key()] = observer_list; + } + + observer_list->AddObserver(observer); +#ifndef NDEBUG + ++observer_counts_[type]; +#endif +} + +void NotificationService::RemoveObserver(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source) { + DCHECK(type < NOTIFICATION_TYPE_COUNT); + DCHECK(HasKey(observers_[type], source)); + + NotificationObserverList* observer_list = observers_[type][source.map_key()]; + if (observer_list) { + observer_list->RemoveObserver(observer); +#ifndef NDEBUG + --observer_counts_[type]; +#endif + } + + // TODO(jhughes): Remove observer list from map if empty? +} + +void NotificationService::Notify(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type > NOTIFY_ALL); // Allowed for subscription, but not posting. + DCHECK(type < NOTIFICATION_TYPE_COUNT); + + // There's no particular reason for the order in which the different + // classes of observers get notified here. + + // Notify observers of all types and all sources + if (HasKey(observers_[NOTIFY_ALL], AllSources()) && + source != AllSources()) + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[NOTIFY_ALL][AllSources().map_key()], + Observe(type, source, details)); + // Notify observers of all types and the given source + if (HasKey(observers_[NOTIFY_ALL], source)) + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[NOTIFY_ALL][source.map_key()], + Observe(type, source, details)); + // Notify observers of the given type and all sources + if (HasKey(observers_[type], AllSources()) && + source != AllSources()) + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[type][AllSources().map_key()], + Observe(type, source, details)); + // Notify observers of the given type and the given source + if (HasKey(observers_[type], source)) + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[type][source.map_key()], + Observe(type, source, details)); +} + + +NotificationService::~NotificationService() { + ThreadLocalStorage::Set(tls_index_, NULL); + +#ifndef NDEBUG + for (int i = 0; i < NOTIFICATION_TYPE_COUNT; i++) { + if (observer_counts_[i] > 0) { + LOG(WARNING) << observer_counts_[i] << " notification observer(s) leaked" + << " of notification type " << i; + } + } +#endif + + for (int i = 0; i < NOTIFICATION_TYPE_COUNT; i++) { + NotificationSourceMap omap = observers_[i]; + for (NotificationSourceMap::iterator it = omap.begin(); + it != omap.end(); ++it) { + delete it->second; + } + } +} + +NotificationObserver::~NotificationObserver() {} diff --git a/chrome/common/notification_service.h b/chrome/common/notification_service.h new file mode 100644 index 0000000..1243748 --- /dev/null +++ b/chrome/common/notification_service.h @@ -0,0 +1,144 @@ +// 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 describes a central switchboard for notifications that might +// happen in various parts of the application, and allows users to register +// observers for various classes of events that they're interested in. + +#ifndef CHROME_COMMON_NOTIFICATION_SERVICE_H__ +#define CHROME_COMMON_NOTIFICATION_SERVICE_H__ + +#include <map> + +#include "base/thread_local_storage.h" +#include "base/observer_list.h" +#include "base/values.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_types.h" + +class NotificationObserver; + +class NotificationService { + public: + // Returns the NotificationService object for the current thread, or NULL if + // none. + static NotificationService* current() { + return static_cast<NotificationService *>( + ThreadLocalStorage::Get(tls_index_)); + } + + // Normally instantiated when the thread is created. Not all threads have + // a NotificationService. Only one instance should be created per thread. + NotificationService(); + ~NotificationService(); + + // Registers a NotificationObserver to be called whenever a matching + // notification is posted. Observer is a pointer to an object subclassing + // NotificationObserver to be notified when an event matching the other two + // parameters is posted to this service. Type is the type of events to + // be notified about (or NOTIFY_ALL to receive events of all types). + // Source is a NotificationSource object (created using + // "Source<classname>(pointer)"), if this observer only wants to + // receive events from that object, or NotificationService::AllSources() + // to receive events from all sources. + // + // A given observer can be registered only once for each combination of + // type and source. If the same object is registered more than once, + // it must be removed for each of those combinations of type and source later. + // + // The caller retains ownership of the object pointed to by observer. + void AddObserver(NotificationObserver* observer, + NotificationType type, const NotificationSource& source); + + // Removes the object pointed to by observer from receiving notifications + // that match type and source. If no object matching the parameters is + // currently registered, this method is a no-op. + void RemoveObserver(NotificationObserver* observer, + NotificationType type, const NotificationSource& source); + + // Synchronously posts a notification to all interested observers. + // Source is a reference to a NotificationSource object representing + // the object originating the notification (can be + // NotificationService::AllSources(), in which case + // only observers interested in all sources will be notified). + // Details is a reference to an object containing additional data about + // the notification. If no additional data is needed, NoDetails() is used. + // There is no particular order in which the observers will be notified. + void Notify(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Returns a NotificationSource that represents all notification sources + // (for the purpose of registering an observer for events from all sources). + static Source<void> AllSources() { return Source<void>(NULL); } + + // Returns a NotificationDetails object that represents a lack of details + // associated with a notification. (This is effectively a null pointer.) + static Details<void> NoDetails() { return Details<void>(NULL); } + + private: + typedef ObserverList<NotificationObserver> NotificationObserverList; + typedef std::map<uintptr_t, NotificationObserverList*> NotificationSourceMap; + + // Convenience function to determine whether a source has a + // NotificationObserverList in the given map; + static bool HasKey(const NotificationSourceMap& map, + const NotificationSource& source); + + // Keeps track of the observers for each type of notification. + // Until we get a prohibitively large number of notification types, + // a simple array is probably the fastest way to dispatch. + NotificationSourceMap observers_[NOTIFICATION_TYPE_COUNT]; + + // The thread local storage index, used for getting the current thread's + // instance. + static int tls_index_; + +#ifndef NDEBUG + // Used to check to see that AddObserver and RemoveObserver calls are + // balanced. + int observer_counts_[NOTIFICATION_TYPE_COUNT]; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(NotificationService); +}; + +// This is the base class for notification observers. When a matching +// notification is posted to the notification service, Observe is called. +class NotificationObserver { + public: + virtual ~NotificationObserver(); + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) = 0; +}; + +#endif // CHROME_COMMON_NOTIFICATION_SERVICE_H__ diff --git a/chrome/common/notification_service_unittest.cc b/chrome/common/notification_service_unittest.cc new file mode 100644 index 0000000..f286163 --- /dev/null +++ b/chrome/common/notification_service_unittest.cc @@ -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. + +#include "chrome/common/notification_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class NotificationServiceTest: public testing::Test { +}; + +class TestObserver : public NotificationObserver { +public: + TestObserver() : notification_count_(0) {} + + int notification_count() { return notification_count_; } + + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + ++notification_count_; + } + +private: + int notification_count_; +}; + +// Bogus class to act as a NotificationSource for the messages. +class TestSource {}; + +} // namespace + + +TEST(NotificationServiceTest, Basic) { + TestSource test_source; + TestSource other_source; + + // Check the equality operators defined for NotificationSource + EXPECT_TRUE( + Source<TestSource>(&test_source) == Source<TestSource>(&test_source)); + EXPECT_TRUE( + Source<TestSource>(&test_source) != Source<TestSource>(&other_source)); + + TestObserver all_types_all_sources; + TestObserver idle_all_sources; + TestObserver all_types_test_source; + TestObserver idle_test_source; + + NotificationService* service = NotificationService::current(); + + // Make sure it doesn't freak out when there are no observers. + service->Notify(NOTIFY_IDLE, + Source<TestSource>(&test_source), + NotificationService::NoDetails()); + + service->AddObserver( + &all_types_all_sources, NOTIFY_ALL, NotificationService::AllSources()); + service->AddObserver( + &idle_all_sources, NOTIFY_IDLE, NotificationService::AllSources()); + service->AddObserver( + &all_types_test_source, NOTIFY_ALL, Source<TestSource>(&test_source)); + service->AddObserver( + &idle_test_source, NOTIFY_IDLE, Source<TestSource>(&test_source)); + + EXPECT_EQ(0, all_types_all_sources.notification_count()); + EXPECT_EQ(0, idle_all_sources.notification_count()); + EXPECT_EQ(0, all_types_test_source.notification_count()); + EXPECT_EQ(0, idle_test_source.notification_count()); + + service->Notify(NOTIFY_IDLE, + Source<TestSource>(&test_source), + NotificationService::NoDetails()); + + EXPECT_EQ(1, all_types_all_sources.notification_count()); + EXPECT_EQ(1, idle_all_sources.notification_count()); + EXPECT_EQ(1, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->Notify(NOTIFY_BUSY, + Source<TestSource>(&test_source), + NotificationService::NoDetails()); + + EXPECT_EQ(2, all_types_all_sources.notification_count()); + EXPECT_EQ(1, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->Notify(NOTIFY_IDLE, + Source<TestSource>(&other_source), + NotificationService::NoDetails()); + + EXPECT_EQ(3, all_types_all_sources.notification_count()); + EXPECT_EQ(2, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->Notify(NOTIFY_BUSY, + Source<TestSource>(&other_source), + NotificationService::NoDetails()); + + EXPECT_EQ(4, all_types_all_sources.notification_count()); + EXPECT_EQ(2, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + // Try send with NULL source. + service->Notify(NOTIFY_IDLE, + NotificationService::AllSources(), + NotificationService::NoDetails()); + + EXPECT_EQ(5, all_types_all_sources.notification_count()); + EXPECT_EQ(3, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->RemoveObserver( + &all_types_all_sources, NOTIFY_ALL, NotificationService::AllSources()); + service->RemoveObserver( + &idle_all_sources, NOTIFY_IDLE, NotificationService::AllSources()); + service->RemoveObserver( + &all_types_test_source, NOTIFY_ALL, Source<TestSource>(&test_source)); + service->RemoveObserver( + &idle_test_source, NOTIFY_IDLE, Source<TestSource>(&test_source)); + + service->Notify(NOTIFY_IDLE, + Source<TestSource>(&test_source), + NotificationService::NoDetails()); + + EXPECT_EQ(5, all_types_all_sources.notification_count()); + EXPECT_EQ(3, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + // Removing an observer that isn't there is a no-op, this should be fine. + service->RemoveObserver( + &all_types_all_sources, NOTIFY_ALL, NotificationService::AllSources()); +} + +TEST(NotificationServiceTest, MultipleRegistration) { + TestSource test_source; + + TestObserver idle_test_source; + + NotificationService* service = NotificationService::current(); + + service->AddObserver( + &idle_test_source, NOTIFY_IDLE, Source<TestSource>(&test_source)); + service->AddObserver( + &idle_test_source, NOTIFY_ALL, Source<TestSource>(&test_source)); + + service->Notify(NOTIFY_IDLE, + Source<TestSource>(&test_source), + NotificationService::NoDetails()); + EXPECT_EQ(2, idle_test_source.notification_count()); + + service->RemoveObserver( + &idle_test_source, NOTIFY_IDLE, Source<TestSource>(&test_source)); + + service->Notify(NOTIFY_IDLE, + Source<TestSource>(&test_source), + NotificationService::NoDetails()); + EXPECT_EQ(3, idle_test_source.notification_count()); + + service->RemoveObserver( + &idle_test_source, NOTIFY_ALL, Source<TestSource>(&test_source)); + + service->Notify(NOTIFY_IDLE, + Source<TestSource>(&test_source), + NotificationService::NoDetails()); + EXPECT_EQ(3, idle_test_source.notification_count()); +} diff --git a/chrome/common/notification_source.h b/chrome/common/notification_source.h new file mode 100644 index 0000000..31077e9 --- /dev/null +++ b/chrome/common/notification_source.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. + +// This file defines the type used to provide sources for NotificationService +// notifications. + +#ifndef CHROME_COMMON_NOTIFICATION_SOURCE_H__ +#define CHROME_COMMON_NOTIFICATION_SOURCE_H__ + +#include "base/basictypes.h" + +// Do not declare a NotificationSource directly--use either +// "Source<sourceclassname>(sourceclasspointer)" or +// NotificationService::AllSources(). +class NotificationSource { + public: + ~NotificationSource() {} + + // NotificationSource can be used as the index for a map; this method + // returns the pointer to the current source as an identifier, for use as a + // map index. + uintptr_t map_key() const { return reinterpret_cast<uintptr_t>(ptr_); } + + bool operator!=(const NotificationSource& other) const { + return ptr_ != other.ptr_; + } + bool operator==(const NotificationSource& other) const { + return ptr_ == other.ptr_; + } + + protected: + NotificationSource(void* ptr) : ptr_(ptr) {} + NotificationSource(const NotificationSource& other) : ptr_(other.ptr_) { } + + void* ptr_; + + private: + void operator=(const NotificationSource&); +}; + +template <class T> +class Source : public NotificationSource { + public: + Source(T* ptr) : NotificationSource(ptr) {} + + Source(const NotificationSource& other) + : NotificationSource(other) {} + + T* operator->() const { return ptr(); } + T* ptr() const { return static_cast<T*>(ptr_); } +}; + +#endif // CHROME_COMMON_NOTIFICATION_SOURCE_H__ diff --git a/chrome/common/notification_types.h b/chrome/common/notification_types.h new file mode 100644 index 0000000..42c1804 --- /dev/null +++ b/chrome/common/notification_types.h @@ -0,0 +1,456 @@ +// 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 describes various types used to describe and filter notifications +// that pass through the NotificationService. + +#ifndef CHROME_COMMON_NOTIFICATION_TYPES_H__ +#define CHROME_COMMON_NOTIFICATION_TYPES_H__ + +enum NotificationType { + // Special signal value to represent an interest in all notifications. + // Not valid when posting a notification. + NOTIFY_ALL = 0, + + // The app is done processing user actions, now is a good time to do + // some background work. + NOTIFY_IDLE, + + // Means that the app has just started doing something in response + // to a user action, and that background processes shouldn't run if avoidable. + NOTIFY_BUSY, + + // A content load is starting. The source will be a + // Source<NavigationController> corresponding to the tab + // in which the load is occurring. No details are + // expected for this notification. + NOTIFY_LOAD_START, + + // A content load has stopped. The source will be a + // Source<NavigationController> corresponding to the tab + // in which the load is occurring. Details in the form of a + // LoadNotificationDetails object are optional. + NOTIFY_LOAD_STOP, + + // A NavigationController's state has changed (a new pending load has been + // added, a load has committed, etc.). + NOTIFY_NAVIGATION_STATE_CHANGED, + + // A frame is staring a provisional load. The source is a + // Source<NavigationController> corresponding to the tab in which the load + // occurs. Details is a bool specifying if the load occurs in the main + // frame (or a sub-frame if false). + NOTIFY_FRAME_PROVISIONAL_LOAD_START, + + // Content was loaded from an in-memory cache. The source will be a + // Source<NavigationController> corresponding to the tab + // in which the load occurred. Details in the form of a + // LoadFromMemoryCacheDetails object are provided. + NOTIFY_LOAD_FROM_MEMORY_CACHE, + + // A provisional content load has committed. The source will be a + // Source<NavigationController> corresponding to the tab in which the load + // occurred. Details in the form of a ProvisionalLoadDetails object are + // provided. + NOTIFY_FRAME_PROVISIONAL_LOAD_COMMITTED, + + // A provisional content load has failed with an error. The source will be a + // Source<NavigationController> corresponding to the tab + // in which the load occurred. Details in the form of a + // ProvisionalLoadDetails object are provided. + NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR, + + // A response has been received for a resource request. The source will be a + // Source<NavigationController> corresponding to the tab in which the request + // was issued. Details in the form of a ResourceRequestDetails object are + // provided. + NOTIFY_RESOURCE_RESPONSE_STARTED, + + // The response to a resource request has completed. The source will be a + // Source<NavigationController> corresponding to the tab in which the request + // was issued. Details in the form of a ResourceRequestDetails object are + // provided. + NOTIFY_RESOURCE_RESPONSE_COMPLETED, + + // A redirect was received while requesting a resource. The source will be a + // Source<NavigationController> corresponding to the tab in which the request + // was issued. Details in the form of a ResourceRedirectDetails are provided. + NOTIFY_RESOURCE_RECEIVED_REDIRECT, + + // This message is sent after a window has been opened. The source is + // a Source<Browser> with a pointer to the new window. + // No details are expected. + NOTIFY_BROWSER_OPENED, + + // This message is sent after a window has been closed. The source is + // a Source<Browser> with a pointer to the closed window. + // Details is a boolean that if true indicates that the application will be + // closed as a result of this browser window closure (i.e. this was the last + // opened browser window). Note that the boolean pointed to by Details is + // only valid for the duration of this call. + NOTIFY_BROWSER_CLOSED, + + // This notification is sent after a tab has been appended to the tab_strip. + // The source is a Source<NavigationController> with a pointer to + // controller for the added tab. + // The details is a Detail<Browser> with a pointer to the window + // that the tab is being added to. + NOTIFY_TAB_APPENDED, + + // This message is sent before a tab has been closed. The source is + // a Source<NavigationController> with a pointer to the controller for the + // closed tab. + // No details are expected. + // + // See also NOTIFY_TAB_CLOSED. + NOTIFY_TAB_CLOSING, + + // Notification that a tab has been closed. The source is the + // NavigationController with no details. + NOTIFY_TAB_CLOSED, + + // This notification is sent when a render view host has connected to a + // renderer process. The source is a Source<WebContents> with a pointer to + // the WebContents. A NOTIFY_WEB_CONTENTS_DISCONNECTED notification is + // guaranteed before the source pointer becomes junk. + // No details are expected. + NOTIFY_WEB_CONTENTS_CONNECTED, + + // This notification is sent when a WebContents swaps its render view host + // with another one, possibly changing processes. The source is a + // Source<WebContents> with a pointer to the WebContents. A + // NOTIFY_WEB_CONTENTS_DISCONNECTED notification is guaranteed before the + // source pointer becomes junk. + // No details are expected. + NOTIFY_WEB_CONTENTS_SWAPPED, + + // This message is sent after a WebContents is disconnected from the + // renderer process. + // The source is a Source<WebContents> with a pointer to the WebContents + // (the pointer is usable). + // No details are expected. + NOTIFY_WEB_CONTENTS_DISCONNECTED, + + // This notification is sent when a plugin process host has connected to a + // plugin process. There is no usable source, since it is sent from an + // ephemeral task; register for AllSources() to receive this notification. + // The details are in a Details<PluginProcessInfo> with a pointer to + // a plug-in process info for the plugin, that is only valid for the time of + // the notification (don't keep this pointer around, make a copy of the object + // if you need to keep it). + NOTIFY_PLUGIN_PROCESS_HOST_CONNECTED, + + // This message is sent after a PluginProcessHost is disconnected from the + // plugin process. There is no usable source, since it is sent from an + // ephemeral task; register for AllSources() to receive this notification. + // The details are in a Details<PluginProcessInfo> with a pointer to + // a plug-in process info for the plugin, that is only valid for the time of + // the notification (don't keep this pointer around, make a copy of the object + // if you need to keep it). + NOTIFY_PLUGIN_PROCESS_HOST_DISCONNECTED, + + // This message is sent when a plugin process disappears unexpectedly. + // There is no usable source, since it is sent from an + // ephemeral task; register for AllSources() to receive this notification. + // The details are in a Details<PluginProcessInfo> with a pointer to + // a plug-in process info for the plugin, that is only valid for the time of + // the notification (don't keep this pointer around, make a copy of the object + // if you need to keep it). + NOTIFY_PLUGIN_PROCESS_CRASHED, + + // This message indicates that an instance of a particular plugin was + // created in a page. (If one page contains several regions rendered + // by the same plugin, this notification will occur once for each region + // during the page load.) + // There is no usable source, since it is sent from an + // ephemeral task; register for AllSources() to receive this notification. + // The details are in a Details<PluginProcessInfo> with a pointer to + // a plug-in process info for the plugin, that is only valid for the time of + // the notification (don't keep this pointer around, make a copy of the object + // if you need to keep it). + NOTIFY_PLUGIN_INSTANCE_CREATED, + + // This notification is sent when the result of a find-in-page search is + // available with the browser process. The source is a Source<TabContents> + // with a pointer to the WebContents. Details encompass a + // FindNotificationDetail object that tells whether the match was + // found or not found. + NOTIFY_FIND_RESULT_AVAILABLE, + + // Sent when a URL that has been typed has been added or modified. This is + // used by the in-memory URL database (used by autocomplete) to track changes + // to the main history system. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLsModifiedDetails that lists the modified or + // added URLs. + NOTIFY_HISTORY_TYPED_URLS_MODIFIED, + + // Sent when the user visits a URL. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLsMo. + NOTIFY_HISTORY_URL_VISITED, + + // Sent when one or more URLs are deleted. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLsDeletedDetails that lists the deleted URLs. + NOTIFY_HISTORY_URLS_DELETED, + + // This is sent when the "starred" button is clicked, toggling the + // starredness of a tab's current URL. The source is a Profile and the + // details is history::URLsStarredDetails that contains the list of URLs and + // whether they were starred or unstarred. + NOTIFY_URLS_STARRED, + + // Sent when the bookmark bar model finishes loading. This source is the + // Profile, and the details aren't used. + NOTIFY_BOOKMARK_MODEL_LOADED, + + // This is sent when the user creates a starred entry or changes the title, + // visual order or parent of a star entry. The source is the Profile and the + // details a StarredEntryDetails that identifies the entry that changed. + NOTIFY_STAR_ENTRY_CHANGED, + + // This is sent when a starred group is created. The source is the Profile, + // and the details a StarredEntryDetails that identifies the entry that + // was created. + NOTIFY_STAR_GROUP_CREATED, + + // This is sent when a login prompt is shown. The source is the + // Source<NavigationController> for the tab in which the prompt is shown. + // Details are a LoginNotificationDetails which provide the LoginHandler + // that should be given authentication. + NOTIFY_AUTH_NEEDED, + + // This is sent when authentication credentials have been supplied (either + // by the user or by an automation service), but before we've actually + // received another response from the server. The source is the + // Source<NavigationController> for the tab in which the prompt was shown. + // No details are expected. + NOTIFY_AUTH_SUPPLIED, + + // This is sent when the user does a gesture resulting in a noteworthy + // action taking place. This is typically used for logging. The + // source is the profile, and the details is a wstring identifying the action. + NOTIFY_USER_ACTION, + + // This message is sent after a constrained window has been closed. The + // source is a Source<ConstrainedWindow> with a pointer to the closed child + // window. (The pointer isn't usable, except for identification.) No details + // are expected. + NOTIFY_CWINDOW_CLOSED, + + // This is sent whenever a feed's unread count has changed. + // This happens in two cases: + // 1) we've polled the feed and found new entries + // 2) the user has visited a page that had unread entries, so the unread + // count is now reset to zero. + // The source is the Profile and the details is the FeedStatus object. + NOTIFY_FEED_UNREADS_CHANGED, + + // Sent when a history service is created. The source is the profile that the + // history service belongs to, and the details is the pointer to the newly + // created HistoryService object. + NOTIFY_HISTORY_CREATED, + + // Indicates that the new page tab has finished loading. This is + // used for performance testing to see how fast we can load it after startup, + // and is only called once for the lifetime of the browser. The source is + // unused. Details is an integer: the number of milliseconds elapsed between + // starting and finishing all painting. + NOTIFY_INITIAL_NEW_TAB_UI_LOAD, + + // Indicates that a render process has terminated. The source will be the + // RenderProcessHost that corresponds to the process, and the details is a + // bool specifying whether the termination was expected, i.e. if false it + // means the process crashed. + NOTIFY_RENDERER_PROCESS_TERMINATED, + + // Indicates that a render process has become unresponsive for a period of + // time. The source will be the RenderWidgetHost that corresponds to the hung + // view, and no details are expected. + NOTIFY_RENDERER_PROCESS_HANG, + + // Indicates that a top window has been closed. The source is the HWND that + // was closed, no details are expected. + NOTIFY_WINDOW_CLOSED, + + // Indicates that a render process is created in the sandbox. The source + // will be the RenderProcessHost that corresponds to the created process + // and the detail is a bool telling us if the process got created on the + // sandbox desktop or not. + NOTIFY_RENDERER_PROCESS_IN_SBOX, + + // Notification triggered when a web application has been installed or + // uninstalled. Any application view should reload its data. + // The source is the profile. No details are provided. + NOTIFY_WEB_APP_INSTALL_CHANGED, + + // Download start and stop notifications. Stop notifications can occur on both + // normal completion or via a cancel operation. + NOTIFY_DOWNLOAD_START, + NOTIFY_DOWNLOAD_STOP, + + // Used to monitor web cache usage by notifying whenever the CacheManagerHost + // observes new UsageStats. The source will be the RenderProcessHost that + // corresponds to the new statistics. Details are a UsageStats object sent + // by the renderer, and should be copied - ptr not guaranteed to be valid + // after the notification. + NOTIFY_WEB_CACHE_STATS_OBSERVED, + + // Notification that all URLs with a particular host have been removed. + // The source is the profile the host was deleted in, and the details is a + // history::HostDeletedDetails struct. + NOTIFY_HOST_DELETED_FROM_HISTORY, + + // Notification that a view was removed from a view hierarchy. The source is + // the view, the details is the parent view. + NOTIFY_VIEW_REMOVED, + + // Notification that the TemplateURLModel has finished loading from the + // database. The source is the TemplateURLModel, and the details are + // NoDetails. + TEMPLATE_URL_MODEL_LOADED, + + // Notification from WebContents that we have received a response from + // the renderer after using the dom inspector. + NOTIFY_DOM_INSPECT_ELEMENT_RESPONSE, + + // Notification from WebContents that we have received a response from + // the renderer in response to a dom automation controller action. + NOTIFY_DOM_OPERATION_RESPONSE, + + // Notification that a page's presentation index has changed (this is the + // index that the user has manually overridden in the most visited view). + // The source is the profile, and the details is a + // history::PresentationIndexDetails. + NOTIFY_PAGE_PRESENTATION_INDEX_CHANGED, + + // This is sent when an item of the Omnibox popup is selected. The source is + // the profile. + NOTIFY_OMNIBOX_OPENED_URL, + + // This is sent when the users preference for when the bookmark bar should + // be shown changes. The source is the profile, and the details are + // NoDetails. + NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, + + // This is sent when an interstitial page showing in a WebContents is closed + // (as the result of a navigation to another page). The source is the + // WebContents the interstitial page is in. + // Note that you should not initiate a navigation as part of the processing of + // this notification, since this notification may be triggered as part of the + // destruction of the tab contents (the navigation controller would reuse + // the tab contents right before it would be destroyed). + NOTIFY_INTERSTITIAL_PAGE_CLOSED, + + // This is sent when an externally hosted tab is created. The details contain + // the ExternalTabContainer that contains the tab + NOTIFY_EXTERNAL_TAB_CREATED, + + // This is sent when an externally hosted tab is closed. + // No details are expected. + NOTIFY_EXTERNAL_TAB_CLOSED, + + // Notification from a PrintedDocument that it has been updated. It may be + // that a printed page has just been generated or that the document's number + // of pages has been calculated. Details is the new page or NULL if only the + // number of pages in the document has been updated. + NOTIFY_PRINTED_DOCUMENT_UPDATED, + + // Notification from PrintJob that an event occured. It can be that a page + // finished printing or that the print job failed. Details is + // PrintJob::EventDetails. + NOTIFY_PRINT_JOB_EVENT, + + // This is sent when network interception is disabled for a plugin, or the + // plugin is unloaded. This should only be sent/received on the browser IO + // thread or the plugin thread. The source is the plugin that is disabling + // interception. No details are expected. + NOTIFY_CHROME_PLUGIN_UNLOADED, + + // This is sent to a pref observer when a pref is changed. + NOTIFY_PREF_CHANGED, + + // This is sent to notify that the RenderViewHost displayed in a WebContents + // has changed. Source is the WebContents for which the change happened, + // details is the previous RenderViewHost (can be NULL when the first + // RenderViewHost is set). + NOTIFY_RENDER_VIEW_HOST_CHANGED, + + // This notification is sent when a TabContents is being hidden, e.g. due to + // switching away from this tab. The source is a Source<TabContents>. + NOTIFY_TAB_CONTENTS_HIDDEN, + + // This notification is sent when a TabContents is being destroyed. Any object + // holding a reference to a TabContents can listen to that notification to + // properly reset the reference. The source is a Source<TabContents>. + NOTIFY_TAB_CONTENTS_DESTROYED, + + // Sent by the autocomplete edit when it is destroyed. + NOTIFY_AUTOCOMPLETE_EDIT_DESTROYED, + + // Sent by history when the favicon of a starred URL changes. In the future, + // we may want a notification for all URLs so that the history view can stay + // up-to-date. In that case, we should change this notification rather than + // adding a new one for non-starred favicons. + // The source is the profile, and the details is + // history::FavIconChangeDetails (see history_notifications.h). + NOTIFY_STARRED_FAVICON_CHANGED, + + // Sent on the browser IO thread when an URLRequestContext is released by its + // owning Profile. The source is a pointer to the URLRequestContext. + NOTIFY_URL_REQUEST_CONTEXT_RELEASED, + + // Sent when the main Google URL has been updated. Some services cache this + // value and need to update themselves when it changes. See + // google_util::GetGoogleURLAndUpdateIfNecessary(). + NOTIFY_GOOGLE_URL_UPDATED, + + // Sent when WM_ENDSESSION has been received, after the browsers have been + // closed but before browser process has been shutdown. The source/details + // are all source and no details. + NOTIFY_SESSION_END, + + // Sent when the bookmark bubble is shown for a particular URL. The source + // is the profile, the details the URL. + NOTIFY_BOOKMARK_BUBBLE_SHOWN, + + // Sent when the bookmark bubble hides. The source is the profile, the + // details unused. + NOTIFY_BOOKMARK_BUBBLE_HIDDEN, + + // Used to determine the number of notification types. Not valid as + // a type parameter when registering for or posting notifications. + NOTIFICATION_TYPE_COUNT +}; + +#endif // CHROME_COMMON_NOTIFICATION_TYPES_H__ diff --git a/chrome/common/os_exchange_data.cc b/chrome/common/os_exchange_data.cc new file mode 100644 index 0000000..05984b5 --- /dev/null +++ b/chrome/common/os_exchange_data.cc @@ -0,0 +1,703 @@ +// 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 <shlobj.h> + +#include "chrome/common/os_exchange_data.h" + +#include "base/clipboard_util.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/pickle.h" +#include "base/scoped_handle.h" +#include "base/string_util.h" +#include "net/base/net_util.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/stl_util-inl.h" +#include "chrome/common/win_util.h" +#include "googleurl/src/gurl.h" +#include "generated_resources.h" + +// Creates a new STGMEDIUM object to hold the specified text. The caller +// owns the resulting object. +static STGMEDIUM* GetStorageForWString(const std::wstring& data); +static STGMEDIUM* GetStorageForString(const std::string& data); +// Creates the contents of an Internet Shortcut file for the given URL. +static void GetInternetShortcutFileContents(const GURL& url, std::string* data); +// Creates a valid file name given a suggested title and URL. +static void CreateValidFileNameFromTitle(const GURL& url, + const std::wstring& title, + std::wstring* validated); +// Creates a File Descriptor for the creation of a file to the given URL and +// returns a handle to it. +static STGMEDIUM* GetStorageForFileDescriptor( + const std::wstring& valid_file_name); + +/////////////////////////////////////////////////////////////////////////////// +// FormatEtcEnumerator + +// +// This object implements an enumeration interface. The existence of an +// implementation of this interface is exposed to clients through +// OSExchangeData's EnumFormatEtc method. Our implementation is nobody's +// business but our own, so it lives in this file. +// +// This Windows API is truly a gem. It wants to be an enumerator but assumes +// some sort of sequential data (why not just use an array?). See comments +// throughout. +// +class FormatEtcEnumerator : public IEnumFORMATETC { + public: + FormatEtcEnumerator(OSExchangeData::StoredData::const_iterator begin, + OSExchangeData::StoredData::const_iterator end); + ~FormatEtcEnumerator(); + + // IEnumFORMATETC implementation: + HRESULT __stdcall Next( + ULONG count, FORMATETC* elements_array, ULONG* elements_fetched); + HRESULT __stdcall Skip(ULONG skip_count); + HRESULT __stdcall Reset(); + HRESULT __stdcall Clone(IEnumFORMATETC** clone); + + // IUnknown implementation: + HRESULT __stdcall QueryInterface(const IID& iid, void** object); + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + + private: + // This can only be called from |CloneFromOther|, since it initializes the + // contents_ from the other enumerator's contents. + FormatEtcEnumerator() : ref_count_(0) { + } + + // Clone a new FormatEtc from another instance of this enumeration. + static FormatEtcEnumerator* CloneFromOther(const FormatEtcEnumerator* other); + + private: + // We are _forced_ to use a vector as our internal data model as Windows' + // retarded IEnumFORMATETC API assumes a deterministic ordering of elements + // through methods like Next and Skip. This exposes the underlying data + // structure to the user. Bah. + std::vector<FORMATETC*> contents_; + + // The cursor of the active enumeration - an index into |contents_|. + int cursor_; + + LONG ref_count_; + + DISALLOW_EVIL_CONSTRUCTORS(FormatEtcEnumerator); +}; + +// Safely makes a copy of all of the relevant bits of a FORMATETC object. +static void CloneFormatEtc(FORMATETC* source, FORMATETC* clone) { + *clone = *source; + if (source->ptd) { + source->ptd = + static_cast<DVTARGETDEVICE*>(CoTaskMemAlloc(sizeof(DVTARGETDEVICE))); + *(clone->ptd) = *(source->ptd); + } +} + +FormatEtcEnumerator::FormatEtcEnumerator( + OSExchangeData::StoredData::const_iterator start, + OSExchangeData::StoredData::const_iterator end) + : ref_count_(0), cursor_(0) { + // Copy FORMATETC data from our source into ourselves. + while (start != end) { + FORMATETC* format_etc = new FORMATETC; + CloneFormatEtc(&(*start)->format_etc, format_etc); + contents_.push_back(format_etc); + ++start; + } +} + +FormatEtcEnumerator::~FormatEtcEnumerator() { + STLDeleteContainerPointers(contents_.begin(), contents_.end()); +} + +STDMETHODIMP FormatEtcEnumerator::Next( + ULONG count, FORMATETC* elements_array, ULONG* elements_fetched) { + // MSDN says |elements_fetched| is allowed to be NULL if count is 1. + if (!elements_fetched) + DCHECK(count == 1); + + // This method copies count elements into |elements_array|. + int index = 0; + while (cursor_ < static_cast<int>(contents_.size()) && + static_cast<ULONG>(index) < count) { + CloneFormatEtc(contents_.at(cursor_), &elements_array[index]); + ++cursor_; + ++index; + } + // The out param is for how many we actually copied. + if (elements_fetched) + *elements_fetched = index; + + // If the two don't agree, then we fail. + return index == count ? S_OK : S_FALSE; +} + +STDMETHODIMP FormatEtcEnumerator::Skip(ULONG skip_count) { + cursor_ += skip_count; + // MSDN implies it's OK to leave the enumerator trashed. + // "Whatever you say, boss" + return cursor_ <= static_cast<int>(contents_.size()) ? S_OK : S_FALSE; +} + +STDMETHODIMP FormatEtcEnumerator::Reset() { + cursor_ = 0; + return S_OK; +} + +STDMETHODIMP FormatEtcEnumerator::Clone(IEnumFORMATETC** clone) { + // Clone the current enumerator in its exact state, including cursor. + FormatEtcEnumerator* e = CloneFromOther(this); + e->AddRef(); + *clone = e; + return S_OK; +} + +STDMETHODIMP FormatEtcEnumerator::QueryInterface(const IID& iid, + void** object) { + *object = NULL; + if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IEnumFORMATETC)) { + *object = this; + } else { + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +ULONG FormatEtcEnumerator::AddRef() { + return InterlockedIncrement(&ref_count_); +} + +ULONG FormatEtcEnumerator::Release() { + if (InterlockedDecrement(&ref_count_) == 0) { + ULONG copied_refcnt = ref_count_; + delete this; + return copied_refcnt; + } + return ref_count_; +} + +// static +FormatEtcEnumerator* FormatEtcEnumerator::CloneFromOther( + const FormatEtcEnumerator* other) { + FormatEtcEnumerator* e = new FormatEtcEnumerator; + // Copy FORMATETC data from our source into ourselves. + std::vector<FORMATETC*>::const_iterator start = other->contents_.begin(); + while (start != other->contents_.end()) { + FORMATETC* format_etc = new FORMATETC; + CloneFormatEtc(*start, format_etc); + e->contents_.push_back(format_etc); + ++start; + } + // Carry over + e->cursor_ = other->cursor_; + return e; +} + +/////////////////////////////////////////////////////////////////////////////// +// OSExchangeData, public: + +// static +bool OSExchangeData::HasPlainTextURL(IDataObject* source) { + std::wstring plain_text; + return (ClipboardUtil::GetPlainText(source, &plain_text) && + !plain_text.empty() && GURL(plain_text).is_valid()); +} + +// static +bool OSExchangeData::GetPlainTextURL(IDataObject* source, GURL* url) { + std::wstring plain_text; + if (ClipboardUtil::GetPlainText(source, &plain_text) && + !plain_text.empty()) { + GURL gurl(plain_text); + if (gurl.is_valid()) { + *url = gurl; + return true; + } + } + return false; +} + +OSExchangeData::OSExchangeData() + : ref_count_(0) { +} + +OSExchangeData::OSExchangeData(IDataObject* source) + : ref_count_(0) { + source_object_ = source; +} + +OSExchangeData::~OSExchangeData() { + STLDeleteContainerPointers(contents_.begin(), contents_.end()); +} + +void OSExchangeData::SetString(const std::wstring& data) { + STGMEDIUM* storage = GetStorageForWString(data); + contents_.push_back(new StoredDataInfo(CF_UNICODETEXT, storage)); + + // Also add plain text. + storage = GetStorageForString(WideToUTF8(data)); + contents_.push_back(new StoredDataInfo(CF_TEXT, storage)); +} + +void OSExchangeData::SetURL(const GURL& url, const std::wstring& title) { + // NOTE WELL: + // Every time you change the order of the first two CLIPFORMATS that get + // added here, you need to update the EnumerationViaCOM test case in + // the _unittest.cc file to reflect the new arrangement otherwise that test + // will fail! It assumes an insertion order. + + // Add text/x-moz-url for drags from Firefox + std::wstring x_moz_url_str = UTF8ToWide(url.spec()); + x_moz_url_str += '\n'; + x_moz_url_str += title; + STGMEDIUM* storage = GetStorageForWString(x_moz_url_str); + contents_.push_back(new StoredDataInfo( + ClipboardUtil::GetMozUrlFormat()->cfFormat, storage)); + + // Add a .URL shortcut file for dragging to Explorer. + std::wstring valid_file_name; + CreateValidFileNameFromTitle(url, title, &valid_file_name); + std::string shortcut_url_file_contents; + GetInternetShortcutFileContents(url, &shortcut_url_file_contents); + SetFileContents(valid_file_name, shortcut_url_file_contents); + + // Add a UniformResourceLocator link for apps like IE and Word. + storage = GetStorageForWString(UTF8ToWide(url.spec())); + contents_.push_back(new StoredDataInfo( + ClipboardUtil::GetUrlWFormat()->cfFormat, storage)); + storage = GetStorageForString(url.spec()); + contents_.push_back(new StoredDataInfo( + ClipboardUtil::GetUrlFormat()->cfFormat, storage)); + + // TODO(beng): (http://b/1085501) add CF_HTML... + + // Also add text representations (these should be last since they're the + // least preferable). + storage = GetStorageForWString(UTF8ToWide(url.spec())); + contents_.push_back(new StoredDataInfo(CF_UNICODETEXT, storage)); + storage = GetStorageForString(url.spec()); + contents_.push_back(new StoredDataInfo(CF_TEXT, storage)); +} + +void OSExchangeData::SetFilename(const std::wstring& full_path) { + const size_t drop_size = sizeof(DROPFILES); + const size_t bytes = drop_size + (full_path.length() + 2) * sizeof(wchar_t); + HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes); + if (!hdata) + return; + + ScopedHGlobal<DROPFILES> locked_mem(hdata); + DROPFILES* drop_files = locked_mem.get(); + drop_files->pFiles = sizeof(DROPFILES); + drop_files->fWide = TRUE; + wchar_t* data = reinterpret_cast<wchar_t*>((BYTE*)(drop_files) + drop_size); + const size_t copy_size = (full_path.length() + 1) * sizeof(wchar_t); + memcpy(data, full_path.c_str(), copy_size); + data[full_path.length() + 1] = L'\0'; // Double NULL + + // Set up the STGMEDIUM + STGMEDIUM* storage = new STGMEDIUM; + storage->tymed = TYMED_HGLOBAL; + storage->hGlobal = drop_files; + storage->pUnkForRelease = NULL; + + // Set up the StoredDataInfo + StoredDataInfo* info = new StoredDataInfo(CF_HDROP, storage); + contents_.push_back(info); +} + +void OSExchangeData::SetPickledData(CLIPFORMAT format, const Pickle& data) { + STGMEDIUM* storage = GetStorageForString( + std::string(static_cast<const char *>(data.data()), + static_cast<size_t>(data.size()))); + contents_.push_back(new StoredDataInfo(format, storage)); +} + +void OSExchangeData::SetFileContents(const std::wstring& filename, + const std::string& file_contents) { + // Add CFSTR_FILEDESCRIPTOR + STGMEDIUM* storage = GetStorageForFileDescriptor(filename); + contents_.push_back(new StoredDataInfo( + ClipboardUtil::GetFileDescriptorFormat()->cfFormat, storage)); + + // Add CFSTR_FILECONTENTS + storage = GetStorageForString(file_contents); + contents_.push_back(new StoredDataInfo( + ClipboardUtil::GetFileContentFormatZero()->cfFormat, storage)); +} + +void OSExchangeData::SetCFHtml(const std::wstring& cf_html) { + STGMEDIUM* storage = GetStorageForString(WideToUTF8(cf_html)); + contents_.push_back(new StoredDataInfo( + ClipboardUtil::GetHtmlFormat()->cfFormat, storage)); +} + +bool OSExchangeData::GetString(std::wstring* data) const { + return ClipboardUtil::GetPlainText(source_object_, data); +} + +bool OSExchangeData::GetURLAndTitle(GURL* url, std::wstring* title) const { + std::wstring url_str; + bool success = ClipboardUtil::GetUrl(source_object_, &url_str, title); + if (success) { + GURL test_url(url_str); + if (test_url.is_valid()) { + *url = test_url; + return true; + } + } else if (GetPlainTextURL(source_object_, url)) { + title->clear(); + return true; + } + return false; +} + +bool OSExchangeData::GetFilename(std::wstring* full_path) const { + std::vector<std::wstring> filenames; + bool success = ClipboardUtil::GetFilenames(source_object_, &filenames); + if (success) + full_path->assign(filenames[0]); + return success; +} + +bool OSExchangeData::GetPickledData(CLIPFORMAT format, Pickle* data) const { + DCHECK(data); + FORMATETC format_etc = + { format, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + bool success = false; + STGMEDIUM medium; + if (SUCCEEDED(source_object_->GetData(&format_etc, &medium))) { + if (medium.tymed & TYMED_HGLOBAL) { + ScopedHGlobal<char> c_data(medium.hGlobal); + DCHECK(c_data.Size() > 0); + // Need to subtract 1 as SetPickledData adds an extra byte to the end. + *data = Pickle(c_data.get(), static_cast<int>(c_data.Size() - 1)); + success = true; + } + ReleaseStgMedium(&medium); + } + return success; +} + +bool OSExchangeData::GetFileContents(std::wstring* filename, + std::string* file_contents) const { + return ClipboardUtil::GetFileContents(source_object_, filename, + file_contents); +} + +bool OSExchangeData::GetCFHtml(std::wstring* cf_html) const { + return ClipboardUtil::GetCFHtml(source_object_, cf_html); +} + +bool OSExchangeData::HasString() const { + return ClipboardUtil::HasPlainText(source_object_); +} + +bool OSExchangeData::HasURL() const { + return (ClipboardUtil::HasUrl(source_object_) || + HasPlainTextURL(source_object_)); +} + +bool OSExchangeData::HasFile() const { + return ClipboardUtil::HasFilenames(source_object_); +} + +bool OSExchangeData::HasFormat(CLIPFORMAT format) const { + FORMATETC format_etc = + { format, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + return (source_object_->QueryGetData(&format_etc) == S_OK); +} + +/////////////////////////////////////////////////////////////////////////////// +// OSExchangeData, IDataObject implementation: + +// The following function, DuplicateMedium, is derived from WCDataObject.cpp +// in the WebKit source code. This is the license information for the file: +/* + * 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. + */ +static void DuplicateMedium(CLIPFORMAT source_clipformat, + STGMEDIUM* source, + STGMEDIUM* destination) { + switch (source->tymed) { + case TYMED_HGLOBAL: + destination->hGlobal = + static_cast<HGLOBAL>(OleDuplicateData( + source->hGlobal, source_clipformat, 0)); + break; + case TYMED_MFPICT: + destination->hMetaFilePict = + static_cast<HMETAFILEPICT>(OleDuplicateData( + source->hMetaFilePict, source_clipformat, 0)); + break; + case TYMED_GDI: + destination->hBitmap = + static_cast<HBITMAP>(OleDuplicateData( + source->hBitmap, source_clipformat, 0)); + break; + case TYMED_ENHMF: + destination->hEnhMetaFile = + static_cast<HENHMETAFILE>(OleDuplicateData( + source->hEnhMetaFile, source_clipformat, 0)); + break; + case TYMED_FILE: + destination->lpszFileName = + static_cast<LPOLESTR>(OleDuplicateData( + source->lpszFileName, source_clipformat, 0)); + break; + case TYMED_ISTREAM: + destination->pstm = source->pstm; + destination->pstm->AddRef(); + break; + case TYMED_ISTORAGE: + destination->pstg = source->pstg; + destination->pstg->AddRef(); + break; + } + + destination->tymed = source->tymed; + destination->pUnkForRelease = source->pUnkForRelease; + if (destination->pUnkForRelease) + destination->pUnkForRelease->AddRef(); +} + +HRESULT OSExchangeData::GetData(FORMATETC* format_etc, STGMEDIUM* medium) { + StoredData::const_iterator iter = contents_.begin(); + while (iter != contents_.end()) { + if ((*iter)->format_etc.cfFormat == format_etc->cfFormat) { + DuplicateMedium((*iter)->format_etc.cfFormat, (*iter)->medium, medium); + return S_OK; + } + ++iter; + } + + return DV_E_FORMATETC; +} + +HRESULT OSExchangeData::GetDataHere(FORMATETC* format_etc, STGMEDIUM* medium) { + return DATA_E_FORMATETC; +} + +HRESULT OSExchangeData::QueryGetData(FORMATETC* format_etc) { + StoredData::const_iterator iter = contents_.begin(); + while (iter != contents_.end()) { + if ((*iter)->format_etc.cfFormat == format_etc->cfFormat) + return S_OK; + ++iter; + } + return DV_E_FORMATETC; +} + +HRESULT OSExchangeData::GetCanonicalFormatEtc( + FORMATETC* format_etc, FORMATETC* result) { + format_etc->ptd = NULL; + return E_NOTIMPL; +} + +HRESULT OSExchangeData::SetData( + FORMATETC* format_etc, STGMEDIUM* medium, BOOL should_release) { + STGMEDIUM* local_medium = new STGMEDIUM; + if (should_release) { + *local_medium = *medium; + } else { + DuplicateMedium(format_etc->cfFormat, medium, local_medium); + } + + StoredDataInfo* info = + new StoredDataInfo(format_etc->cfFormat, local_medium); + info->medium->tymed = format_etc->tymed; + info->owns_medium = !!should_release; + contents_.push_back(info); + + return S_OK; +} + +HRESULT OSExchangeData::EnumFormatEtc( + DWORD direction, IEnumFORMATETC** enumerator) { + if (direction == DATADIR_GET) { + FormatEtcEnumerator* e = + new FormatEtcEnumerator(contents_.begin(), contents_.end()); + e->AddRef(); + *enumerator = e; + return S_OK; + } + return E_NOTIMPL; +} + +HRESULT OSExchangeData::DAdvise( + FORMATETC* format_etc, DWORD advf, IAdviseSink* sink, DWORD* connection) { + return OLE_E_ADVISENOTSUPPORTED; +} + +HRESULT OSExchangeData::DUnadvise(DWORD connection) { + return OLE_E_ADVISENOTSUPPORTED; +} + +HRESULT OSExchangeData::EnumDAdvise(IEnumSTATDATA** enumerator) { + return OLE_E_ADVISENOTSUPPORTED; +} + +/////////////////////////////////////////////////////////////////////////////// +// OSExchangeData, IUnknown implementation: + +HRESULT OSExchangeData::QueryInterface(const IID& iid, void** object) { + *object = NULL; + if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDataObject)) { + *object = this; + } else { + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +ULONG OSExchangeData::AddRef() { + return InterlockedIncrement(&ref_count_); +} + +ULONG OSExchangeData::Release() { + if (InterlockedDecrement(&ref_count_) == 0) { + ULONG copied_refcnt = ref_count_; + delete this; + return copied_refcnt; + } + return ref_count_; +} + +/////////////////////////////////////////////////////////////////////////////// +// OSExchangeData, private: + +template<class T> +static HGLOBAL CopyStringToGlobalHandle(const T& payload) { + int bytes = static_cast<int>(payload.size() + 1) * sizeof(T::value_type); + HANDLE handle = GlobalAlloc(GPTR, bytes); + void* data = GlobalLock(handle); + size_t allocated = static_cast<size_t>(GlobalSize(handle)); + memcpy(data, payload.c_str(), allocated); + static_cast<T::value_type*>(data)[payload.size()] = '\0'; + GlobalUnlock(handle); + return handle; +} + +static STGMEDIUM* GetStorageForWString(const std::wstring& data) { + STGMEDIUM* storage = new STGMEDIUM; + storage->hGlobal = CopyStringToGlobalHandle<std::wstring>(data); + storage->tymed = TYMED_HGLOBAL; + storage->pUnkForRelease = NULL; + return storage; +} + +static STGMEDIUM* GetStorageForString(const std::string& data) { + STGMEDIUM* storage = new STGMEDIUM; + storage->hGlobal = CopyStringToGlobalHandle<std::string>(data); + storage->tymed = TYMED_HGLOBAL; + storage->pUnkForRelease = NULL; + return storage; +} + +static void GetInternetShortcutFileContents(const GURL& url, + std::string* data) { + DCHECK(data); + static const std::string kInternetShortcutFileStart = + "[InternetShortcut]\r\nURL="; + static const std::string kInternetShortcutFileEnd = + "\r\n"; + *data = kInternetShortcutFileStart + url.spec() + kInternetShortcutFileEnd; +} + +static void CreateValidFileNameFromTitle(const GURL& url, + const std::wstring& title, + std::wstring* validated) { + if (title.empty()) { + if (url.is_valid()) { + *validated = net_util::GetSuggestedFilename(url, std::wstring(), + std::wstring()); + } else { + // Nothing else can be done, just use a default. + *validated = l10n_util::GetString(IDS_UNTITLED_SHORTCUT_FILE_NAME); + } + } else { + *validated = title; + file_util::ReplaceIllegalCharacters(validated, '-'); + } + static const wchar_t extension[] = L".url"; + static const size_t max_length = MAX_PATH - arraysize(extension); + if (validated->size() > max_length) + validated->erase(max_length); + *validated += extension; +} + +static STGMEDIUM* GetStorageForFileDescriptor( + const std::wstring& valid_file_name) { + DCHECK(!valid_file_name.empty() && valid_file_name.size() + 1 <= MAX_PATH); + HANDLE handle = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); + FILEGROUPDESCRIPTOR* descriptor = + reinterpret_cast<FILEGROUPDESCRIPTOR*>(GlobalLock(handle)); + + descriptor->cItems = 1; + wcscpy_s(descriptor->fgd[0].cFileName, + valid_file_name.size() + 1, + valid_file_name.c_str()); + descriptor->fgd[0].dwFlags = FD_LINKUI; + + GlobalUnlock(handle); + + STGMEDIUM* storage = new STGMEDIUM; + storage->hGlobal = handle; + storage->tymed = TYMED_HGLOBAL; + storage->pUnkForRelease = NULL; + return storage; +} diff --git a/chrome/common/os_exchange_data.h b/chrome/common/os_exchange_data.h new file mode 100644 index 0000000..6a543a7 --- /dev/null +++ b/chrome/common/os_exchange_data.h @@ -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. + +#ifndef CHROME_COMMON_OS_EXCHANGE_DATA_H__ +#define CHROME_COMMON_OS_EXCHANGE_DATA_H__ + +#include <atlbase.h> +#include <objidl.h> +#include <vector> + +#include "base/basictypes.h" + +class GURL; +class Pickle; + +/////////////////////////////////////////////////////////////////////////////// +// +// OSExchangeData +// An object that holds interchange data to be sent out to OS services like +// clipboard, drag and drop, etc. This object exposes an API that clients can +// use to specify raw data and its high level type. This object takes care of +// translating that into something the OS can understand. +// +/////////////////////////////////////////////////////////////////////////////// +class OSExchangeData : public IDataObject { + public: + // Returns true if source has plain text that is a valid url. + static bool HasPlainTextURL(IDataObject* source); + + // Returns true if source has plain text that is a valid URL and sets url to + // that url. + static bool GetPlainTextURL(IDataObject* source, GURL* url); + + OSExchangeData(); + OSExchangeData(IDataObject* source); + virtual ~OSExchangeData(); + + // These functions add data to the OSExchangeData object of various Chrome + // types. The OSExchangeData object takes care of translating the data into + // a format suitable for exchange with the OS. + // NOTE WELL: Typically, a data object like this will contain only one of the + // following types of data. In cases where more data is held, the + // order in which these functions are called is _important_! + // ---> The order types are added to an OSExchangeData object controls + // the order of enumeration in our IEnumFORMATETC implementation! + // This comes into play when selecting the best (most preferable) + // data type for insertion into a DropTarget. + void SetString(const std::wstring& data); + // A URL can have an optional title in some exchange formats. + void SetURL(const GURL& url, const std::wstring& title); + // A full path to a file + void SetFilename(const std::wstring& full_path); + // Adds pickled data of the specified format. + void SetPickledData(CLIPFORMAT format, const Pickle& data); + // Adds the bytes of a file (CFSTR_FILECONTENTS and CFSTR_FILEDESCRIPTOR). + void SetFileContents(const std::wstring& filename, + const std::string& file_contents); + // Adds a snippet of Windows HTML (CF_HTML). + void SetCFHtml(const std::wstring& cf_html); + + // These functions retrieve data of the specified type. If data exists, the + // functions return and the result is in the out parameter. If the data does + // not exist, the out parameter is not touched. The out parameter cannot be + // NULL. + bool GetString(std::wstring* data) const; + bool GetURLAndTitle(GURL* url, std::wstring* title) const; + // Return the path of a file, if available. + bool GetFilename(std::wstring* full_path) const; + bool GetPickledData(CLIPFORMAT format, Pickle* data) const; + bool GetFileContents(std::wstring* filename, + std::string* file_contents) const; + bool GetCFHtml(std::wstring* cf_html) const; + + // Test whether or not data of certain types is present, without actually + // returning anything. + bool HasString() const; + bool HasURL() const; + bool HasURLTitle() const; + bool HasFile() const; + bool HasFormat(CLIPFORMAT format) const; + + // IDataObject implementation: + HRESULT __stdcall GetData(FORMATETC* format_etc, STGMEDIUM* medium); + HRESULT __stdcall GetDataHere(FORMATETC* format_etc, STGMEDIUM* medium); + HRESULT __stdcall QueryGetData(FORMATETC* format_etc); + HRESULT __stdcall GetCanonicalFormatEtc( + FORMATETC* format_etc, FORMATETC* result); + HRESULT __stdcall SetData( + FORMATETC* format_etc, STGMEDIUM* medium, BOOL should_release); + HRESULT __stdcall EnumFormatEtc( + DWORD direction, IEnumFORMATETC** enumerator); + HRESULT __stdcall DAdvise( + FORMATETC* format_etc, DWORD advf, IAdviseSink* sink, DWORD* connection); + HRESULT __stdcall DUnadvise(DWORD connection); + HRESULT __stdcall EnumDAdvise(IEnumSTATDATA** enumerator); + + // IUnknown implementation: + HRESULT __stdcall QueryInterface(const IID& iid, void** object); + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + + private: + // FormatEtcEnumerator only likes us for our StoredDataMap typedef. + friend class FormatEtcEnumerator; + + // Our internal representation of stored data & type info. + struct StoredDataInfo { + FORMATETC format_etc; + STGMEDIUM* medium; + bool owns_medium; + + StoredDataInfo(CLIPFORMAT cf, STGMEDIUM* a_medium) { + format_etc.cfFormat = cf; + format_etc.dwAspect = DVASPECT_CONTENT; + format_etc.lindex = -1; + format_etc.ptd = NULL; + format_etc.tymed = a_medium->tymed; + + owns_medium = true; + + medium = a_medium; + } + + ~StoredDataInfo() { + if (owns_medium) { + ReleaseStgMedium(medium); + delete medium; + } + } + }; + + typedef std::vector<StoredDataInfo*> StoredData; + StoredData contents_; + + CComPtr<IDataObject> source_object_; + + LONG ref_count_; + + DISALLOW_EVIL_CONSTRUCTORS(OSExchangeData); +}; + +#endif // #ifndef CHROME_COMMON_OS_EXCHANGE_DATA_H__ diff --git a/chrome/common/os_exchange_data_unittest.cc b/chrome/common/os_exchange_data_unittest.cc new file mode 100644 index 0000000..510e363 --- /dev/null +++ b/chrome/common/os_exchange_data_unittest.cc @@ -0,0 +1,369 @@ +// 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 <atlbase.h> +#include <shlobj.h> + +#include "base/pickle.h" +#include "base/ref_counted.h" +#include "base/scoped_handle.h" +#include "base/string_util.h" +#include "chrome/common/os_exchange_data.h" +#include "chrome/common/win_util.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +typedef testing::Test OSExchangeDataTest; + +// Test setting/getting using the OSExchangeData API +TEST(OSExchangeDataTest, StringDataGetAndSet) { + OSExchangeData* data = new OSExchangeData; + std::wstring input = L"I can has cheezburger?"; + data->SetString(input); + + OSExchangeData* data2 = new OSExchangeData(data); + std::wstring output; + EXPECT_TRUE(data2->GetString(&output)); + EXPECT_EQ(input, output); + std::string url_spec = "http://www.goats.com/"; + GURL url(url_spec); + std::wstring title; + EXPECT_FALSE(data2->GetURLAndTitle(&url, &title)); + // No URLs in |data|, so url should be untouched. + EXPECT_EQ(url_spec, url.spec()); + // data gets freed when data2 releases the ref on it + delete data2; +} + +// Test getting using the IDataObject COM API +TEST(OSExchangeDataTest, StringDataAccessViaCOM) { + OSExchangeData* data = new OSExchangeData; + std::wstring input = L"O hai googlz."; + data->SetString(input); + CComPtr<IDataObject> com_data(data); + + FORMATETC format_etc = + { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc)); + + STGMEDIUM medium; + EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium)); + std::wstring output = + ScopedHGlobal<wchar_t>(medium.hGlobal).get(); + EXPECT_EQ(input, output); + ReleaseStgMedium(&medium); + + // data is freed automatically by CComPtr. +} + +// Test setting using the IDataObject COM API +TEST(OSExchangeDataTest, StringDataWritingViaCOM) { + OSExchangeData* data = new OSExchangeData; + std::wstring input = L"http://www.google.com/"; + + CComPtr<IDataObject> com_data(data); + + // Store data in the object using the COM SetData API. + CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL); + FORMATETC format_etc = + { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium; + medium.tymed = TYMED_HGLOBAL; + HGLOBAL glob = GlobalAlloc(GPTR, sizeof(wchar_t) * (input.size() + 1)); + size_t stringsz = input.size(); + SIZE_T sz = GlobalSize(glob); + ScopedHGlobal<wchar_t> global_lock(glob); + wchar_t* buffer_handle = global_lock.get(); + wcscpy_s(buffer_handle, input.size() + 1, input.c_str()); + medium.hGlobal = glob; + medium.pUnkForRelease = NULL; + EXPECT_EQ(S_OK, com_data->SetData(&format_etc, &medium, TRUE)); + + // Construct a new object with the old object so that we can use our access + // APIs. + OSExchangeData* data2 = new OSExchangeData(com_data); + EXPECT_TRUE(data2->HasURL()); + GURL url_from_data; + std::wstring title; + EXPECT_TRUE(data2->GetURLAndTitle(&url_from_data, &title)); + GURL reference_url(input); + EXPECT_EQ(reference_url.spec(), url_from_data.spec()); + // deleting data2 will free data because it holds a ref to it. + delete data2; +} + +TEST(OSExchangeDataTest, URLDataAccessViaCOM) { + OSExchangeData* data = new OSExchangeData; + GURL url("http://www.google.com/"); + data->SetURL(url, L""); + CComPtr<IDataObject> com_data(data); + + CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL); + FORMATETC format_etc = + { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc)); + + STGMEDIUM medium; + EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium)); + std::wstring output = + ScopedHGlobal<wchar_t>(medium.hGlobal).get(); + EXPECT_EQ(url.spec(), WideToUTF8(output)); + ReleaseStgMedium(&medium); +} + +TEST(OSExchangeDataTest, MultipleFormatsViaCOM) { + OSExchangeData* data = new OSExchangeData; + std::string url_spec = "http://www.google.com/"; + GURL url(url_spec); + std::wstring text = L"O hai googlz."; + data->SetURL(url, L"Google"); + data->SetString(text); + + CComPtr<IDataObject> com_data(data); + + CLIPFORMAT cfstr_ineturl = RegisterClipboardFormat(CFSTR_INETURL); + FORMATETC url_format_etc = + { cfstr_ineturl, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + EXPECT_EQ(S_OK, com_data->QueryGetData(&url_format_etc)); + FORMATETC text_format_etc = + { CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + EXPECT_EQ(S_OK, com_data->QueryGetData(&text_format_etc)); + + STGMEDIUM medium; + EXPECT_EQ(S_OK, com_data->GetData(&url_format_etc, &medium)); + std::wstring output_url = + ScopedHGlobal<wchar_t>(medium.hGlobal).get(); + EXPECT_EQ(url.spec(), WideToUTF8(output_url)); + ReleaseStgMedium(&medium); + + // The text is supposed to be the raw text of the URL, _NOT_ the value of + // |text|! This is because the URL is added first and thus takes precedence! + EXPECT_EQ(S_OK, com_data->GetData(&text_format_etc, &medium)); + std::wstring output_text = + ScopedHGlobal<wchar_t>(medium.hGlobal).get(); + EXPECT_EQ(url_spec, WideToUTF8(output_text)); + ReleaseStgMedium(&medium); +} + +TEST(OSExchangeDataTest, EnumerationViaCOM) { + OSExchangeData* data = new OSExchangeData; + data->SetURL(GURL("http://www.google.com/"), L""); + data->SetString(L"O hai googlz."); + + CLIPFORMAT cfstr_file_group_descriptor = + RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); + CLIPFORMAT text_x_moz_url = RegisterClipboardFormat(L"text/x-moz-url"); + + CComPtr<IDataObject> com_data(data); + CComPtr<IEnumFORMATETC> enumerator; + EXPECT_EQ(S_OK, com_data->EnumFormatEtc(DATADIR_GET, &enumerator)); + + // Test that we can get one item. + { + // Explictly don't reset the first time, to verify the creation state is + // OK. + ULONG retrieved = 0; + FORMATETC elements_array[1]; + EXPECT_EQ(S_OK, enumerator->Next(1, + reinterpret_cast<FORMATETC*>(&elements_array), &retrieved)); + EXPECT_EQ(1, retrieved); + EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat); + } + + // Test that we can get one item with a NULL retrieved value. + { + EXPECT_EQ(S_OK, enumerator->Reset()); + FORMATETC elements_array[1]; + EXPECT_EQ(S_OK, enumerator->Next(1, + reinterpret_cast<FORMATETC*>(&elements_array), NULL)); + EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat); + } + + // Test that we can get two items. + { + EXPECT_EQ(S_OK, enumerator->Reset()); + ULONG retrieved = 0; + FORMATETC elements_array[2]; + EXPECT_EQ(S_OK, enumerator->Next(2, + reinterpret_cast<FORMATETC*>(&elements_array), &retrieved)); + EXPECT_EQ(2, retrieved); + EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat); + EXPECT_EQ(cfstr_file_group_descriptor, elements_array[1].cfFormat); + } + + // Test that we can skip the first item. + { + EXPECT_EQ(S_OK, enumerator->Reset()); + EXPECT_EQ(S_OK, enumerator->Skip(1)); + ULONG retrieved = 0; + FORMATETC elements_array[1]; + EXPECT_EQ(S_OK, enumerator->Next(1, + reinterpret_cast<FORMATETC*>(&elements_array), &retrieved)); + EXPECT_EQ(1, retrieved); + EXPECT_EQ(cfstr_file_group_descriptor, elements_array[0].cfFormat); + } + + // Test that we can skip the first item, and create a clone that matches in + // this state, and modify the original without affecting the clone. + { + EXPECT_EQ(S_OK, enumerator->Reset()); + EXPECT_EQ(S_OK, enumerator->Skip(1)); + CComPtr<IEnumFORMATETC> cloned_enumerator; + EXPECT_EQ(S_OK, enumerator->Clone(&cloned_enumerator)); + EXPECT_EQ(S_OK, enumerator->Reset()); + + { + ULONG retrieved = 0; + FORMATETC elements_array[1]; + EXPECT_EQ(S_OK, cloned_enumerator->Next(1, + reinterpret_cast<FORMATETC*>(&elements_array), &retrieved)); + EXPECT_EQ(1, retrieved); + EXPECT_EQ(cfstr_file_group_descriptor, elements_array[0].cfFormat); + } + + { + ULONG retrieved = 0; + FORMATETC elements_array[1]; + EXPECT_EQ(S_OK, enumerator->Next(1, + reinterpret_cast<FORMATETC*>(&elements_array), &retrieved)); + EXPECT_EQ(1, retrieved); + EXPECT_EQ(text_x_moz_url, elements_array[0].cfFormat); + } + } +} + +TEST(OSExchangeDataTest, TestURLExchangeFormats) { + OSExchangeData* data = new OSExchangeData; + std::string url_spec = "http://www.google.com/"; + GURL url(url_spec); + std::wstring url_title = L"Google"; + data->SetURL(url, url_title); + std::wstring output; + + OSExchangeData* data2 = new OSExchangeData(data); + + // URL spec and title should match + GURL output_url; + std::wstring output_title; + EXPECT_TRUE(data2->GetURLAndTitle(&output_url, &output_title)); + EXPECT_EQ(url_spec, output_url.spec()); + EXPECT_EQ(url_title, output_title); + std::wstring output_string; + + // URL should be the raw text response + EXPECT_TRUE(data2->GetString(&output_string)); + EXPECT_EQ(url_spec, WideToUTF8(output_string)); + + // File contents access via COM + CComPtr<IDataObject> com_data(data); + { + CLIPFORMAT cfstr_file_contents = RegisterClipboardFormat(CFSTR_FILECONTENTS); + FORMATETC format_etc = + { cfstr_file_contents, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + EXPECT_EQ(S_OK, com_data->QueryGetData(&format_etc)); + + STGMEDIUM medium; + EXPECT_EQ(S_OK, com_data->GetData(&format_etc, &medium)); + std::string output = + ScopedHGlobal<char>(medium.hGlobal).get(); + std::string file_contents = "[InternetShortcut]\r\nURL=" + url_spec + "\r\n"; + EXPECT_EQ(file_contents, output); + ReleaseStgMedium(&medium); + } + + // Need to manually free data2 since we never stuff it into a COMPtr. + delete data2; +} + +TEST(OSExchangeDataTest, TestPickledData) { + CLIPFORMAT test_cf = RegisterClipboardFormat(L"chrome/test"); + + Pickle saved_pickle; + saved_pickle.WriteInt(1); + saved_pickle.WriteInt(2); + scoped_refptr<OSExchangeData> data(new OSExchangeData()); + data->SetPickledData(test_cf, saved_pickle); + + scoped_refptr<OSExchangeData> copy(new OSExchangeData(data.get())); + EXPECT_TRUE(copy->HasFormat(test_cf)); + + Pickle restored_pickle; + EXPECT_TRUE(copy->GetPickledData(test_cf, &restored_pickle)); + void* p_iterator = NULL; + int value; + EXPECT_TRUE(restored_pickle.ReadInt(&p_iterator, &value)); + EXPECT_EQ(1, value); + EXPECT_TRUE(restored_pickle.ReadInt(&p_iterator, &value)); + EXPECT_EQ(2, value); +} + +TEST(OSExchangeDataTest, FileContents) { + scoped_refptr<OSExchangeData> data(new OSExchangeData); + std::string file_contents("data\0with\0nulls", 15); + data->SetFileContents(L"filename.txt", file_contents); + + scoped_refptr<OSExchangeData> copy(new OSExchangeData(data.get())); + std::wstring filename; + std::string read_contents; + EXPECT_TRUE(copy->GetFileContents(&filename, &read_contents)); + EXPECT_EQ(L"filename.txt", filename); + EXPECT_EQ(file_contents, read_contents); +} + +TEST(OSExchangeDataTest, Html) { + scoped_refptr<OSExchangeData> data(new OSExchangeData); + std::wstring html(L"Version:0.9\nStartHTML:71\nEndHTML:160\n" + L"StartFragment:130\nEndFragment:150\n<HTML>\n<BODY>\n" + L"<!--StartFragment-->\n<b>bold.</b> <i><b>This is bold italic.</b></i>\n" + L"<!--EndFragment-->\n</BODY>\n</HTML>"); + data->SetCFHtml(html); + + scoped_refptr<OSExchangeData> copy(new OSExchangeData(data.get())); + std::wstring read_html; + EXPECT_TRUE(copy->GetCFHtml(&read_html)); + EXPECT_EQ(html, read_html); +} + +TEST(OSExchangeDataTest, SetURLWithMaxPath) { + scoped_refptr<OSExchangeData> data(new OSExchangeData); + std::wstring long_title(L'a', MAX_PATH + 1); + data->SetURL(GURL("http://google.com"), long_title); +} + +TEST(OSExchangeDataTest, ProvideURLForPlainTextURL) { + scoped_refptr<OSExchangeData> data(new OSExchangeData); + data->SetString(L"http://google.com"); + + scoped_ptr<OSExchangeData> data2(new OSExchangeData(data.get())); + ASSERT_TRUE(data2->HasURL()); + GURL read_url; + std::wstring title; + EXPECT_TRUE(data2->GetURLAndTitle(&read_url, &title)); + EXPECT_EQ(GURL("http://google.com"), read_url); +} diff --git a/chrome/common/page_transition_types.h b/chrome/common/page_transition_types.h new file mode 100644 index 0000000..c9b41d9 --- /dev/null +++ b/chrome/common/page_transition_types.h @@ -0,0 +1,177 @@ +// 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 CHROME_COMMON_PAGE_TRANSITION_TYPES_H__ +#define CHROME_COMMON_PAGE_TRANSITION_TYPES_H__ + +#include "base/basictypes.h" +#include "base/logging.h" + +// This class is for scoping only. +class PageTransition { + public: + // Types of transitions between pages. These are stored in the history + // database to separate visits, and are reported by the renderer for page + // navigations. + // + // WARNING: don't change these numbers. They are written directly into the + // history database, so future versions will need the same values to match + // the enums. + // + // A type is made of a core value and a set of qualifiers. A type has one + // core value and 0 or or more qualifiers. + enum { + // User got to this page by clicking a link on another page. + LINK = 0, + + // User got this page by typing the URL in the URL bar. This should not be + // used for cases where the user selected a choice that didn't look at all + // like a URL; see GENERATED below. + // + // We also use this for other "explicit" navigation actions. + TYPED = 1, + + // User got to this page through a suggestion in the UI, for example, + // through the destinations page. + AUTO_BOOKMARK = 2, + + // This is a subframe navigation. This is any content that is automatically + // loaded in a non-toplevel frame. For example, if a page consists of + // several frames containing ads, those ad URLs will have this transition + // type. The user may not even realize the content in these pages is a + // separate frame, so may not care about the URL (see MANUAL below). + AUTO_SUBFRAME = 3, + + // For subframe navigations that are explicitly requested by the user and + // generate new navigation entries in the back/forward list. These are + // probably more important than frames that were automatically loaded in + // the background because the user probably cares about the fact that this + // link was loaded. + MANUAL_SUBFRAME = 4, + + // User got to this page by typing in the URL bar and selecting an entry + // that did not look like a URL. For example, a match might have the URL + // of a Google search result page, but appear like "Search Google for ...". + // These are not quite the same as TYPED navigations because the user + // didn't type or see the destination URL. + GENERATED = 5, + + // The page was specified in the command line or is the start page. + START_PAGE = 6, + + // The user filled out values in a form and submitted it. NOTE that in + // some situations submitting a form does not result in this transition + // type. This can happen if the form uses script to submit the contents. + FORM_SUBMIT = 7, + + // The user "reloaded" the page, either by hitting the reload button or by + // hitting enter in the address bar. NOTE: This is distinct from the + // concept of whether a particular load uses "reload semantics" (i.e. + // bypasses cached data). For this reason, lots of code needs to pass + // around the concept of whether a load should be treated as a "reload" + // separately from their tracking of this transition type, which is mainly + // used for proper scoring for consumers who care about how frequently a + // user typed/visited a particular URL. + // + // SessionRestore and undo tab close use this transition type too. + RELOAD = 8, + + // ADDING NEW CORE VALUE? Be sure to update the LAST_CORE and CORE_MASK + // values below. + LAST_CORE = RELOAD, + CORE_MASK = 0xFF, + + // Qualifiers + // Any of the core values above can be augmented by one or more qualifiers. + // These qualifiers further define the transition. + + // The beginning of a navigation chain. + CHAIN_START = 0x10000000, + + // The last transition in a redirect chain. + CHAIN_END = 0x20000000, + + // Redirects caused by JavaScript or a meta refresh tag on the page. + CLIENT_REDIRECT = 0x40000000, + + // Redirects sent from the server by HTTP headers. It might be nice to + // break this out into 2 types in the future, permanent or temporary, if we + // can get that information from WebKit. + SERVER_REDIRECT = 0x80000000, + + // Used to test whether a transition involves a redirect. + IS_REDIRECT_MASK = 0xC0000000, + + // General mask defining the bits used for the qualifiers. + QUALIFIER_MASK = 0xFFFFFF00 + }; + + // The type used for the bitfield. + typedef int Type; + + static bool ValidType(int32 type) { + int32 t = StripQualifier(static_cast<Type>(type)); + return (t >= 0 && t <= LAST_CORE && + (type & ~(QUALIFIER_MASK | CORE_MASK)) == 0); + } + + static Type FromInt(int32 type) { + if (!ValidType(type)) { + NOTREACHED() << "Invalid transition type " << type; + + // Return a safe default so we don't have corrupt data in release mode. + return LINK; + } + return static_cast<Type>(type); + } + + // Returns true if the given transition is a top-level frame transition, or + // false if the transition was for a subframe. + static bool IsMainFrame(Type type) { + int32 t = StripQualifier(type); + return (t != AUTO_SUBFRAME && t != MANUAL_SUBFRAME); + } + + // Returns whether a transition involves a redirection + static bool IsRedirect(Type type) { + return (type & IS_REDIRECT_MASK) != 0; + } + + // Simplifies the provided transition by removing any qualifier + static Type StripQualifier(Type type) { + return static_cast<Type>(type & ~QUALIFIER_MASK); + } + + // Return the qualifier + static int32 GetQualifier(Type type) { + return type & QUALIFIER_MASK; + } +}; + +#endif // CHROME_COMMON_PAGE_TRANSITION_TYPES_H__ diff --git a/chrome/common/plugin_messages.cc b/chrome/common/plugin_messages.cc new file mode 100644 index 0000000..7f46d2a --- /dev/null +++ b/chrome/common/plugin_messages.cc @@ -0,0 +1,48 @@ +// 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 "chrome/common/ipc_message.h" + +#ifdef IPC_MESSAGE_LOG_ENABLED + +#define IPC_MESSAGE_MACROS_LOG_ENABLED + +#endif // IPC_MESSAGE_LOG_ENABLED + +#include "chrome/common/plugin_messages.h" + +void PluginMessagesInit() { +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::RegisterMessageLogger(PluginProcessStart, PluginProcessMsgLog); + IPC::RegisterMessageLogger(PluginProcessHostStart, PluginProcessHostMsgLog); + IPC::RegisterMessageLogger(PluginStart, PluginMsgLog); + IPC::RegisterMessageLogger(PluginHostStart, PluginHostMsgLog); + IPC::RegisterMessageLogger(NPObjectStart, NPObjectMsgLog); +#endif +}
\ No newline at end of file diff --git a/chrome/common/plugin_messages.h b/chrome/common/plugin_messages.h new file mode 100644 index 0000000..8f9e816 --- /dev/null +++ b/chrome/common/plugin_messages.h @@ -0,0 +1,550 @@ +// 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. + +// Defines messages between the browser and plugin process, as well as between +// the renderer and plugin process. +// +// See render_message* for information about the multi-pass include of headers. + +#ifndef CHROME_COMMON_PLUGIN_MESSAGES_H__ +#define CHROME_COMMON_PLUGIN_MESSAGES_H__ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "bindings/npapi.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" + +void PluginMessagesInit(); + +// Name prefix of the event handle when a message box is displayed. +#define kMessageBoxEventPrefix L"message_box_active" + +// Structures for messages that have too many parameters to be put in a +// predefined IPC message. + +struct PluginMsg_Init_Params { + HWND containing_window; + GURL url; + std::vector<std::string> arg_names; + std::vector<std::string> arg_values; + bool load_manually; + HANDLE modal_dialog_event; +}; + +struct PluginHostMsg_URLRequest_Params { + std::string method; + bool is_javascript_url; + std::string target; + std::vector<char> buffer; + bool is_file_data; + bool notify; + std::string url; + HANDLE notify_data; + bool popups_allowed; +}; + +struct PluginMsg_URLRequestReply_Params { + int resource_id; + std::string url; + bool notify_needed; + HANDLE notify_data; +}; + +struct PluginMsg_Paint_Params { + gfx::Size size; + gfx::Rect clip_rect; + gfx::Rect damaged_rect; + + // Bitmap's bits. + HANDLE shared_memory; + + // Information about the world transform (see GetWorldTransform). + XFORM xf; +}; + +struct PluginMsg_PrintResponse_Params { + HANDLE shared_memory; + size_t size; +}; + +struct PluginMsg_DidReceiveResponseParams { + int id; + std::string mime_type; + std::string headers; + uint32 expected_length; + uint32 last_modified; +}; + +struct NPIdentifier_Param { + bool is_string; + std::string string; + int number; +}; + +enum NPVariant_ParamEnum { + NPVARIANT_PARAM_VOID, + NPVARIANT_PARAM_NULL, + NPVARIANT_PARAM_BOOL, + NPVARIANT_PARAM_INT, + NPVARIANT_PARAM_DOUBLE, + NPVARIANT_PARAM_STRING, + // Used when when the NPObject is running in the caller's process, so we + // create an NPObjectProxy in the other process. + NPVARIANT_PARAM_OBJECT_ROUTING_ID, + // Used when the NPObject we're sending is running in the callee's process + // (i.e. we have an NPObjectProxy for it). In that case we want the callee + // to just use the raw pointer. + NPVARIANT_PARAM_OBJECT_POINTER, +}; + +struct NPVariant_Param { + NPVariant_ParamEnum type; + bool bool_value; + int int_value; + double double_value; + std::string string_value; + int npobject_routing_id; + void* npobject_pointer; +}; + + +#define IPC_MESSAGE_MACROS_ENUMS +#include "chrome/common/plugin_messages_internal.h" + +#ifdef IPC_MESSAGE_MACROS_LOG_ENABLED +# undef IPC_MESSAGE_MACROS_LOG +# define IPC_MESSAGE_MACROS_CLASSES + +# include "chrome/common/plugin_messages_internal.h" +# define IPC_MESSAGE_MACROS_LOG +# undef IPC_MESSAGE_MACROS_CLASSES + +# include "chrome/common/plugin_messages_internal.h" +#else +# define IPC_MESSAGE_MACROS_CLASSES +# include "chrome/common/plugin_messages_internal.h" +#endif + +namespace IPC { + +// Traits for PluginMsg_Init_Params structure to pack/unpack. +template <> +struct ParamTraits<PluginMsg_Init_Params> { + typedef PluginMsg_Init_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.containing_window); + WriteParam(m, p.url); + DCHECK(p.arg_names.size() == p.arg_values.size()); + WriteParam(m, p.arg_names); + WriteParam(m, p.arg_values); + WriteParam(m, p.load_manually); + WriteParam(m, p.modal_dialog_event); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->containing_window) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->arg_names) && + ReadParam(m, iter, &p->arg_values) && + ReadParam(m, iter, &p->load_manually) && + ReadParam(m, iter, &p->modal_dialog_event); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.containing_window, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.arg_names, l); + l->append(L", "); + LogParam(p.arg_values, l); + l->append(L", "); + LogParam(p.load_manually, l); + l->append(L", "); + LogParam(p.modal_dialog_event, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits<PluginHostMsg_URLRequest_Params> { + typedef PluginHostMsg_URLRequest_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.method); + WriteParam(m, p.is_javascript_url); + WriteParam(m, p.target); + WriteParam(m, p.buffer); + WriteParam(m, p.is_file_data); + WriteParam(m, p.notify); + WriteParam(m, p.url); + WriteParam(m, p.notify_data); + WriteParam(m, p.popups_allowed); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->method) && + ReadParam(m, iter, &p->is_javascript_url) && + ReadParam(m, iter, &p->target) && + ReadParam(m, iter, &p->buffer) && + ReadParam(m, iter, &p->is_file_data) && + ReadParam(m, iter, &p->notify) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->notify_data) && + ReadParam(m, iter, &p->popups_allowed); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.method, l); + l->append(L", "); + LogParam(p.is_javascript_url, l); + l->append(L", "); + LogParam(p.target, l); + l->append(L", "); + LogParam(p.buffer, l); + l->append(L", "); + LogParam(p.is_file_data, l); + l->append(L", "); + LogParam(p.notify, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.notify_data, l); + l->append(L", "); + LogParam(p.popups_allowed, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits<PluginMsg_URLRequestReply_Params> { + typedef PluginMsg_URLRequestReply_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.resource_id); + WriteParam(m, p.url); + WriteParam(m, p.notify_needed); + WriteParam(m, p.notify_data); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->resource_id) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->notify_needed) && + ReadParam(m, iter, &p->notify_data); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.resource_id, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.notify_needed, l); + l->append(L", "); + LogParam(p.notify_data, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits<PluginMsg_Paint_Params> { + typedef PluginMsg_Paint_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.size); + WriteParam(m, p.clip_rect); + WriteParam(m, p.damaged_rect); + WriteParam(m, p.shared_memory); + WriteParam(m, p.xf); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->size) && + ReadParam(m, iter, &r->clip_rect) && + ReadParam(m, iter, &r->damaged_rect) && + ReadParam(m, iter, &r->shared_memory) && + ReadParam(m, iter, &r->xf); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.size, l); + l->append(L", "); + LogParam(p.clip_rect, l); + l->append(L", "); + LogParam(p.damaged_rect, l); + l->append(L", "); + LogParam(p.shared_memory, l); + l->append(L", "); + LogParam(p.xf, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits<PluginMsg_PrintResponse_Params> { + typedef PluginMsg_PrintResponse_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.shared_memory); + WriteParam(m, p.size); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->shared_memory) && + ReadParam(m, iter, &r->size); + } + static void Log(const param_type& p, std::wstring* l) { + } +}; + +template <> +struct ParamTraits<PluginMsg_DidReceiveResponseParams> { + typedef PluginMsg_DidReceiveResponseParams param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.id); + WriteParam(m, p.mime_type); + WriteParam(m, p.headers); + WriteParam(m, p.expected_length); + WriteParam(m, p.last_modified); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->id) && + ReadParam(m, iter, &r->mime_type) && + ReadParam(m, iter, &r->headers) && + ReadParam(m, iter, &r->expected_length) && + ReadParam(m, iter, &r->last_modified); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.id, l); + l->append(L", "); + LogParam(p.mime_type, l); + l->append(L", "); + LogParam(p.headers, l); + l->append(L", "); + LogParam(p.expected_length, l); + l->append(L", "); + LogParam(p.last_modified, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits<NPEvent> { + typedef NPEvent param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(NPEvent)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (!result || data_size != sizeof(NPEvent)) { + NOTREACHED(); + return false; + } + + memcpy(r, data, sizeof(NPEvent)); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring event, wparam, lparam; + lparam = StringPrintf(L"(%d, %d)", LOWORD(p.lParam), HIWORD(p.lParam)); + switch(p.event) { + case WM_KEYDOWN: + event = L"WM_KEYDOWN"; + wparam = StringPrintf(L"%d", p.wParam); + lparam = StringPrintf(L"%d", p.lParam); + break; + case WM_KEYUP: + event = L"WM_KEYDOWN"; + wparam = StringPrintf(L"%d", p.wParam); + lparam = StringPrintf(L"%x", p.lParam); + break; + case WM_MOUSEMOVE: + event = L"WM_MOUSEMOVE"; + if (p.wParam & MK_LBUTTON) { + wparam = L"MK_LBUTTON"; + } else if (p.wParam & MK_MBUTTON) { + wparam = L"MK_MBUTTON"; + } else if (p.wParam & MK_RBUTTON) { + wparam = L"MK_RBUTTON"; + } + break; + case WM_LBUTTONDOWN: + event = L"WM_LBUTTONDOWN"; + break; + case WM_MBUTTONDOWN: + event = L"WM_MBUTTONDOWN"; + break; + case WM_RBUTTONDOWN: + event = L"WM_RBUTTONDOWN"; + break; + case WM_LBUTTONUP: + event = L"WM_LBUTTONUP"; + break; + case WM_MBUTTONUP: + event = L"WM_MBUTTONUP"; + break; + case WM_RBUTTONUP: + event = L"WM_RBUTTONUP"; + break; + } + + if (p.wParam & MK_CONTROL) { + if (!wparam.empty()) + wparam += L" "; + wparam += L"MK_CONTROL"; + } + + if (p.wParam & MK_SHIFT) { + if (!wparam.empty()) + wparam += L" "; + wparam += L"MK_SHIFT"; + } + + l->append(L"("); + LogParam(event, l); + l->append(L", "); + LogParam(wparam, l); + l->append(L", "); + LogParam(lparam, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits<NPIdentifier_Param> { + typedef NPIdentifier_Param param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.is_string); + if (p.is_string) { + WriteParam(m, p.string); + } else { + WriteParam(m, p.number); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + if (!ReadParam(m, iter, &r->is_string)) + return false; + + bool result; + if (r->is_string) { + result = ReadParam(m, iter, &r->string); + } else { + result = ReadParam(m, iter, &r->number); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + if (p.is_string) { + l->append(ASCIIToWide(p.string)); + } else { + l->append(StringPrintf(L"%d", p.number)); + } + } +}; + +template <> +struct ParamTraits<NPVariant_Param> { + typedef NPVariant_Param param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.type)); + if (p.type == NPVARIANT_PARAM_BOOL) { + WriteParam(m, p.bool_value); + } else if (p.type == NPVARIANT_PARAM_INT) { + WriteParam(m, p.int_value); + } else if (p.type == NPVARIANT_PARAM_DOUBLE) { + WriteParam(m, p.double_value); + } else if (p.type == NPVARIANT_PARAM_STRING) { + WriteParam(m, std::string(p.string_value)); + } else if (p.type == NPVARIANT_PARAM_OBJECT_ROUTING_ID) { + // This is the routing id used to connect NPObjectProxy in the other + // process with NPObjectStub in this process. + WriteParam(m, p.npobject_routing_id); + // The actual NPObject pointer, in case it's passed back to this process. + WriteParam(m, p.npobject_pointer); + } else if (p.type == NPVARIANT_PARAM_OBJECT_POINTER) { + // The NPObject resides in the other process, so just send its pointer. + WriteParam(m, p.npobject_pointer); + } else { + DCHECK(p.type == NPVARIANT_PARAM_VOID || p.type == NPVARIANT_PARAM_NULL); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type)) + return false; + + bool result = false; + r->type = static_cast<NPVariant_ParamEnum>(type); + if (r->type == NPVARIANT_PARAM_BOOL) { + result = ReadParam(m, iter, &r->bool_value); + } else if (r->type == NPVARIANT_PARAM_INT) { + result = ReadParam(m, iter, &r->int_value); + } else if (r->type == NPVARIANT_PARAM_DOUBLE) { + result = ReadParam(m, iter, &r->double_value); + } else if (r->type == NPVARIANT_PARAM_STRING) { + result = ReadParam(m, iter, &r->string_value); + } else if (r->type == NPVARIANT_PARAM_OBJECT_ROUTING_ID) { + result = + ReadParam(m, iter, &r->npobject_routing_id) && + ReadParam(m, iter, &r->npobject_pointer); + } else if (r->type == NPVARIANT_PARAM_OBJECT_POINTER) { + result = ReadParam(m, iter, &r->npobject_pointer); + } else if ((r->type == NPVARIANT_PARAM_VOID) || + (r->type == NPVARIANT_PARAM_NULL)) { + result = true; + } else { + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + if (p.type == NPVARIANT_PARAM_BOOL) { + LogParam(p.bool_value, l); + } else if (p.type == NPVARIANT_PARAM_INT) { + LogParam(p.int_value, l); + } else if (p.type == NPVARIANT_PARAM_DOUBLE) { + LogParam(p.double_value, l); + } else if (p.type == NPVARIANT_PARAM_STRING) { + LogParam(p.string_value, l); + } else if (p.type == NPVARIANT_PARAM_OBJECT_ROUTING_ID) { + LogParam(p.npobject_routing_id, l); + LogParam(p.npobject_pointer, l); + } else if (p.type == NPVARIANT_PARAM_OBJECT_POINTER) { + LogParam(p.npobject_pointer, l); + } + } +}; + +} // namespace IPC + +#endif // CHROME_COMMON_PLUGIN_MESSAGES_H__ diff --git a/chrome/common/plugin_messages_internal.h b/chrome/common/plugin_messages_internal.h new file mode 100644 index 0000000..1379e98 --- /dev/null +++ b/chrome/common/plugin_messages_internal.h @@ -0,0 +1,318 @@ +// 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/shared_memory.h" +#include "chrome/common/ipc_message_macros.h" +#include "webkit/glue/webcursor.h" + +//----------------------------------------------------------------------------- +// PluginProcess messages +// These are messages sent from the browser to the plugin process. +IPC_BEGIN_MESSAGES(PluginProcess, 3) + // Tells the plugin process to create a new channel for communication with a + // renderer. The channel name is returned in a + // PluginProcessHostMsg_ChannelCreated message. + // The renderer_handle is the handle of the renderer process requesting + // the channel. It has to be valid in the context of the plugin process. + IPC_MESSAGE_CONTROL2(PluginProcessMsg_CreateChannel, + int /* process_id */, + HANDLE /* renderer handle */) + + IPC_MESSAGE_CONTROL1(PluginProcessMsg_ShutdownResponse, + bool /* ok to shutdown */) + + // Allows a chrome plugin loaded in the browser process to send arbitrary + // data to an instance of the same plugin loaded in a plugin process. + IPC_MESSAGE_CONTROL1(PluginProcessMsg_PluginMessage, + std::vector<uint8> /* opaque data */) + + IPC_MESSAGE_CONTROL0(PluginProcessMsg_BrowserShutdown) + +IPC_END_MESSAGES(PluginProcess) + + +//----------------------------------------------------------------------------- +// PluginProcessHost messages +// These are messages sent from the plugin process to the browser process. +IPC_BEGIN_MESSAGES(PluginProcessHost, 4) + // Response to a PluginProcessMsg_CreateChannel message. + IPC_MESSAGE_CONTROL2(PluginProcessHostMsg_ChannelCreated, + int /* process_id */, + std::wstring /* channel_name */) + + IPC_MESSAGE_ROUTED3(PluginProcessHostMsg_DownloadUrl, + std::string /* URL */, + int /* process id */, + HWND /* caller window */) + + IPC_SYNC_MESSAGE_CONTROL0_1(PluginProcessHostMsg_GetPluginFinderUrl, + std::string /* plugin finder URL */) + + IPC_MESSAGE_CONTROL0(PluginProcessHostMsg_ShutdownRequest) + + // Allows a chrome plugin loaded in a plugin process to send arbitrary + // data to an instance of the same plugin loaded in the browser process. + IPC_MESSAGE_CONTROL1(PluginProcessHostMsg_PluginMessage, + std::vector<uint8> /* opaque data */) + + // Retrieve the given type of info that is associated with the given + // CPBrowsingContext. Returns the result in a string. + IPC_SYNC_MESSAGE_CONTROL0_1(PluginProcessHostMsg_GetPluginDataDir, + std::wstring /* data_dir_retval */) + + // Used to get cookies for the given URL. The request_context is a + // CPBrowsingContext, but is passed as int32 to avoid compilation errors. + IPC_SYNC_MESSAGE_CONTROL2_1(PluginProcessHostMsg_GetCookies, + int32 /* request_context */, + GURL /* url */, + std::string /* cookies */) + +IPC_END_MESSAGES(PluginProcessHost) + + +//----------------------------------------------------------------------------- +// Plugin messages +// These are messages sent from the renderer process to the plugin process. +IPC_BEGIN_MESSAGES(Plugin, 5) + // Tells the plugin process to create a new plugin instance with the given + // id. A corresponding WebPluginDelegateStub is created which hosts the + // WebPluginDelegateImpl. + IPC_SYNC_MESSAGE_CONTROL1_1(PluginMsg_CreateInstance, + std::string /* mime_type */, + int /* instance_id */) + + // The WebPluginDelegateProxy sends this to the WebPluginDelegateStub in its + // destructor, so that the stub deletes the actual WebPluginDelegateImpl + // object that it's hosting. + IPC_SYNC_MESSAGE_CONTROL1_0(PluginMsg_DestroyInstance, + int /* instance_id */) + + IPC_SYNC_MESSAGE_CONTROL0_1(PluginMsg_GenerateRouteID, + int /* id */) + + // The messages below all map to WebPluginDelegate methods. + IPC_SYNC_MESSAGE_ROUTED1_1(PluginMsg_Init, + PluginMsg_Init_Params, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_Paint, + PluginMsg_Paint_Params /* params */) + + IPC_SYNC_MESSAGE_ROUTED0_1(PluginMsg_Print, + PluginMsg_PrintResponse_Params /* params */) + + // Returns a shared memory handle to a EMF buffer. + IPC_SYNC_MESSAGE_ROUTED1_2(PluginMsg_PaintIntoSharedMemory, + PluginMsg_Paint_Params /* params */, + SharedMemoryHandle /* emf_buffer */, + size_t /* bytes */) + + IPC_SYNC_MESSAGE_ROUTED0_2(PluginMsg_GetPluginScriptableObject, + int /* route_id */, + void* /* npobject_ptr */) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_DidFinishLoadWithReason, + int /* reason */) + + IPC_MESSAGE_ROUTED3(PluginMsg_UpdateGeometry, + gfx::Rect /* window_rect */, + gfx::Rect /* clip_rect */, + bool /* visible */) + + IPC_SYNC_MESSAGE_ROUTED0_0(PluginMsg_SetFocus) + + IPC_SYNC_MESSAGE_ROUTED1_2(PluginMsg_HandleEvent, + NPEvent /* event */, + bool /* handled */, + WebCursor /* cursor type*/) + + IPC_SYNC_MESSAGE_ROUTED2_0(PluginMsg_WillSendRequest, + int /* id */, + GURL /* url */) + + IPC_SYNC_MESSAGE_ROUTED1_1(PluginMsg_DidReceiveResponse, + PluginMsg_DidReceiveResponseParams, + bool /* cancel */) + + IPC_SYNC_MESSAGE_ROUTED2_0(PluginMsg_DidReceiveData, + int /* id */, + std::vector<char> /* buffer */) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_DidFinishLoading, + int /* id */) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_DidFail, + int /* id */) + + IPC_MESSAGE_ROUTED5(PluginMsg_SendJavaScriptStream, + std::string /* url */, + std::wstring /* result */, + bool /* success */, + bool /* notify required */, + int /* notify data */) + + IPC_MESSAGE_ROUTED2(PluginMsg_DidReceiveManualResponse, + std::string /* url */, + PluginMsg_DidReceiveResponseParams) + + IPC_MESSAGE_ROUTED1(PluginMsg_DidReceiveManualData, + std::vector<char> /* buffer */) + + IPC_MESSAGE_ROUTED0(PluginMsg_DidFinishManualLoading) + + IPC_MESSAGE_ROUTED0(PluginMsg_DidManualLoadFail) + + IPC_MESSAGE_ROUTED0(PluginMsg_InstallMissingPlugin) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_HandleURLRequestReply, + PluginMsg_URLRequestReply_Params) + + IPC_SYNC_MESSAGE_ROUTED3_0(PluginMsg_URLRequestRouted, + std::string /* url */, + bool /* notify_needed */, + HANDLE /* notify data */) +IPC_END_MESSAGES(Plugin) + + +//----------------------------------------------------------------------------- +// PluginHost messages +// These are messages sent from the plugin process to the renderer process. +// They all map to the corresponding WebPlugin methods. +IPC_BEGIN_MESSAGES(PluginHost, 6) + // Sends the plugin window information to the renderer. + // The window parameter is a handle to the window if the plugin is a windowed + // plugin. It is NULL for windowless plugins. + // The modal_loop_pump_messages_event parameter is an event handle which is + // passed in for windowless plugins and is used to indicate if messages + // are to be pumped in sync calls to the plugin process. Currently used + // in HandleEvent calls. + IPC_SYNC_MESSAGE_ROUTED2_0(PluginHostMsg_SetWindow, + HWND /* window */, + HANDLE /* modal_loop_pump_messages_event */) + + IPC_MESSAGE_ROUTED1(PluginHostMsg_URLRequest, + PluginHostMsg_URLRequest_Params) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginHostMsg_CancelResource, + int /* id */) + + IPC_MESSAGE_ROUTED0(PluginHostMsg_Invalidate) + + IPC_MESSAGE_ROUTED1(PluginHostMsg_InvalidateRect, + gfx::Rect /* rect */) + + IPC_SYNC_MESSAGE_ROUTED1_2(PluginHostMsg_GetWindowScriptNPObject, + int /* route id */, + bool /* success */, + void* /* npobject_ptr */) + + IPC_SYNC_MESSAGE_ROUTED1_2(PluginHostMsg_GetPluginElement, + int /* route id */, + bool /* success */, + void* /* npobject_ptr */) + + IPC_MESSAGE_ROUTED3(PluginHostMsg_SetCookie, + GURL /* url */, + GURL /* policy_url */, + std::string /* cookie */) + + IPC_SYNC_MESSAGE_ROUTED2_1(PluginHostMsg_GetCookies, + GURL /* url */, + GURL /* policy_url */, + std::string /* cookies */) + + // Asks the browser to show a modal HTML dialog. The dialog is passed the + // given arguments as a JSON string, and returns its result as a JSON string + // through json_retval. + IPC_SYNC_MESSAGE_ROUTED4_1(PluginHostMsg_ShowModalHTMLDialog, + GURL /* url */, + int /* width */, + int /* height */, + std::string /* json_arguments */, + std::string /* json_retval */) + + IPC_MESSAGE_ROUTED1(PluginHostMsg_MissingPluginStatus, + int /* status */) + + IPC_SYNC_MESSAGE_ROUTED0_1(PluginHostMsg_GetCPBrowsingContext, + uint32 /* context */) + +IPC_END_MESSAGES(PluginHost) + +//----------------------------------------------------------------------------- +// NPObject messages +// These are messages used to marshall NPObjects. They are sent both from the +// plugin to the renderer and from the renderer to the plugin. +IPC_BEGIN_MESSAGES(NPObject, 7) + IPC_SYNC_MESSAGE_ROUTED0_0(NPObjectMsg_Release) + + IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_HasMethod, + NPIdentifier_Param /* name */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED3_2(NPObjectMsg_Invoke, + bool /* is_default */, + NPIdentifier_Param /* method */, + std::vector<NPVariant_Param> /* args */, + NPVariant_Param /* result_param */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_HasProperty, + NPIdentifier_Param /* name */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_2(NPObjectMsg_GetProperty, + NPIdentifier_Param /* name */, + NPVariant_Param /* property */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED2_1(NPObjectMsg_SetProperty, + NPIdentifier_Param /* name */, + NPVariant_Param /* property */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_RemoveProperty, + NPIdentifier_Param /* name */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED0_0(NPObjectMsg_Invalidate) + + IPC_SYNC_MESSAGE_ROUTED0_2(NPObjectMsg_Enumeration, + std::vector<NPIdentifier_Param> /* value */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_2(NPObjectMsg_Evaluate, + std::string /* script */, + NPVariant_Param /* result_param */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_0(NPObjectMsg_SetException, + std::string /* message */) + +IPC_END_MESSAGES(NPObject) diff --git a/chrome/common/pref_member.h b/chrome/common/pref_member.h new file mode 100644 index 0000000..9e175d2 --- /dev/null +++ b/chrome/common/pref_member.h @@ -0,0 +1,216 @@ +// 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 helper class that stays in sync with a preference (bool, int, real, or +// string). For example: +// +// class MyClass { +// public: +// MyClass(PrefService* prefs) { +// my_string_.Init(prefs::kHomePage, prefs, NULL /* no observer */); +// } +// private: +// StringPrefMember my_string_; +// }; +// +// my_string_ should stay in sync with the prefs::kHomePage pref and will +// update if either the pref changes or if my_string_.SetValue is called. +// +// An optional observer can be passed into the Init method which can be used to +// notify MyClass of changes. + +#ifndef CHROME_COMMON_PREF_MEMBER_H__ +#define CHROME_COMMON_PREF_MEMBER_H__ + +#include <string> + +#include "base/logging.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_service.h" + +template <typename ValueType> +class PrefMember : public NotificationObserver { + public: + // Defer initialization to an Init method so it's easy to make this class be + // a member variable. + PrefMember() : observer_(NULL), prefs_(NULL), is_synced_(false), + setting_value_(false) {} + + // Do the actual initialization of the class. |observer| may be null if you + // don't want any notifications of changes. + void Init(const wchar_t* pref_name, PrefService* prefs, + NotificationObserver* observer) { + DCHECK(pref_name); + DCHECK(prefs); + DCHECK(pref_name_.empty()); // Check that Init is only called once. + observer_ = observer; + prefs_ = prefs; + pref_name_ = pref_name; + DCHECK(!pref_name_.empty()); + + // Add ourself as a pref observer so we can keep our local value in sync. + prefs_->AddPrefObserver(pref_name, this); + } + + virtual ~PrefMember() { + if (!pref_name_.empty()) + prefs_->RemovePrefObserver(pref_name_.c_str(), this); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(!pref_name_.empty()); + DCHECK(NOTIFY_PREF_CHANGED == type); + UpdateValueFromPref(); + is_synced_ = true; + if (!setting_value_ && observer_) + observer_->Observe(type, source, details); + } + + // Retrieve the value of the member variable. + ValueType GetValue() { + DCHECK(!pref_name_.empty()); + // We lazily fetch the value from the pref service the first time GetValue + // is called. + if (!is_synced_) { + UpdateValueFromPref(); + is_synced_ = true; + } + return value_; + } + + // Provided as a convenience. + ValueType operator*() { + return GetValue(); + } + + // Set the value of the member variable. + void SetValue(const ValueType& value) { + DCHECK(!pref_name_.empty()); + setting_value_ = true; + UpdatePref(value); + setting_value_ = false; + } + + protected: + // These methods are used to do the actual sync with pref of the specified + // type. + virtual void UpdateValueFromPref() = 0; + virtual void UpdatePref(const ValueType& value) = 0; + + std::wstring pref_name_; + PrefService* prefs_; + + // We cache the value of the pref so we don't have to keep walking the pref + // tree. + ValueType value_; + + private: + NotificationObserver* observer_; + bool is_synced_; + bool setting_value_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Implementations of Boolean, Integer, Real, and String PrefMember below. + +class BooleanPrefMember : public PrefMember<bool> { + public: + BooleanPrefMember() : PrefMember() { } + virtual ~BooleanPrefMember() { } + + protected: + virtual void UpdateValueFromPref() { + value_ = prefs_->GetBoolean(pref_name_.c_str()); + } + + virtual void UpdatePref(const bool& value) { + prefs_->SetBoolean(pref_name_.c_str(), value); + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(BooleanPrefMember); +}; + +class IntegerPrefMember : public PrefMember<int> { + public: + IntegerPrefMember() : PrefMember() { } + virtual ~IntegerPrefMember() { } + + protected: + virtual void UpdateValueFromPref() { + value_ = prefs_->GetInteger(pref_name_.c_str()); + } + + virtual void UpdatePref(const int& value) { + prefs_->SetInteger(pref_name_.c_str(), value); + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(IntegerPrefMember); +}; + +class RealPrefMember : public PrefMember<double> { + public: + RealPrefMember() : PrefMember() { } + virtual ~RealPrefMember() { } + + protected: + virtual void UpdateValueFromPref() { + value_ = prefs_->GetReal(pref_name_.c_str()); + } + + virtual void UpdatePref(const double& value) { + prefs_->SetReal(pref_name_.c_str(), value); + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(RealPrefMember); +}; + +class StringPrefMember : public PrefMember<std::wstring> { + public: + StringPrefMember() : PrefMember() { } + virtual ~StringPrefMember() { } + + protected: + virtual void UpdateValueFromPref() { + value_ = prefs_->GetString(pref_name_.c_str()); + } + + virtual void UpdatePref(const std::wstring& value) { + prefs_->SetString(pref_name_.c_str(), value); + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(StringPrefMember); +}; + +#endif // CHROME_COMMON_PREF_MEMBER_H__ diff --git a/chrome/common/pref_member_unittest.cc b/chrome/common/pref_member_unittest.cc new file mode 100644 index 0000000..5a26ece --- /dev/null +++ b/chrome/common/pref_member_unittest.cc @@ -0,0 +1,214 @@ +// 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 "chrome/common/pref_member.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +static const wchar_t kBoolPref[] = L"bool"; +static const wchar_t kIntPref[] = L"int"; +static const wchar_t kRealPref[] = L"real"; +static const wchar_t kStringPref[] = L"string"; + +void RegisterTestPrefs(PrefService* prefs) { + prefs->RegisterBooleanPref(kBoolPref, false); + prefs->RegisterIntegerPref(kIntPref, 0); + prefs->RegisterRealPref(kRealPref, 0.0); + prefs->RegisterStringPref(kStringPref, L"default"); +} + +class PrefMemberTestClass : public NotificationObserver { + public: + PrefMemberTestClass(PrefService* prefs) : prefs_(prefs), observe_cnt_(0) { + str_.Init(kStringPref, prefs, this); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(NOTIFY_PREF_CHANGED == type); + PrefService* prefs_in = Source<PrefService>(source).ptr(); + EXPECT_EQ(prefs_in, prefs_); + std::wstring* pref_name_in = Details<std::wstring>(details).ptr(); + EXPECT_EQ(*pref_name_in, kStringPref); + EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref)); + ++observe_cnt_; + } + + StringPrefMember str_; + int observe_cnt_; + + private: + PrefService* prefs_; +}; + +} // anonymous namespace + +TEST(PrefMemberTest, BasicGetAndSet) { + PrefService prefs; + RegisterTestPrefs(&prefs); + + // Test bool + BooleanPrefMember boolean; + boolean.Init(kBoolPref, &prefs, NULL); + + // Check the defaults + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Try changing through the member variable. + boolean.SetValue(true); + EXPECT_TRUE(boolean.GetValue()); + EXPECT_TRUE(prefs.GetBoolean(kBoolPref)); + EXPECT_TRUE(*boolean); + + // Try changing back through the pref. + prefs.SetBoolean(kBoolPref, false); + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Test int + IntegerPrefMember integer; + integer.Init(kIntPref, &prefs, NULL); + + // Check the defaults + EXPECT_EQ(0, prefs.GetInteger(kIntPref)); + EXPECT_EQ(0, integer.GetValue()); + EXPECT_EQ(0, *integer); + + // Try changing through the member variable. + integer.SetValue(5); + EXPECT_EQ(5, integer.GetValue()); + EXPECT_EQ(5, prefs.GetInteger(kIntPref)); + EXPECT_EQ(5, *integer); + + // Try changing back through the pref. + prefs.SetInteger(kIntPref, 2); + EXPECT_EQ(2, prefs.GetInteger(kIntPref)); + EXPECT_EQ(2, integer.GetValue()); + EXPECT_EQ(2, *integer); + + // Test real (double) + RealPrefMember real; + real.Init(kRealPref, &prefs, NULL); + + // Check the defaults + EXPECT_EQ(0.0, prefs.GetReal(kRealPref)); + EXPECT_EQ(0.0, real.GetValue()); + EXPECT_EQ(0.0, *real); + + // Try changing through the member variable. + real.SetValue(1.0); + EXPECT_EQ(1.0, real.GetValue()); + EXPECT_EQ(1.0, prefs.GetReal(kRealPref)); + EXPECT_EQ(1.0, *real); + + // Try changing back through the pref. + prefs.SetReal(kRealPref, 3.0); + EXPECT_EQ(3.0, prefs.GetReal(kRealPref)); + EXPECT_EQ(3.0, real.GetValue()); + EXPECT_EQ(3.0, *real); + + // Test string + StringPrefMember string; + string.Init(kStringPref, &prefs, NULL); + + // Check the defaults + EXPECT_EQ(L"default", prefs.GetString(kStringPref)); + EXPECT_EQ(L"default", string.GetValue()); + EXPECT_EQ(L"default", *string); + + // Try changing through the member variable. + string.SetValue(L"foo"); + EXPECT_EQ(L"foo", string.GetValue()); + EXPECT_EQ(L"foo", prefs.GetString(kStringPref)); + EXPECT_EQ(L"foo", *string); + + // Try changing back through the pref. + prefs.SetString(kStringPref, L"bar"); + EXPECT_EQ(L"bar", prefs.GetString(kStringPref)); + EXPECT_EQ(L"bar", string.GetValue()); + EXPECT_EQ(L"bar", *string); +} + +TEST(PrefMemberTest, TwoPrefs) { + // Make sure two RealPrefMembers stay in sync. + PrefService prefs; + RegisterTestPrefs(&prefs); + + RealPrefMember pref1; + pref1.Init(kRealPref, &prefs, NULL); + RealPrefMember pref2; + pref2.Init(kRealPref, &prefs, NULL); + + pref1.SetValue(2.3); + EXPECT_EQ(2.3, *pref2); + + pref2.SetValue(3.5); + EXPECT_EQ(3.5, *pref1); + + prefs.SetReal(kRealPref, 4.2); + EXPECT_EQ(4.2, *pref1); + EXPECT_EQ(4.2, *pref2); +} + +TEST(PrefMemberTest, Observer) { + PrefService prefs; + RegisterTestPrefs(&prefs); + + PrefMemberTestClass test_obj(&prefs); + EXPECT_EQ(L"default", *test_obj.str_); + + // Calling SetValue should not fire the observer. + test_obj.str_.SetValue(L"hello"); + EXPECT_EQ(0, test_obj.observe_cnt_); + EXPECT_EQ(L"hello", prefs.GetString(kStringPref)); + + // Changing the pref does fire the observer. + prefs.SetString(kStringPref, L"world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ(L"world", *(test_obj.str_)); + + // Not changing the value should not fire the observer. + prefs.SetString(kStringPref, L"world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ(L"world", *(test_obj.str_)); + + prefs.SetString(kStringPref, L"hello"); + EXPECT_EQ(2, test_obj.observe_cnt_); + EXPECT_EQ(L"hello", prefs.GetString(kStringPref)); +} + +TEST(PrefMemberTest, NoInit) { + // Make sure not calling Init on a PrefMember doesn't cause problems. + IntegerPrefMember pref; +} diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc new file mode 100644 index 0000000..77f68cd --- /dev/null +++ b/chrome/common/pref_names.cc @@ -0,0 +1,425 @@ +// 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 "chrome/common/pref_names.h" + +namespace prefs { + +// *************** PROFILE PREFS *************** +// These are attached to the user profile + +// This is the URL of the page to load when opening new tabs. +const wchar_t kHomePage[] = L"homepage"; + +// This is the human-readable name of the current profile. +const wchar_t kProfileName[] = L"profile.name"; + +// This is the abbreviated human-readable name of the current profile +const wchar_t kProfileNickname[] = L"profile.nickname"; + +// This is a short, preferably human-readable ID for the current profile. +// Its value should be unique within the current user data directory. +const wchar_t kProfileID[] = L"profile.id"; + +// A boolean value that when true stipulates that the history of the active +// tab should be shown in its entirety at the start of the list, rather than +// being mingled based on view time. +const wchar_t kRecentlyViewedModelBiasActiveTabHistory[] = + L"recently_viewed_model.bias_active_tab_history"; + +// This is a value that defines the selection mode used by the recently +// viewed pages model to determine how to load the selected item. values are: +// 0 - the tab that contains or contained the item should be activated, and +// the item navigated to within that tab. +// 1 - if the tab that contains or contained the item is active, the item +// should be navigated to, otherwise a new tab is opened with the +// selected item only. +// +// ... future +const wchar_t kRecentlyViewedModelSelectionMode[] = + L"recently_viewed_model.selection_mode"; + +// Used to determine if the last session exited cleanly. Set to false when +// first opened, and to true when closing. On startup if the value is false, +// it means the profile didn't exit cleanly. +const wchar_t kSessionExitedCleanly[] = L"profile.exited_cleanly"; + +// This is one of three integer values: +// 0: (or empty) don't do anything special on startup. +// 1: restore the last session. +// 2: this was used to indicate a specific session should be restored. It is +// no longer used, but saved to avoid conflict with old preferences. +// 3: unused, previously indicated the user wants to restore a saved session. +// 4: restore the URLs defined in kURLsToRestoreOnStartup. +const wchar_t kRestoreOnStartup[] = L"session.restore_on_startup"; + +// The URLs to restore on startup or when the home button is pressed. The URLs +// are only restored on startup if kRestoreOnStartup is 4. +const wchar_t kURLsToRestoreOnStartup[] = + L"session.urls_to_restore_on_startup"; + +// The application locale. +const wchar_t kApplicationLocale[] = L"intl.app_locale"; + +// The default character encoding to assume for a web page in the +// absence of MIME charset specification +const wchar_t kDefaultCharset[] = L"intl.charset_default"; + +// The value to use for Accept-Languages HTTP header when making an HTTP +// request. +const wchar_t kAcceptLanguages[] = L"intl.accept_languages"; + +// The value to use for showing locale-dependent encoding list for different +// locale, it's initialized from the corresponding string resource that is +// stored in non-translatable part of the resource bundle. +const wchar_t kStaticEncodings[] = L"intl.static_encodings"; + +// WebKit preferences. +// A boolean flag to indicate whether WebKit standard font family is +// serif or sans-serif. We don't have a UI for setting standard family. +// Instead, we use this pref to map either serif or sans_serif to WebKit +// standard font family. At the moment, we don't have a UI for this +// flag, either. +const wchar_t kWebKitStandardFontIsSerif[] = + L"webkit.webprefs.standard_font_is_serif"; +const wchar_t kWebKitFixedFontFamily[] = L"webkit.webprefs.fixed_font_family"; +const wchar_t kWebKitSerifFontFamily[] = L"webkit.webprefs.serif_font_family"; +const wchar_t kWebKitSansSerifFontFamily[] = + L"webkit.webprefs.sansserif_font_family"; +const wchar_t kWebKitCursiveFontFamily[] = + L"webkit.webprefs.cursive_font_family"; +const wchar_t kWebKitFantasyFontFamily[] = + L"webkit.webprefs.fantasy_font_family"; +const wchar_t kWebKitDefaultFontSize[] = L"webkit.webprefs.default_font_size"; +const wchar_t kWebKitDefaultFixedFontSize[] = + L"webkit.webprefs.default_fixed_font_size"; +const wchar_t kWebKitMinimumFontSize[] = L"webkit.webprefs.minimum_font_size"; +const wchar_t kWebKitMinimumLogicalFontSize[] = + L"webkit.webprefs.minimum_logical_font_size"; +const wchar_t kWebKitJavascriptEnabled[] = + L"webkit.webprefs.javascript_enabled"; +const wchar_t kWebKitJavascriptCanOpenWindowsAutomatically[] = + L"webkit.webprefs.javascript_can_open_windows_automatically"; +const wchar_t kWebKitLoadsImagesAutomatically[] = + L"webkit.webprefs.loads_images_automatically"; +const wchar_t kWebKitPluginsEnabled[] = L"webkit.webprefs.plugins_enabled"; +const wchar_t kWebKitDomPasteEnabled[] = L"webkit.webprefs.dom_paste_enabled"; +const wchar_t kWebKitShrinksStandaloneImagesToFit[] = + L"webkit.webprefs.shrinks_standalone_images_to_fit"; +const wchar_t kWebKitDeveloperExtrasEnabled[] = + L"webkit.webprefs.developer_extras_enabled"; +const wchar_t kWebKitUsesUniversalDetector[] = + L"webkit.webprefs.uses_universal_detector"; +const wchar_t kWebKitTextAreasAreResizable[] = + L"webkit.webprefs.text_areas_are_resizable"; +const wchar_t kWebKitJavaEnabled[] = + L"webkit.webprefs.java_enabled"; + +// Boolean which specifies whether the bookmark bar is visible on all tabs. +const wchar_t kShowBookmarkBar[] = L"bookmark_bar.show_on_all_tabs"; + +// Boolean which specifies whether the destinations tab should always be on. +const wchar_t kAlwaysCreateDestinationsTab[] = + L"profile.always_create_destinations_tab"; + +// Boolean that is true if the password manager is on (will record new +// passwords and fill in known passwords). +const wchar_t kPasswordManagerEnabled[] = L"profile.password_manager_enabled"; + +// Boolean that is true when SafeBrowsing is enabled. +const wchar_t kSafeBrowsingEnabled[] = L"safebrowsing.enabled"; + +// Boolean that is true when Suggest support is enabled. +const wchar_t kSearchSuggestEnabled[] = L"search.suggest_enabled"; + +// Enum that specifies whether to enforce a third-party cookie blocking policy. +// 0 - allow all cookies. +// 1 - block third-party cookies +// 2 - block all cookies +const wchar_t kCookieBehavior[] = L"security.cookie_behavior"; + +// Boolean that is true if mixed content should be filtered. +// TODO(jcampan): http://b/1084034: at some point this will become an enum +// (int): don't filter, filter everything, filter images only. +const wchar_t kMixedContentFiltering[] = L"security.mixed_content_filtering"; + +// The URL (as understood by TemplateURLRef) the default search provider uses +// for searches. +const wchar_t kDefaultSearchProviderSearchURL[] = + L"default_search_provider.search_url"; + +// The URL (as understood by TemplateURLRef) the default search provider uses +// for suggestions. +const wchar_t kDefaultSearchProviderSuggestURL[] = + L"default_search_provider.suggest_url"; + +// The name of the default search provider. +const wchar_t kDefaultSearchProviderName[] = L"default_search_provider.name"; + +// The id of the default search provider. +const wchar_t kDefaultSearchProviderID[] = L"default_search_provider.id"; + +// Boolean of whether or not popups should be completely blocked (true), or +// just opened "minimized" (default, false). +const wchar_t kBlockPopups[] = L"browser.block_popups"; + +// Boolean which specifies whether we should ask the user if we should download +// a file (true) or just download it automatically. +const wchar_t kPromptForDownload[] = L"download.prompt_for_download"; + +// A boolean pref set to true if we're using Link Doctor error pages. +const wchar_t kAlternateErrorPagesEnabled[] = L"alternate_error_pages.enabled"; + +// A boolean pref set to true if DNS pre-fetching is being done in browser. +const wchar_t kDnsPrefetchingEnabled[] = L"dns_prefetching.enabled"; + +// An adaptively identified list of domain names to be pre-fetched during the +// next startup, based on what was actually needed during this startup. +const wchar_t kDnsStartupPrefetchList[] = L"StartupDNSPrefetchList"; + +// The disabled messages in IPC logging. +const wchar_t kIpcDisabledMessages[] = L"ipc_log_disabled_messages"; + +// A boolean pref set to true if a Home button to open the Home pages should be +// visible on the toolbar. +const wchar_t kShowHomeButton[] = L"browser.show_home_button"; + +// A string value which saves short list of recently user selected encodings +// separated with comma punctuation mark. +const wchar_t kRecentlySelectedEncoding[] = + L"profile.recently_selected_encodings"; + + +// *************** LOCAL STATE *************** +// These are attached to the machine/installation + +// List of profiles that the app knows about from last run. +const wchar_t kAvailableProfiles[] = L"profiles.available"; + +// The metrics client GUID and session ID. +const wchar_t kMetricsClientID[] = L"user_experience_metrics.client_id"; +const wchar_t kMetricsSessionID[] = L"user_experience_metrics.session_id"; + +// Boolean set to true when we're recording user metrics. +const wchar_t kMetricsIsRecording[] = L"user_experience_metrics.record"; + +// Date/time when the current metrics profile ID was created +// (which hopefully corresponds to first run). +const wchar_t kMetricsClientIDTimestamp[] = + L"user_experience_metrics.client_id_timestamp"; + +// Boolean that specifies whether or not crash reporting and metrics reporting +// are sent over the network for analysis. +const wchar_t kMetricsReportingEnabled[] = + L"user_experience_metrics.reporting_enabled"; + +// Array of strings that are each UMA logs that were supposed to be sent in the +// first minute of a browser session. These logs include things like crash count +// info, etc. +const wchar_t kMetricsInitialLogs[] = + L"user_experience_metrics.initial_logs"; + +// Array of strings that are each UMA logs that were not sent because the +// browser terminated before these accumulated metrics could be sent. These +// logs typically include histograms and memory reports, as well as ongoing +// user activities. +const wchar_t kMetricsOngoingLogs[] = + L"user_experience_metrics.ongoing_logs"; + +// Where profile specific metrics are placed. +const wchar_t kProfileMetrics[] = L"user_experience_metrics.profiles"; + +// The metrics for a profile are stored as dictionary values under the +// path kProfileMetrics. The individual metrics are placed under the path +// kProfileMetrics.kProfilePrefix<hashed-profile-id>. +const wchar_t kProfilePrefix[] = L"profile-"; + +// True if the previous run of the program exited cleanly. +const wchar_t kStabilityExitedCleanly[] = + L"user_experience_metrics.stability.exited_cleanly"; + +// False if we received a session end and either we crashed during processing +// the session end or ran out of time and windows terminated us. +const wchar_t kStabilitySessionEndCompleted[] = + L"user_experience_metrics.stability.session_end_completed"; + +// Number of times the application was launched since last report. +const wchar_t kStabilityLaunchCount[] = + L"user_experience_metrics.stability.launch_count"; + +// Number of times the application exited uncleanly since the last report. +const wchar_t kStabilityCrashCount[] = + L"user_experience_metrics.stability.crash_count"; + +// Number of times the session end did not complete. +const wchar_t kStabilityIncompleteSessionEndCount[] = + L"user_experience_metrics.stability.incomplete_session_end_count"; + +// Number of times a page load event occurred since the last report. +const wchar_t kStabilityPageLoadCount[] = + L"user_experience_metrics.stability.page_load_count"; + +// Number of times a renderer process crashed since the last report. +const wchar_t kStabilityRendererCrashCount[] = + L"user_experience_metrics.stability.renderer_crash_count"; + +// Number of times a renderer started in the sandbox and successfully +// used the sandbox desktop. +const wchar_t kSecurityRendererOnSboxDesktop[] = + L"user_experience_metrics.security.renderer_on_sbox_desktop"; + +// Number of times a renderer started in the sandbox and failed to +// used the sandbox desktop. +const wchar_t kSecurityRendererOnDefaultDesktop[] = + L"user_experience_metrics.security.renderer_on_default_desktop"; + +// Time when the app was last launched, in seconds since the epoch. +const wchar_t kStabilityLaunchTimeSec[] = + L"user_experience_metrics.stability.launch_time_sec"; + +// Time when the app was last known to be running, in seconds since +// the epoch. +const wchar_t kStabilityLastTimestampSec[] = + L"user_experience_metrics.stability.last_timestamp_sec"; + +// Number of milliseconds that the main application process was up since +// the last report. +const wchar_t kStabilityUptimeSec[] = + L"user_experience_metrics.stability.uptime_sec"; + +// This is the location of a list of dictionaries of plugin stability stats. +const wchar_t kStabilityPluginStats[] = + L"user_experience_metrics.stability.plugin_stats"; + +// Number of times the renderer has become non-responsive since the last +// report. +const wchar_t kStabilityRendererHangCount[] = + L"user_experience_metrics.stability.renderer_hang_count"; + +// The keys below are used for the dictionaries in the +// kStabilityPluginStats list. +const wchar_t kStabilityPluginPath[] = L"path"; +const wchar_t kStabilityPluginLaunches[] = L"launches"; +const wchar_t kStabilityPluginInstances[] = L"instances"; +const wchar_t kStabilityPluginCrashes[] = L"crashes"; + +// If true, the user will be prompted to manually launch renderer processes. +const wchar_t kStartRenderersManually[] = L"renderer.start_manually"; + +// A collection of position, size, and other data relating to the browser +// window to restore on startup. +const wchar_t kBrowserWindowPlacement[] = L"browser.window_placement"; + +// A collection of position, size, and other data relating to the task +// manager window to restore on startup. +const wchar_t kTaskManagerWindowPlacement[] = L"task_manager.window_placement"; + +// A collection of position, size, and other data relating to the page info +// window to restore on startup. +const wchar_t kPageInfoWindowPlacement[] = L"page_info.window_placement"; + +// An integer specifying the total number of bytes to be used by the +// renderer's in-memory cache of objects. +const wchar_t kMemoryCacheSize[] = L"renderer.memory_cache.size"; + +// String which specifies where to download files to by default. +const wchar_t kDownloadDefaultDirectory[] = L"download.default_directory"; + +// String which specifies where to save html files to by default. +const wchar_t kSaveFileDefaultDirectory[] = L"savefile.default_directory"; + +// Extensions which should be opened upon completion. +const wchar_t kDownloadExtensionsToOpen[] = L"download.extensions_to_open"; + +// Integer which specifies the frequency in milliseconds for detecting whether +// plugin windows are hung. +const wchar_t kHungPluginDetectFrequency[] = + L"browser.hung_plugin_detect_freq"; + +// Integer which specifies the timeout value to be used for SendMessageTimeout +// to detect a hung plugin window. +const wchar_t kPluginMessageResponseTimeout[] = + L"browser.plugin_message_response_timeout"; + +// String which represents the dictionary name for our spell-checker. +const wchar_t kSpellCheckDictionary[] = L"spellcheck.dictionary"; + +// Dictionary of schemes used by the external protocol handler. +// The value is true if the scheme must be ignored. +const wchar_t kExcludedSchemes[] = L"protocol_handler.excluded_schemes"; + +// Keys used for MAC handling of SafeBrowsing requests. +const wchar_t kSafeBrowsingClientKey[] = L"safe_browsing.client_key"; +const wchar_t kSafeBrowsingWrappedKey[] = L"safe_browsing.wrapped_key"; + +// Integer that specifies the index of the tab the user was on when they +// last visited the options window. +const wchar_t kOptionsWindowLastTabIndex[] = L"options_window.last_tab_index"; + +// The mere fact that this pref is registered signals that we should show the +// First Run Search Information bubble when the first browser window appears. +// This preference is only registered by the first-run procedure. +const wchar_t kShouldShowFirstRunBubble[] = L"show-first-run-bubble"; + +// Signal that we should show the welcome page when we launch Chrome. +const wchar_t kShouldShowWelcomePage[] = L"show-welcome-page"; + +// String containing the last known Google URL. We re-detect this on startup in +// most cases, and use it to send traffic to the correct Google host or with the +// correct Google domain/country code for whatever location the user is in. +const wchar_t kLastKnownGoogleURL[] = L"browser.last_known_google_url"; + +// Integer containing the system GeoID the first time we checked the template +// URL prepopulate data. This is used to avoid adding a whole bunch of new +// search engine choices if prepopulation runs when the user's GeoID differs +// from their previous GeoID. This pref does not exist until prepopulation has +// been run at least once. +const wchar_t kGeoIDAtInstall[] = L"geoid_at_install"; + +// An enum value of how the browser was shut down (see browser_shutdown.h). +const wchar_t kShutdownType[] = L"shutdown.type"; +// Number of processes that were open when the user shut down. +const wchar_t kShutdownNumProcesses[] = L"shutdown.num_processes"; +// Number of processes that were shut down using the slow path. +const wchar_t kShutdownNumProcessesSlow[] = L"shutdown.num_processes_slow"; + +// Number of bookmarks/folders on the bookmark bar/other bookmark folder. +const wchar_t kNumBookmarksOnBookmarkBar[] = + L"user_experience_metrics.num_bookmarks_on_bookmark_bar"; +const wchar_t kNumFoldersOnBookmarkBar[] = + L"user_experience_metrics.num_folders_on_bookmark_bar"; +const wchar_t kNumBookmarksInOtherBookmarkFolder[] = + L"user_experience_metrics.num_bookmarks_in_other_bookmark_folder"; +const wchar_t kNumFoldersInOtherBookmarkFolder[] = + L"user_experience_metrics.num_folders_in_other_bookmark_folder"; + +// Number of keywords. +const wchar_t kNumKeywords[] = L"user_experience_metrics.num_keywords"; + +} // namespace prefs diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h new file mode 100644 index 0000000..eb1f3fc --- /dev/null +++ b/chrome/common/pref_names.h @@ -0,0 +1,167 @@ +// 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 names of various preferences, for easier changing. + +#ifndef CHROME_COMMON_PREF_NAMES_H__ +#define CHROME_COMMON_PREF_NAMES_H__ + +namespace prefs { + +// Profile prefs +extern const wchar_t kHomePage[]; +extern const wchar_t kProfileName[]; +extern const wchar_t kProfileNickname[]; +extern const wchar_t kProfileID[]; +extern const wchar_t kRecentlyViewedModelBiasActiveTabHistory[]; +extern const wchar_t kRecentlyViewedModelSelectionMode[]; +extern const wchar_t kSessionExitedCleanly[]; +extern const wchar_t kRestoreOnStartup[]; +extern const wchar_t kURLsToRestoreOnStartup[]; +extern const wchar_t kApplicationLocale[]; +extern const wchar_t kDefaultCharset[]; +extern const wchar_t kAcceptLanguages[]; +extern const wchar_t kStaticEncodings[]; +extern const wchar_t kShowBookmarkBar[]; +extern const wchar_t kWebKitStandardFontIsSerif[]; +extern const wchar_t kWebKitFixedFontFamily[]; +extern const wchar_t kWebKitSerifFontFamily[]; +extern const wchar_t kWebKitSansSerifFontFamily[]; +extern const wchar_t kWebKitCursiveFontFamily[]; +extern const wchar_t kWebKitFantasyFontFamily[]; +extern const wchar_t kWebKitDefaultFontSize[]; +extern const wchar_t kWebKitDefaultFixedFontSize[]; +extern const wchar_t kWebKitMinimumFontSize[]; +extern const wchar_t kWebKitMinimumLogicalFontSize[]; +extern const wchar_t kWebKitJavascriptEnabled[]; +extern const wchar_t kWebKitJavascriptCanOpenWindowsAutomatically[]; +extern const wchar_t kWebKitLoadsImagesAutomatically[]; +extern const wchar_t kWebKitPluginsEnabled[]; +extern const wchar_t kWebKitDomPasteEnabled[]; +extern const wchar_t kWebKitShrinksStandaloneImagesToFit[]; +extern const wchar_t kWebKitDeveloperExtrasEnabled[]; +extern const wchar_t kWebKitUsesUniversalDetector[]; +extern const wchar_t kWebKitTextAreasAreResizable[]; +extern const wchar_t kWebKitJavaEnabled[]; +extern const wchar_t kAlwaysCreateDestinationsTab[]; +extern const wchar_t kPasswordManagerEnabled[]; +extern const wchar_t kSafeBrowsingEnabled[]; +extern const wchar_t kSearchSuggestEnabled[]; +extern const wchar_t kCookieBehavior[]; +extern const wchar_t kMixedContentFiltering[]; +extern const wchar_t kDefaultSearchProviderSearchURL[]; +extern const wchar_t kDefaultSearchProviderSuggestURL[]; +extern const wchar_t kDefaultSearchProviderName[]; +extern const wchar_t kDefaultSearchProviderID[]; +extern const wchar_t kBlockPopups[]; +extern const wchar_t kPromptForDownload[]; +extern const wchar_t kAlternateErrorPagesEnabled[]; +extern const wchar_t kDnsPrefetchingEnabled[]; +extern const wchar_t kDnsStartupPrefetchList[]; +extern const wchar_t kIpcDisabledMessages[]; +extern const wchar_t kShowHomeButton[]; +extern const wchar_t kRecentlySelectedEncoding[]; + +// Local state +extern const wchar_t kAvailableProfiles[]; + +extern const wchar_t kMetricsClientID[]; +extern const wchar_t kMetricsSessionID[]; +extern const wchar_t kMetricsIsRecording[]; +extern const wchar_t kMetricsClientIDTimestamp[]; +extern const wchar_t kMetricsReportingEnabled[]; +extern const wchar_t kMetricsInitialLogs[]; +extern const wchar_t kMetricsOngoingLogs[]; + +extern const wchar_t kProfileMetrics[]; +extern const wchar_t kProfilePrefix[]; + +extern const wchar_t kStabilityExitedCleanly[]; +extern const wchar_t kStabilitySessionEndCompleted[]; +extern const wchar_t kStabilityLaunchCount[]; +extern const wchar_t kStabilityCrashCount[]; +extern const wchar_t kStabilityIncompleteSessionEndCount[]; +extern const wchar_t kStabilityPageLoadCount[]; +extern const wchar_t kStabilityRendererCrashCount[]; +extern const wchar_t kStabilityLaunchTimeSec[]; +extern const wchar_t kStabilityLastTimestampSec[]; +extern const wchar_t kStabilityUptimeSec[]; +extern const wchar_t kStabilityRendererHangCount[]; + +extern const wchar_t kSecurityRendererOnSboxDesktop[]; +extern const wchar_t kSecurityRendererOnDefaultDesktop[]; + +extern const wchar_t kStabilityPluginStats[]; +extern const wchar_t kStabilityPluginPath[]; +extern const wchar_t kStabilityPluginLaunches[]; +extern const wchar_t kStabilityPluginInstances[]; +extern const wchar_t kStabilityPluginCrashes[]; + +extern const wchar_t kStartRenderersManually[]; +extern const wchar_t kBrowserWindowPlacement[]; +extern const wchar_t kTaskManagerWindowPlacement[]; +extern const wchar_t kPageInfoWindowPlacement[]; +extern const wchar_t kMemoryCacheSize[]; + +extern const wchar_t kDownloadDefaultDirectory[]; +extern const wchar_t kDownloadExtensionsToOpen[]; + +extern const wchar_t kSaveFileDefaultDirectory[]; + +extern const wchar_t kHungPluginDetectFrequency[]; +extern const wchar_t kPluginMessageResponseTimeout[]; + +extern const wchar_t kSpellCheckDictionary[]; + +extern const wchar_t kExcludedSchemes[]; + +extern const wchar_t kSafeBrowsingClientKey[]; +extern const wchar_t kSafeBrowsingWrappedKey[]; + +extern const wchar_t kOptionsWindowLastTabIndex[]; +extern const wchar_t kShouldShowFirstRunBubble[]; +extern const wchar_t kShouldShowWelcomePage[]; + +extern const wchar_t kLastKnownGoogleURL[]; + +extern const wchar_t kGeoIDAtInstall[]; + +extern const wchar_t kShutdownType[]; +extern const wchar_t kShutdownNumProcesses[]; +extern const wchar_t kShutdownNumProcessesSlow[]; + +extern const wchar_t kNumBookmarksOnBookmarkBar[]; +extern const wchar_t kNumFoldersOnBookmarkBar[]; +extern const wchar_t kNumBookmarksInOtherBookmarkFolder[]; +extern const wchar_t kNumFoldersInOtherBookmarkFolder[]; + +extern const wchar_t kNumKeywords[]; +} + +#endif // CHROME_COMMON_PREF_NAMES_H__ diff --git a/chrome/common/pref_service.cc b/chrome/common/pref_service.cc new file mode 100644 index 0000000..17d455b --- /dev/null +++ b/chrome/common/pref_service.cc @@ -0,0 +1,709 @@ +// 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 "chrome/common/pref_service.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/stl_util-inl.h" + +#include "generated_resources.h" + +namespace { + +// The number of milliseconds we'll wait to do a write of chrome prefs to disk. +// This lets us batch together write operations. +static const int kCommitIntervalMs = 10000; + +// Replaces the given file's content with the given data. This allows the +// preferences to be written to disk on a background thread. +class SaveLaterTask : public Task { + public: + SaveLaterTask(const std::wstring& file_name, + const std::string& data) + : file_name_(file_name), + data_(data) { + } + + void Run() { + // Write the data to a temp file then rename to avoid data loss if we crash + // while writing the file. + std::wstring tmp_file_name = file_name_ + L".tmp"; + int bytes_written = file_util::WriteFile(tmp_file_name, data_.c_str(), + static_cast<int>(data_.length())); + if (bytes_written != -1) { + MoveFileEx(tmp_file_name.c_str(), file_name_.c_str(), + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); + } + } + + private: + std::wstring file_name_; + std::string data_; + + DISALLOW_EVIL_CONSTRUCTORS(SaveLaterTask); +}; + +// A helper function for RegisterLocalized*Pref that creates a Value* based on +// the string value in the locale dll. Because we control the values in a +// locale dll, this should always return a Value of the appropriate type. +Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) { + std::wstring resource_string = l10n_util::GetString(message_id); + DCHECK(!resource_string.empty()); + switch (type) { + case Value::TYPE_BOOLEAN: { + if (L"true" == resource_string) + return Value::CreateBooleanValue(true); + if (L"false" == resource_string) + return Value::CreateBooleanValue(false); + break; + } + + case Value::TYPE_INTEGER: { + int num_int = 0; + int parsed_values = swscanf_s(resource_string.c_str(), L"%d", &num_int); + // This is a trusted value (comes from our locale dll), so it should + // successfully parse. + DCHECK(parsed_values == 1); + return Value::CreateIntegerValue(num_int); + break; + } + + case Value::TYPE_REAL: { + double num_double = 0.0; + int parsed_values = swscanf_s(resource_string.c_str(), L"%lf", + &num_double); + // This is a trusted value (comes from our locale dll), so it should + // successfully parse. + DCHECK(parsed_values == 1); + return Value::CreateRealValue(num_double); + break; + } + + case Value::TYPE_STRING: { + return Value::CreateStringValue(resource_string); + break; + } + + default: { + DCHECK(false) << + "list and dictionary types can not have default locale values"; + } + } + NOTREACHED(); + return Value::CreateNullValue(); +} + +} // namespace + +PrefService::PrefService() + : persistent_(new DictionaryValue), + transient_(new DictionaryValue), + save_preferences_factory_(NULL) { +} + +PrefService::PrefService(const std::wstring& pref_filename) + : persistent_(new DictionaryValue), + transient_(new DictionaryValue), + pref_filename_(pref_filename), +#pragma warning(suppress: 4355) // Okay to pass "this" here. + save_preferences_factory_(this) { + LoadPersistentPrefs(pref_filename_); +} + +PrefService::~PrefService() { + DCHECK(CalledOnValidThread()); + + // Verify that there are no pref observers when we shut down. + for (PrefObserverMap::iterator it = pref_observers_.begin(); + it != pref_observers_.end(); ++it) { + NotificationObserverList::Iterator obs_iterator(*(it->second)); + if (obs_iterator.GetNext()) { + LOG(WARNING) << "pref observer found at shutdown " << it->first; + } + } + + STLDeleteContainerPointers(prefs_.begin(), prefs_.end()); + prefs_.clear(); + STLDeleteContainerPairSecondPointers(pref_observers_.begin(), + pref_observers_.end()); + pref_observers_.clear(); +} + +bool PrefService::LoadPersistentPrefs(const std::wstring& file_path) { + DCHECK(!file_path.empty()); + DCHECK(CalledOnValidThread()); + + JSONFileValueSerializer serializer(file_path); + Value* root = NULL; + if (serializer.Deserialize(&root)) { + // Preferences should always have a dictionary root. + if (!root->IsType(Value::TYPE_DICTIONARY)) { + delete root; + return false; + } + + persistent_.reset(static_cast<DictionaryValue*>(root)); + return true; + } + + return false; +} + +void PrefService::ReloadPersistentPrefs() { + DCHECK(CalledOnValidThread()); + + JSONFileValueSerializer serializer(pref_filename_); + Value* root; + if (serializer.Deserialize(&root)) { + // Preferences should always have a dictionary root. + if (!root->IsType(Value::TYPE_DICTIONARY)) { + delete root; + return; + } + + persistent_.reset(static_cast<DictionaryValue*>(root)); + for (PreferenceSet::iterator it = prefs_.begin(); + it != prefs_.end(); ++it) { + (*it)->root_pref_ = persistent_.get(); + } + } +} + +bool PrefService::SavePersistentPrefs(Thread* thread) const { + DCHECK(!pref_filename_.empty()); + DCHECK(CalledOnValidThread()); + + // TODO(tc): Do we want to prune webkit preferences that match the default + // value? + std::string data; + JSONStringValueSerializer serializer(&data); + serializer.set_pretty_print(true); + if (!serializer.Serialize(*(persistent_.get()))) + return false; + + SaveLaterTask* task = new SaveLaterTask(pref_filename_, data); + if (thread != NULL) { + // We can use the background thread, it will take ownership of the task. + thread->message_loop()->PostTask(FROM_HERE, task); + } else { + // In unit test mode, we have no background thread, just execute. + task->Run(); + delete task; + } + return true; +} + +void PrefService::ScheduleSavePersistentPrefs(Thread* thread) { + if (!save_preferences_factory_.empty()) + return; + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + save_preferences_factory_.NewRunnableMethod( + &PrefService::SavePersistentPrefs, thread), + kCommitIntervalMs); +} + +void PrefService::RegisterBooleanPref(const wchar_t* path, + bool default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateBooleanValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterIntegerPref(const wchar_t* path, + int default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateIntegerValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterRealPref(const wchar_t* path, + double default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateRealValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterStringPref(const wchar_t* path, + const std::wstring& default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateStringValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterListPref(const wchar_t* path) { + Preference* pref = new Preference(persistent_.get(), path, + new ListValue); + RegisterPreference(pref); +} + +void PrefService::RegisterDictionaryPref(const wchar_t* path) { + Preference* pref = new Preference(persistent_.get(), path, + new DictionaryValue()); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedBooleanPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id)); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedIntegerPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id)); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedRealPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_REAL, locale_default_message_id)); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedStringPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id)); + RegisterPreference(pref); +} + +bool PrefService::IsPrefRegistered(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + // TODO(tc): We can remove this method and just use FindPreference. + return FindPreference(path) ? true : false; +} + +bool PrefService::GetBoolean(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + bool result = false; + if (transient_->GetBoolean(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsBoolean(&result); + DCHECK(rv); + return result; +} + +int PrefService::GetInteger(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + int result = 0; + if (transient_->GetInteger(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsInteger(&result); + DCHECK(rv); + return result; +} + +double PrefService::GetReal(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + double result = 0.0; + if (transient_->GetReal(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsReal(&result); + DCHECK(rv); + return result; +} + +std::wstring PrefService::GetString(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + std::wstring result; + if (transient_->GetString(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsString(&result); + DCHECK(rv); + return result; +} + +bool PrefService::HasPrefPath(const wchar_t* path) const { + Value* value = NULL; + return (transient_->Get(path, &value) || persistent_->Get(path, &value)); +} + +const PrefService::Preference* PrefService::FindPreference( + const wchar_t* pref_name) const { + DCHECK(CalledOnValidThread()); + Preference p(NULL, pref_name, NULL); + PreferenceSet::const_iterator it = prefs_.find(&p); + return it == prefs_.end() ? NULL : *it; +} + +const DictionaryValue* PrefService::GetDictionary(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + DictionaryValue* result = NULL; + if (transient_->GetDictionary(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to read an unregistered pref: " << path; + return NULL; + } + const Value* value = pref->GetValue(); + if (value->GetType() == Value::TYPE_NULL) + return NULL; + return static_cast<const DictionaryValue*>(value); +} + +const ListValue* PrefService::GetList(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + ListValue* result = NULL; + if (transient_->GetList(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to read an unregistered pref: " << path; + return NULL; + } + const Value* value = pref->GetValue(); + if (value->GetType() == Value::TYPE_NULL) + return NULL; + return static_cast<const ListValue*>(value); +} + +void PrefService::AddPrefObserver(const wchar_t* path, + NotificationObserver* obs) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to add an observer for an unregistered pref: " + << path; + return; + } + + // Get the pref observer list associated with the path. + NotificationObserverList* observer_list = NULL; + PrefObserverMap::iterator observer_iterator = pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + observer_list = new NotificationObserverList; + pref_observers_[path] = observer_list; + } else { + observer_list = observer_iterator->second; + } + + // Verify that this observer doesn't already exist. + NotificationObserverList::Iterator it(*observer_list); + NotificationObserver* existing_obs; + while (existing_obs = it.GetNext()) { + DCHECK(existing_obs != obs) << path << " observer already registered"; + if (existing_obs == obs) + return; + } + + // Ok, safe to add the pref observer. + observer_list->AddObserver(obs); +} + +void PrefService::RemovePrefObserver(const wchar_t* path, + NotificationObserver* obs) { + DCHECK(CalledOnValidThread()); + + PrefObserverMap::iterator observer_iterator = pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + return; + } + + NotificationObserverList* observer_list = observer_iterator->second; + observer_list->RemoveObserver(obs); +} + +void PrefService::RegisterPreference(Preference* pref) { + DCHECK(CalledOnValidThread()); + + if (FindPreference(pref->name().c_str())) { + DCHECK(false) << "Tried to register duplicate pref " << pref->name(); + delete pref; + return; + } + prefs_.insert(pref); +} + +void PrefService::ClearPref(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to clear an unregistered pref: " << path; + return; + } + + transient_->Remove(path, NULL); + Value* value; + bool has_old_value = persistent_->Get(path, &value); + persistent_->Remove(path, NULL); + + if (has_old_value) + FireObservers(path); +} + +void PrefService::SetBoolean(const wchar_t* path, bool value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_BOOLEAN) { + DCHECK(false) << "Wrong type for SetBoolean: " << path; + return; + } + + scoped_ptr<Value> old_value(GetPrefCopy(path)); + bool rv = persistent_->SetBoolean(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetInteger(const wchar_t* path, int value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_INTEGER) { + DCHECK(false) << "Wrong type for SetInteger: " << path; + return; + } + + scoped_ptr<Value> old_value(GetPrefCopy(path)); + bool rv = persistent_->SetInteger(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetReal(const wchar_t* path, double value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_REAL) { + DCHECK(false) << "Wrong type for SetReal: " << path; + return; + } + + scoped_ptr<Value> old_value(GetPrefCopy(path)); + bool rv = persistent_->SetReal(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetString(const wchar_t* path, const std::wstring& value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_STRING) { + DCHECK(false) << "Wrong type for SetString: " << path; + return; + } + + scoped_ptr<Value> old_value(GetPrefCopy(path)); + bool rv = persistent_->SetString(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +DictionaryValue* PrefService::GetMutableDictionary(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to get an unregistered pref: " << path; + return NULL; + } + if (pref->type() != Value::TYPE_DICTIONARY) { + DCHECK(false) << "Wrong type for GetMutableDictionary: " << path; + return NULL; + } + + DictionaryValue* dict = NULL; + bool rv = persistent_->GetDictionary(path, &dict); + if (!rv) { + dict = new DictionaryValue; + rv = persistent_->Set(path, dict); + DCHECK(rv); + } + return dict; +} + +ListValue* PrefService::GetMutableList(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + DCHECK(false) << "Trying to get an unregistered pref: " << path; + return NULL; + } + if (pref->type() != Value::TYPE_LIST) { + DCHECK(false) << "Wrong type for GetMutableList: " << path; + return NULL; + } + + ListValue* list = NULL; + bool rv = persistent_->GetList(path, &list); + if (!rv) { + list = new ListValue; + rv = persistent_->Set(path, list); + DCHECK(rv); + } + return list; +} + +Value* PrefService::GetPrefCopy(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + DCHECK(pref); + return pref->GetValue()->DeepCopy(); +} + +void PrefService::FireObserversIfChanged(const wchar_t* path, + const Value* old_value) { + Value* new_value = NULL; + persistent_->Get(path, &new_value); + if (!old_value->Equals(new_value)) + FireObservers(path); +} + +void PrefService::FireObservers(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + // Convert path to a std::wstring because the Details constructor requires a + // class. + std::wstring path_str(path); + PrefObserverMap::iterator observer_iterator = pref_observers_.find(path_str); + if (observer_iterator == pref_observers_.end()) + return; + + NotificationObserverList::Iterator it(*(observer_iterator->second)); + NotificationObserver* observer; + while (observer = it.GetNext()) { + observer->Observe(NOTIFY_PREF_CHANGED, + Source<PrefService>(this), + Details<std::wstring>(&path_str)); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// PrefService::Preference + +PrefService::Preference::Preference(DictionaryValue* root_pref, + const wchar_t* name, + Value* default_value) + : root_pref_(root_pref), + name_(name), + default_value_(default_value), + type_(Value::TYPE_NULL) { + DCHECK(name); + + if (default_value) { + type_ = default_value->GetType(); + DCHECK(type_ != Value::TYPE_NULL && type_ != Value::TYPE_BINARY) << + "invalid preference type: " << type_; + } + + // We set the default value of lists and dictionaries to be null so it's + // easier for callers to check for empty list/dict prefs. + if (Value::TYPE_LIST == type_ || Value::TYPE_DICTIONARY == type_) + default_value_.reset(Value::CreateNullValue()); +} + +const Value* PrefService::Preference::GetValue() const { + DCHECK(NULL != root_pref_) << + "Must register pref before getting its value"; + + Value* temp_value = NULL; + if (root_pref_->Get(name_.c_str(), &temp_value) && + temp_value->GetType() == type_) { + return temp_value; + } + + // Pref not found, just return the app default. + return default_value_.get(); +} + +bool PrefService::Preference::IsDefaultValue() const { + DCHECK(default_value_.get()); + return default_value_->Equals(GetValue()); +} diff --git a/chrome/common/pref_service.h b/chrome/common/pref_service.h new file mode 100644 index 0000000..fed3cf2 --- /dev/null +++ b/chrome/common/pref_service.h @@ -0,0 +1,263 @@ +// 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 provides a way to access the application's current preferences. +// This service has two preference stores, one for "persistent" preferences, +// which get serialized for use in the next session, and one for "transient" +// preferences, which are in effect for only the current session +// (this usually encodes things like command-line switches). +// +// Calling the getter functions in this class basically looks at both the +// persistent and transient stores, where any corresponding value in the +// transient store overrides the one in the persistent store. + +#ifndef CHROME_COMMON_PREF_SERVICE_H__ +#define CHROME_COMMON_PREF_SERVICE_H__ + +#include <hash_map> +#include <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/non_thread_safe.h" +#include "base/observer_list.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +class NotificationObserver; +class Preference; +class Thread; + +class PrefService : public NonThreadSafe { + public: + + // A helper class to store all the information associated with a preference. + class Preference { + public: + + // The type of the preference is determined by the type of |default_value|. + // Therefore, the type needs to be a boolean, integer, real, string, + // dictionary (a branch), or list. You shouldn't need to construct this on + // your own, use the PrefService::Register*Pref methods instead. + // |default_value| will be owned by the Preference object. + Preference(DictionaryValue* root_pref, + const wchar_t* name, + Value* default_value); + ~Preference() {} + + Value::ValueType type() const { return type_; } + + // Returns the name of the Preference (i.e., the key, e.g., + // browser.window_placement). + const std::wstring name() const { return name_; } + + // Returns the value of the Preference. If there is no user specified value, + // it returns the default value. + const Value* GetValue() const; + + // Returns true if the current value matches the default value. + bool IsDefaultValue() const; + + private: + friend class PrefService; + + Value::ValueType type_; + std::wstring name_; + scoped_ptr<Value> default_value_; + + // A reference to the pref service's persistent prefs. + DictionaryValue* root_pref_; + + DISALLOW_EVIL_CONSTRUCTORS(Preference); + }; + + // |pref_filename| is the path to the prefs file we will try to load or save to. + explicit PrefService(const std::wstring& pref_filename); + ~PrefService(); + + // Reloads the data from file. This should only be called when the importer + // is running during first run, and the main process may not change pref + // values while the importer process is running. + void ReloadPersistentPrefs(); + + // Writes the data to disk on the provided thread. In Chrome, |thread| should + // be the file thread. The return value only reflects whether serialization + // was successful; we don't know whether the data actually made it on disk + // (since it's on a different thread). This should only be used if we need + // to save immediately (basically, during shutdown). Otherwise, you should + // use ScheduleSavePersistentPrefs. + bool SavePersistentPrefs(Thread* thread) const; + + // Starts a timer that ends up saving the preferences. This helps to batch + // together save requests that happen in a close time frame so we don't write + // to disk too frequently. + void ScheduleSavePersistentPrefs(Thread* thread); + + DictionaryValue* transient() { return transient_.get(); } + + // Make the PrefService aware of a pref. + void RegisterBooleanPref(const wchar_t* path, + bool default_value); + void RegisterIntegerPref(const wchar_t* path, + int default_value); + void RegisterRealPref(const wchar_t* path, + double default_value); + void RegisterStringPref(const wchar_t* path, + const std::wstring& default_value); + void RegisterListPref(const wchar_t* path); + void RegisterDictionaryPref(const wchar_t* path); + + // These varients use a default value from the locale dll instead. + void RegisterLocalizedBooleanPref(const wchar_t* path, + int locale_default_message_id); + void RegisterLocalizedIntegerPref(const wchar_t* path, + int locale_default_message_id); + void RegisterLocalizedRealPref(const wchar_t* path, + int locale_default_message_id); + void RegisterLocalizedStringPref(const wchar_t* path, + int locale_default_message_id); + + // Returns whether the specified pref has been registered. + bool IsPrefRegistered(const wchar_t* path); + + // If the path is valid and the value at the end of the path matches the type + // specified, it will return the specified value. Otherwise, the default value + // (set when the pref was registered) will be returned. + bool GetBoolean(const wchar_t* path) const; + int GetInteger(const wchar_t* path) const; + double GetReal(const wchar_t* path) const; + std::wstring GetString(const wchar_t* path) const; + + // Returns the branch if it exists. If it's not a branch or the branch does + // not exist, returns NULL. This does + const DictionaryValue* GetDictionary(const wchar_t* path) const; + const ListValue* GetList(const wchar_t* path) const; + + // If the pref at the given path changes, we call the observer's Observe + // method with NOTIFY_PREF_CHANGED. + void AddPrefObserver(const wchar_t* path, NotificationObserver* obs); + void RemovePrefObserver(const wchar_t* path, NotificationObserver* obs); + + // Removes a user pref and restores the pref to its default value. + void ClearPref(const wchar_t* path); + + // If the path is valid (i.e., registered), update the pref value. + void SetBoolean(const wchar_t* path, bool value); + void SetInteger(const wchar_t* path, int value); + void SetReal(const wchar_t* path, double value); + void SetString(const wchar_t* path, const std::wstring& value); + + // Used to set the value of dictionary or list values in the pref tree. This + // will create a dictionary or list if one does not exist in the pref tree. + // This method returns NULL only if you're requesting an unregistered pref or + // a non-dict/non-list pref. + // WARNING: Changes to the dictionary or list will not automatically notify + // pref observers. TODO(tc): come up with a way to still fire observers. + DictionaryValue* GetMutableDictionary(const wchar_t* path); + ListValue* GetMutableList(const wchar_t* path); + + // Returns true if a value has been set for the specified path. + // NOTE: this is NOT the same as IsPrefRegistered. In particular + // IsPrefRegistered returns whether RegisterXXX has been invoked, where as + // this checks if a value exists for the path. + bool HasPrefPath(const wchar_t* path) const; + + class PreferencePathComparator { + public: + bool operator() (Preference* lhs, Preference* rhs) const { + return lhs->name() < rhs->name(); + } + }; + typedef std::set<Preference*, PreferencePathComparator> PreferenceSet; + const PreferenceSet& preference_set() const { return prefs_; } + + // A helper method to quickly look up a preference. Returns NULL if the + // preference is not registered. + const Preference* FindPreference(const wchar_t* pref_name) const; + + private: + FRIEND_TEST(PrefServiceTest, Basic); + FRIEND_TEST(PrefServiceTest, Overlay); + FRIEND_TEST(PrefServiceTest, Observers); + FRIEND_TEST(PrefServiceTest, LocalizedPrefs); + FRIEND_TEST(PrefServiceTest, NoObserverFire); + FRIEND_TEST(PrefServiceTest, HasPrefPath); + + FRIEND_TEST(PrefMemberTest, BasicGetAndSet); + FRIEND_TEST(PrefMemberTest, TwoPrefs); + FRIEND_TEST(PrefMemberTest, Observer); + + // This constructor is used only for some unittests. It doesn't try to load + // any existing prefs from a file. + PrefService(); + + // Reads the data from the given file, returning true on success. + bool LoadPersistentPrefs(const std::wstring& file_path); + + // Add a preference to the PreferenceMap. If the pref already exists, return + // false. This method takes ownership of |pref|. + void RegisterPreference(Preference* pref); + + // Returns a copy of the current pref value. The caller is responsible for + // deleting the returned object. + Value* GetPrefCopy(const wchar_t* pref_name); + + // For the given pref_name, fire any observer of the pref. + void FireObservers(const wchar_t* pref_name); + + // For the given pref_name, fire any observer of the pref only if |old_value| + // is different from the current value. + void FireObserversIfChanged(const wchar_t* pref_name, + const Value* old_value); + + scoped_ptr<DictionaryValue> persistent_; + scoped_ptr<DictionaryValue> transient_; + + // The filename that we're loading/saving the prefs to. + std::wstring pref_filename_; + + // Task used by ScheduleSavePersistentPrefs to avoid lots of little saves. + ScopedRunnableMethodFactory<PrefService> save_preferences_factory_; + + // A set of all the registered Preference objects. + PreferenceSet prefs_; + + // A map from pref names to a list of observers. Observers get fired in the + // order they are added. + typedef ObserverList<NotificationObserver> NotificationObserverList; + typedef stdext::hash_map<std::wstring, NotificationObserverList*> + PrefObserverMap; + PrefObserverMap pref_observers_; + + DISALLOW_EVIL_CONSTRUCTORS(PrefService); +}; + +#endif // CHROME_COMMON_PREF_SERVICE_H__ diff --git a/chrome/common/pref_service_uitest.cc b/chrome/common/pref_service_uitest.cc new file mode 100644 index 0000000..d0c56ae --- /dev/null +++ b/chrome/common/pref_service_uitest.cc @@ -0,0 +1,159 @@ +// 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 "base/command_line.h" +#include "base/file_util.h" +#include "base/values.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/window_proxy.h" +#include "chrome/test/ui/ui_test.h" + +class PreferenceServiceTest : public UITest { +public: + void SetUp() { + PathService::Get(base::DIR_TEMP, &tmp_profile_); + file_util::AppendToPath(&tmp_profile_, L"tmp_profile"); + + // Create a fresh, empty copy of this directory. + file_util::Delete(tmp_profile_, true); + ::CreateDirectory(tmp_profile_.c_str(), NULL); + + std::wstring reference_pref_file(test_data_directory_); + file_util::AppendToPath(&reference_pref_file, L"profiles"); + file_util::AppendToPath(&reference_pref_file, L"window_placement"); + file_util::AppendToPath(&reference_pref_file, chrome::kLocalStateFilename); + + tmp_pref_file_ = tmp_profile_; + file_util::AppendToPath(&tmp_pref_file_, chrome::kLocalStateFilename); + + ASSERT_TRUE(file_util::PathExists(reference_pref_file)); + + // Copy only the Local State file, the rest will be automatically created + ASSERT_TRUE(::CopyFileW(reference_pref_file.c_str(), tmp_pref_file_.c_str(), + TRUE)); + + // Make the copy writable + ASSERT_TRUE(::SetFileAttributesW(tmp_pref_file_.c_str(), + FILE_ATTRIBUTE_NORMAL)); + + CommandLine::AppendSwitchWithValue(&launch_arguments_, + switches::kUserDataDir, + tmp_profile_); + } + + bool LaunchAppWithProfile() { + if (!file_util::PathExists(tmp_pref_file_)) + return false; + UITest::SetUp(); + return true; + } + + void TearDown() { + UITest::TearDown(); + + const int kWaitForDeleteMs = 100; + int num_retries = 5; + while (num_retries > 0) { + file_util::Delete(tmp_profile_, true); + if (!file_util::PathExists(tmp_profile_)) + break; + --num_retries; + Sleep(kWaitForDeleteMs); + } + EXPECT_FALSE(file_util::PathExists(tmp_profile_)); + } + +public: + std::wstring tmp_pref_file_; + std::wstring tmp_profile_; +}; + +TEST_F(PreferenceServiceTest, PreservedWindowPlacementIsLoaded) { + // The window should open with the reference profile + ASSERT_TRUE(LaunchAppWithProfile()); + + ASSERT_TRUE(file_util::PathExists(tmp_pref_file_)); + + JSONFileValueSerializer deserializer(tmp_pref_file_); + Value* root = NULL; + ASSERT_TRUE(deserializer.Deserialize(&root)); + + ASSERT_TRUE(root); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + + DictionaryValue* root_dict = static_cast<DictionaryValue*>(root); + + // Retrieve the screen rect for the launched window + scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + scoped_ptr<WindowProxy> window( + automation()->GetWindowForBrowser(browser.get())); + HWND hWnd; + ASSERT_TRUE(window->GetHWND(&hWnd)); + + WINDOWPLACEMENT window_placement; + ASSERT_TRUE(GetWindowPlacement(hWnd, &window_placement)); + + // Retrieve the expected rect values from "Preferences" + int bottom = 0; + std::wstring kBrowserWindowPlacement(prefs::kBrowserWindowPlacement); + ASSERT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".bottom", + &bottom)); + ASSERT_EQ(bottom, window_placement.rcNormalPosition.bottom); + + int top = 0; + ASSERT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".top", + &top)); + ASSERT_EQ(top, window_placement.rcNormalPosition.top); + + int left = 0; + ASSERT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".left", + &left)); + ASSERT_EQ(left, window_placement.rcNormalPosition.left); + + int right = 0; + ASSERT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".right", + &right)); + ASSERT_EQ(right, window_placement.rcNormalPosition.right); + + // Find if launched window is maximized + bool is_window_maximized = (window_placement.showCmd == SW_MAXIMIZE); + + bool is_maximized = false; + ASSERT_TRUE(root_dict->GetBoolean(kBrowserWindowPlacement + L".maximized", + &is_maximized)); + ASSERT_EQ(is_maximized, is_window_maximized); + delete root; +} diff --git a/chrome/common/pref_service_unittest.cc b/chrome/common/pref_service_unittest.cc new file mode 100644 index 0000000..23c5d40 --- /dev/null +++ b/chrome/common/pref_service_unittest.cc @@ -0,0 +1,432 @@ +// 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 "chrome/common/chrome_paths.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_types.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/test/data/resource.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class PrefServiceTest : public testing::Test { +protected: + virtual void SetUp() { + // Name a subdirectory of the temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + file_util::AppendToPath(&test_dir_, L"PrefServiceTest"); + + // Create a fresh, empty copy of this directory. + file_util::Delete(test_dir_, true); + CreateDirectory(test_dir_.c_str(), NULL); + + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_)); + file_util::AppendToPath(&data_dir_, L"pref_service"); + ASSERT_TRUE(file_util::PathExists(data_dir_)); + } + virtual void TearDown() { + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + std::wstring test_dir_; + // the path to the directory where the test data is stored + std::wstring data_dir_; +}; + +class TestPrefObserver : public NotificationObserver { + public: + TestPrefObserver(const PrefService* prefs, const std::wstring& pref_name, + const std::wstring& new_pref_value) + : observer_fired_(false), + prefs_(prefs), + pref_name_(pref_name), + new_pref_value_(new_pref_value) { + } + virtual ~TestPrefObserver() {} + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + EXPECT_EQ(type, NOTIFY_PREF_CHANGED); + PrefService* prefs_in = Source<PrefService>(source).ptr(); + EXPECT_EQ(prefs_in, prefs_); + std::wstring* pref_name_in = Details<std::wstring>(details).ptr(); + EXPECT_EQ(*pref_name_in, pref_name_); + EXPECT_EQ(new_pref_value_, prefs_in->GetString(L"homepage")); + observer_fired_ = true; + } + + bool observer_fired() { return observer_fired_; } + + void Reset(const std::wstring& new_pref_value) { + observer_fired_ = false; + new_pref_value_ = new_pref_value; + } + + private: + bool observer_fired_; + const PrefService* prefs_; + const std::wstring pref_name_; + std::wstring new_pref_value_; +}; + +} // anonymous namespace + +TEST_F(PrefServiceTest, Basic) { + PrefService prefs; + + // Test that it fails on nonexistent file. + std::wstring bogus_input_file = data_dir_; + file_util::AppendToPath(&bogus_input_file, L"read.txt"); + EXPECT_FALSE(prefs.LoadPersistentPrefs(bogus_input_file)); + + // Test that the persistent value can be loaded. + std::wstring input_file = data_dir_; + file_util::AppendToPath(&input_file, L"read.json"); + ASSERT_TRUE(file_util::PathExists(input_file)); + ASSERT_TRUE(prefs.LoadPersistentPrefs(input_file)); + + // Register test prefs. + const wchar_t kNewWindowsInTabs[] = L"tabs.new_windows_in_tabs"; + const wchar_t kMaxTabs[] = L"tabs.max_tabs"; + prefs.RegisterStringPref(prefs::kHomePage, L""); + prefs.RegisterBooleanPref(kNewWindowsInTabs, false); + prefs.RegisterIntegerPref(kMaxTabs, 0); + + std::wstring microsoft(L"http://www.microsoft.com"); + std::wstring cnn(L"http://www.cnn.com"); + std::wstring homepage(L"http://www.example.com"); + + EXPECT_EQ(cnn, prefs.GetString(prefs::kHomePage)); + + // Now test that the transient value overrides the persistent value, + // without actually altering the persistent store. + EXPECT_TRUE(prefs.transient()->SetString(prefs::kHomePage, microsoft)); + EXPECT_TRUE(prefs.transient()->GetString(prefs::kHomePage, &homepage)); + EXPECT_EQ(microsoft, homepage); + + EXPECT_EQ(microsoft, prefs.GetString(prefs::kHomePage)); + + // Test reading some other data types from sub-dictionaries, and + // writing to the persistent store. + EXPECT_TRUE(prefs.GetBoolean(kNewWindowsInTabs)); + prefs.SetBoolean(kNewWindowsInTabs, false); + EXPECT_FALSE(prefs.GetBoolean(kNewWindowsInTabs)); + + EXPECT_EQ(20, prefs.GetInteger(kMaxTabs)); + prefs.SetInteger(kMaxTabs, 10); + EXPECT_EQ(10, prefs.GetInteger(kMaxTabs)); + + // Serialize and compare to expected output. + std::wstring output_file = test_dir_; + file_util::AppendToPath(&output_file, L"write.json"); + prefs.pref_filename_ = output_file; + ASSERT_TRUE(prefs.SavePersistentPrefs(NULL)); + std::wstring golden_output_file = data_dir_; + file_util::AppendToPath(&golden_output_file, L"write.golden.json"); + ASSERT_TRUE(file_util::PathExists(golden_output_file)); + ASSERT_TRUE(file_util::ContentsEqual(golden_output_file, output_file)); +} + +TEST_F(PrefServiceTest, Overlay) { + const std::string transient = + "{\"bool\":true, \"int\":2, \"real\":2.0, \"string\":\"transient\"," + "\"dictionary\":{\"value\":\"transient\"}," + "\"list\":[\"transient\"]}"; + + std::wstring persistent_string(L"persistent"); + std::wstring transient_string(L"transient"); + + PrefService prefs; + + std::wstring persistent_file = data_dir_; + file_util::AppendToPath(&persistent_file, L"overlay.json"); + EXPECT_TRUE(prefs.LoadPersistentPrefs(persistent_file)); + + Value* transient_value; + { + JSONStringValueSerializer serializer(transient); + ASSERT_TRUE(serializer.Deserialize(&transient_value)); + } + prefs.transient()->Set(transient_string, transient_value); + + Value* both_transient_value; + { + JSONStringValueSerializer serializer(transient); + ASSERT_TRUE(serializer.Deserialize(&both_transient_value)); + } + prefs.transient()->Set(L"both", both_transient_value); + + // Register test prefs + const wchar_t* kTypes[] = { L"neither.", L"persistent.", L"transient.", L"both." }; + for (size_t i = 0; i < arraysize(kTypes); ++i) { + wchar_t temp[1024]; + wcscpy_s(temp, kTypes[i]); + wcscat_s(temp, L"bool"); + prefs.RegisterBooleanPref(temp, false); + + wcscpy_s(temp, kTypes[i]); + wcscat_s(temp, L"int"); + prefs.RegisterIntegerPref(temp, 0); + + wcscpy_s(temp, kTypes[i]); + wcscat_s(temp, L"real"); + prefs.RegisterRealPref(temp, 0.0); + + wcscpy_s(temp, kTypes[i]); + wcscat_s(temp, L"string"); + prefs.RegisterStringPref(temp, L""); + + wcscpy_s(temp, kTypes[i]); + wcscat_s(temp, L"list"); + prefs.RegisterListPref(temp); + + wcscpy_s(temp, kTypes[i]); + wcscat_s(temp, L"dictionary"); + prefs.RegisterDictionaryPref(temp); + } + + ASSERT_FALSE(prefs.GetBoolean(L"neither.bool")); + ASSERT_FALSE(prefs.GetBoolean(L"persistent.bool")); + ASSERT_TRUE(prefs.GetBoolean(L"transient.bool")); + ASSERT_TRUE(prefs.GetBoolean(L"both.bool")); + + ASSERT_EQ(0, prefs.GetInteger(L"neither.int")); + ASSERT_EQ(1, prefs.GetInteger(L"persistent.int")); + ASSERT_EQ(2, prefs.GetInteger(L"transient.int")); + ASSERT_EQ(2, prefs.GetInteger(L"both.int")); + + ASSERT_EQ(0.0, prefs.GetReal(L"neither.real")); + ASSERT_EQ(1.0, prefs.GetReal(L"persistent.real")); + ASSERT_EQ(2.0, prefs.GetReal(L"transient.real")); + ASSERT_EQ(2.0, prefs.GetReal(L"both.real")); + + ASSERT_EQ(std::wstring(), prefs.GetString(L"neither.string")); + ASSERT_EQ(persistent_string, prefs.GetString(L"persistent.string")); + ASSERT_EQ(transient_string, prefs.GetString(L"transient.string")); + ASSERT_EQ(transient_string, prefs.GetString(L"both.string")); + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"neither.dictionary"); + ASSERT_FALSE(result_value); + } + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"persistent.dictionary"); + ASSERT_TRUE(result_value); + std::wstring result_string; + ASSERT_TRUE(result_value->GetString(L"value", &result_string)); + ASSERT_EQ(persistent_string, result_string); + } + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"transient.dictionary"); + ASSERT_TRUE(result_value); + std::wstring result_string; + ASSERT_TRUE(result_value->GetString(L"value", &result_string)); + ASSERT_EQ(transient_string, result_string); + } + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"both.dictionary"); + ASSERT_TRUE(result_value); + std::wstring result_string; + ASSERT_TRUE(result_value->GetString(L"value", &result_string)); + ASSERT_EQ(transient_string, result_string); + } + + { + const ListValue* result_value = prefs.GetList(L"neither.list"); + ASSERT_FALSE(result_value); + } + + { + const ListValue* result_value = prefs.GetList(L"persistent.list"); + ASSERT_TRUE(result_value); + Value* member_value; + ASSERT_TRUE(result_value->Get(0, &member_value)); + ASSERT_TRUE(member_value); + std::wstring result_string; + ASSERT_TRUE(member_value->GetAsString(&result_string)); + ASSERT_EQ(persistent_string, result_string); + } + + { + const ListValue* result_value = prefs.GetList(L"transient.list"); + ASSERT_TRUE(result_value); + Value* member_value; + ASSERT_TRUE(result_value->Get(0, &member_value)); + ASSERT_TRUE(member_value); + std::wstring result_string; + ASSERT_TRUE(member_value->GetAsString(&result_string)); + ASSERT_EQ(transient_string, result_string); + } + + { + const ListValue* result_value = prefs.GetList(L"both.list"); + ASSERT_TRUE(result_value); + Value* member_value; + ASSERT_TRUE(result_value->Get(0, &member_value)); + ASSERT_TRUE(member_value); + std::wstring result_string; + ASSERT_TRUE(member_value->GetAsString(&result_string)); + ASSERT_EQ(transient_string, result_string); + } +} + +TEST_F(PrefServiceTest, Observers) { + PrefService prefs; + + std::wstring input_file = data_dir_; + file_util::AppendToPath(&input_file, L"read.json"); + EXPECT_TRUE(file_util::PathExists(input_file)); + EXPECT_TRUE(prefs.LoadPersistentPrefs(input_file)); + + const wchar_t pref_name[] = L"homepage"; + prefs.RegisterStringPref(pref_name, L""); + EXPECT_EQ(std::wstring(L"http://www.cnn.com"), prefs.GetString(pref_name)); + + const std::wstring new_pref_value(L"http://www.google.com/"); + TestPrefObserver obs(&prefs, pref_name, new_pref_value); + prefs.AddPrefObserver(pref_name, &obs); + // This should fire the checks in TestPrefObserver::Observe. + prefs.SetString(pref_name, new_pref_value); + + // Make sure the tests were actually run. + EXPECT_TRUE(obs.observer_fired()); + + // Now try adding a second pref observer. + const std::wstring new_pref_value2(L"http://www.youtube.com/"); + obs.Reset(new_pref_value2); + TestPrefObserver obs2(&prefs, pref_name, new_pref_value2); + prefs.AddPrefObserver(pref_name, &obs2); + // This should fire the checks in obs and obs2. + prefs.SetString(pref_name, new_pref_value2); + EXPECT_TRUE(obs.observer_fired()); + EXPECT_TRUE(obs2.observer_fired()); + + // Make sure obs2 still works after removing obs. + prefs.RemovePrefObserver(pref_name, &obs); + obs.Reset(L""); + obs2.Reset(new_pref_value); + // This should only fire the observer in obs2. + prefs.SetString(pref_name, new_pref_value); + EXPECT_FALSE(obs.observer_fired()); + EXPECT_TRUE(obs2.observer_fired()); + + // Ok, clean up. + prefs.RemovePrefObserver(pref_name, &obs2); +} + +TEST_F(PrefServiceTest, LocalizedPrefs) { + PrefService prefs; + const wchar_t kBoolean[] = L"boolean"; + const wchar_t kInteger[] = L"integer"; + const wchar_t kString[] = L"string"; + prefs.RegisterLocalizedBooleanPref(kBoolean, IDS_LOCALE_BOOL); + prefs.RegisterLocalizedIntegerPref(kInteger, IDS_LOCALE_INT); + prefs.RegisterLocalizedStringPref(kString, IDS_LOCALE_STRING); + + // The locale default should take preference over the user default. + EXPECT_FALSE(prefs.GetBoolean(kBoolean)); + EXPECT_EQ(1, prefs.GetInteger(kInteger)); + EXPECT_EQ(L"hello", prefs.GetString(kString)); + + prefs.SetBoolean(kBoolean, true); + EXPECT_TRUE(prefs.GetBoolean(kBoolean)); + prefs.SetInteger(kInteger, 5); + EXPECT_EQ(5, prefs.GetInteger(kInteger)); + prefs.SetString(kString, L"foo"); + EXPECT_EQ(L"foo", prefs.GetString(kString)); +} + +TEST_F(PrefServiceTest, NoObserverFire) { + PrefService prefs; + + const wchar_t pref_name[] = L"homepage"; + prefs.RegisterStringPref(pref_name, L""); + + const std::wstring new_pref_value(L"http://www.google.com/"); + TestPrefObserver obs(&prefs, pref_name, new_pref_value); + prefs.AddPrefObserver(pref_name, &obs); + // This should fire the checks in TestPrefObserver::Observe. + prefs.SetString(pref_name, new_pref_value); + + // Make sure the observer was actually fired. + EXPECT_TRUE(obs.observer_fired()); + + // Setting the pref to the same value should not set the pref value a second + // time. + obs.Reset(new_pref_value); + prefs.SetString(pref_name, new_pref_value); + EXPECT_FALSE(obs.observer_fired()); + + // Clearing the pref should cause the pref to fire. + obs.Reset(L""); + prefs.ClearPref(pref_name); + EXPECT_TRUE(obs.observer_fired()); + + // Clearing the pref again should not cause the pref to fire. + obs.Reset(L""); + prefs.ClearPref(pref_name); + EXPECT_FALSE(obs.observer_fired()); + + // Ok, clean up. + prefs.RemovePrefObserver(pref_name, &obs); +} + +TEST_F(PrefServiceTest, HasPrefPath) { + PrefService prefs; + + const wchar_t path[] = L"fake.path"; + + // Shouldn't initially have a path. + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Register the path. This doesn't set a value, so the path still shouldn't + // exist. + prefs.RegisterStringPref(path, std::wstring()); + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Set a value and make sure we have a path. + prefs.persistent_->SetString(path, L"blah"); + EXPECT_TRUE(prefs.HasPrefPath(path)); +} diff --git a/chrome/common/process_watcher.cc b/chrome/common/process_watcher.cc new file mode 100644 index 0000000..5163fbd --- /dev/null +++ b/chrome/common/process_watcher.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 "chrome/common/process_watcher.h" + +#include "base/message_loop.h" +#include "chrome/app/result_codes.h" +#include "chrome/common/env_util.h" +#include "chrome/common/env_vars.h" + +// Maximum amount of time (in milliseconds) to wait for the process to exit. +static const int kWaitInterval = 2000; + +namespace { + +class TimerExpiredTask : public Task, public MessageLoop::Watcher { + public: + explicit TimerExpiredTask(ProcessHandle process) : process_(process) { + MessageLoop::current()->WatchObject(process_, this); + } + + virtual ~TimerExpiredTask() { + if (process_) { + KillProcess(); + DCHECK(!process_) << "Make sure to close the handle."; + } + } + + // Task --------------------------------------------------------------------- + + virtual void Run() { + if (process_) + KillProcess(); + } + + // MessageLoop::Watcher ----------------------------------------------------- + + virtual void OnObjectSignaled(HANDLE object) { + if (MessageLoop::current()) { + // When we're called from our destructor, the message loop is in the + // process of being torn down. Only touch the message loop if it is + // still running. + MessageLoop::current()->WatchObject(process_, NULL); // Stop watching. + } + + CloseHandle(process_); + process_ = NULL; + } + + private: + void KillProcess() { + if (env_util::HasEnvironmentVariable(env_vars::kHeadless)) { + // If running the distributed tests, give the renderer a little time to figure out + // that the channel is shutdown and unwind. + if (WaitForSingleObject(process_, kWaitInterval) == WAIT_OBJECT_0) { + OnObjectSignaled(process_); + return; + } + } + + // OK, time to get frisky. We don't actually care when the process + // terminates. We just care that it eventually terminates, and that's what + // TerminateProcess should do for us. Don't check for the result code since + // it fails quite often. This should be investigated eventually. + TerminateProcess(process_, ResultCodes::HUNG); + + // Now, just cleanup as if the process exited normally. + OnObjectSignaled(process_); + } + + // The process that we are watching. + ProcessHandle process_; + + DISALLOW_EVIL_CONSTRUCTORS(TimerExpiredTask); +}; + +} // namespace + +// static +void ProcessWatcher::EnsureProcessTerminated(ProcessHandle process) { + DCHECK(process != GetCurrentProcess()); + + // If already signaled, then we are done! + if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) { + CloseHandle(process); + return; + } + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + new TimerExpiredTask(process), + kWaitInterval); +} diff --git a/chrome/common/process_watcher.h b/chrome/common/process_watcher.h new file mode 100644 index 0000000..18bb3e2 --- /dev/null +++ b/chrome/common/process_watcher.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 CHROME_COMMON_PROCESS_WATCHER_H__ +#define CHROME_COMMON_PROCESS_WATCHER_H__ + +#include "base/process_util.h" + +class ProcessWatcher { + public: + // This method ensures that the specified process eventually terminates, and + // then it closes the given process handle. + // + // It assumes that the process has already been signalled to exit, and it + // begins by waiting a small amount of time for it to exit. If the process + // does not appear to have exited, then this function starts to become + // aggressive about ensuring that the process terminates. + // + // This method does not block the calling thread. + // + // NOTE: The process handle must have been opened with the PROCESS_TERMINATE + // and SYNCHRONIZE permissions. + // + static void EnsureProcessTerminated(ProcessHandle process_handle); + + private: + // Do not instantiate this class. + ProcessWatcher(); + + DISALLOW_EVIL_CONSTRUCTORS(ProcessWatcher); +}; + +#endif // CHROME_COMMON_PROCESS_WATCHER_H__ diff --git a/chrome/common/rand_util.cc b/chrome/common/rand_util.cc new file mode 100644 index 0000000..2c1b190 --- /dev/null +++ b/chrome/common/rand_util.cc @@ -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. + +#include "chrome/common/rand_util.h" + +#include <stdlib.h> +#include <time.h> + +#include "base/logging.h" +#include "base/thread_local_storage.h" +#include "base/win_util.h" + +namespace rand_util { + +// Using TLS since srand() needs to be called once in each thread. +int g_tls_index = ThreadLocalStorage::Alloc(); + +int RandInt(int min, int max) { + if (ThreadLocalStorage::Get(g_tls_index) == 0) { + ThreadLocalStorage::Set(g_tls_index, reinterpret_cast<void*>(1)); + srand(static_cast<unsigned int>(time(0))); + } + + // From the rand man page, use this instead of just rand() % max, so that the + // higher bits are used. + return min + static_cast<int>(static_cast<double>(max - min + 1) * + (::rand() / (RAND_MAX + 1.0))); +} + +int RandIntSecure(int min, int max) { + if (win_util::GetWinVersion() < win_util::WINVERSION_XP) { + // rand_s needs XP and later. + return RandInt(min, max); + } + + unsigned int number; + errno_t rv = rand_s(&number); + DCHECK(rv == 0) << "rand_s failed with error " << rv; + + // From the rand man page, use this instead of just rand() % max, so that the + // higher bits are used. + return min + static_cast<int>(static_cast<double>(max - min + 1.0) * + (number / (UINT_MAX + 1.0))); +} + +} // namespace rand_util diff --git a/chrome/common/rand_util.h b/chrome/common/rand_util.h new file mode 100644 index 0000000..7c27858 --- /dev/null +++ b/chrome/common/rand_util.h @@ -0,0 +1,48 @@ +// 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 CHROME_COMMON_RAND_UTIL_H__ +#define CHROME_COMMON_RAND_UTIL_H__ + +#include "base/basictypes.h" + +namespace rand_util { + +// Returns a random number between min and max (inclusive). This is a +// non-cryptographic random number generator, using rand() from the CRT. +int RandInt(int min, int max); + +// Returns a random number between min and max (inclusive). This is a (slower) +// cryptographic random number generator using rand_s() from the CRT. Note +// that it does not work in Win2K, so it degrades to RandInt. +int RandIntSecure(int min, int max); + +} // namespace rand_util + +#endif // CHROME_COMMON_RAND_UTIL_H__ diff --git a/chrome/common/ref_counted_util.h b/chrome/common/ref_counted_util.h new file mode 100644 index 0000000..093a795 --- /dev/null +++ b/chrome/common/ref_counted_util.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 CHROME_COMMON_REF_COUNTED_UTIL_H__ +#define CHROME_COMMON_REF_COUNTED_UTIL_H__ + +#include "base/ref_counted.h" +#include <vector> + +// RefCountedVector is just a vector wrapped up with +// RefCountedThreadSafe. +template<class T> +class RefCountedVector : + public base::RefCountedThreadSafe<RefCountedVector<T> > { + public: + RefCountedVector() {} + RefCountedVector(const std::vector<T>& initializer) + : data(initializer) {} + + std::vector<T> data; + + DISALLOW_EVIL_CONSTRUCTORS(RefCountedVector<T>); +}; + +// RefCountedThreadSafeBytes represent a ref-counted blob of bytes. +// Useful for passing data between threads without copying. +typedef RefCountedVector<unsigned char> RefCountedBytes; + +#endif // CHROME_COMMON_REF_COUNTED_UTIL_H__
\ No newline at end of file diff --git a/chrome/common/render_messages.cc b/chrome/common/render_messages.cc new file mode 100644 index 0000000..9ac7584 --- /dev/null +++ b/chrome/common/render_messages.cc @@ -0,0 +1,47 @@ +// 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 "chrome/common/ipc_message.h" + +#ifdef IPC_MESSAGE_LOG_ENABLED + +// Preprocessor magic: render_messages.h defines the enums and debug string +// functions if this define is set. +#define IPC_MESSAGE_MACROS_LOG_ENABLED + +#endif // IPC_MESSAGE_LOG_ENABLED + +#include "chrome/common/render_messages.h" + +void RenderMessagesInit() { +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::RegisterMessageLogger(ViewStart, ViewMsgLog); + IPC::RegisterMessageLogger(ViewHostStart, ViewHostMsgLog); +#endif +}
\ No newline at end of file diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h new file mode 100644 index 0000000..32f7d7e --- /dev/null +++ b/chrome/common/render_messages.h @@ -0,0 +1,1548 @@ +// 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 CHROME_COMMON_RENDER_MESSAGES_H__ +#define CHROME_COMMON_RENDER_MESSAGES_H__ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "base/shared_memory.h" +#include "chrome/common/filter_policy.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" +#include "chrome/common/page_transition_types.h" +#include "googleurl/src/gurl.h" +#include "net/base/upload_data.h" +#include "net/url_request/url_request_status.h" +#include "webkit/glue/cache_manager.h" +#include "webkit/glue/context_node_types.h" +#include "webkit/glue/form_data.h" +#include "webkit/glue/password_form.h" +#include "webkit/glue/password_form_dom_manager.h" +#include "webkit/glue/resource_loader_bridge.h" +#include "webkit/glue/webdropdata.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webpreferences.h" +#include "webkit/glue/webview_delegate.h" + +// Parameters structure for ViewMsg_Navigate, which has too many data +// parameters to be reasonably put in a predefined IPC message. +struct ViewMsg_Navigate_Params { + // The page_id for this navigation, or -1 if it is a new navigation. Back, + // Forward, and Reload navigations should have a valid page_id. If the load + // succeeds, then this page_id will be reflected in the resultant + // ViewHostMsg_FrameNavigate message. + int32 page_id; + + // The URL to load. + GURL url; + + // The type of transition. + PageTransition::Type transition; + + // Opaque history state (received by ViewHostMsg_UpdateState). + std::string state; + + // Specifies if the URL should be loaded using 'reload' semantics (i.e., + // bypassing any locally cached content). + bool reload; +}; + +// Parameters structure for ViewHostMsg_FrameNavigate, which has too many data +// parameters to be reasonably put in a predefined IPC message. +struct ViewHostMsg_FrameNavigate_Params { + // Page ID of this navigation. The renderer creates a new unique page ID + // anytime a new session history entry is created. This means you'll get new + // page IDs for user actions, and the old page IDs will be reloaded when + // iframes are loaded automatically. + int32 page_id; + + // URL of the page being loaded. NON-CANONICAL. + GURL url; + + // URL of the referrer of this load. WebKit generates this based on the + // source of the event that caused the load. NON-CANONICAL. + GURL referrer; + + // The type of transition. + PageTransition::Type transition; + + // Lists the redirects that occurred on the way to the current page. This + // vector has the same format as reported by the WebDataSource in the glue, + // with the current page being the last one in the list (so even when + // there's no redirect, there will be one entry in the list. + std::vector<GURL> redirects; + + // Set to false if we want to update the session history but not update + // the browser history. E.g., on unreachable urls. + bool should_update_history; + + // See SearchableFormData for a description of these. + GURL searchable_form_url; + std::wstring searchable_form_element_name; + std::string searchable_form_encoding; + + // See password_form.h. + PasswordForm password_form; + + // Information regarding the security of the connection (empty if the + // connection was not secure). + std::string security_info; + + // The gesture that initiated this navigation. + NavigationGesture gesture; + + // Contents MIME type of main frame. + std::string contents_mime_type; + + // True if this was a post request. + bool is_post; +}; + +// Parameters structure for ViewHostMsg_ContextMenu, which has too many data +// parameters to be reasonably put in a predefined IPC message. +// FIXME(beng): This would be more useful in the future and more efficient +// if the parameters here weren't so literally mapped to what +// they contain for the ContextMenu task. It might be better +// to make the string fields more generic so that this object +// could be used for more contextual actions. +struct ViewHostMsg_ContextMenu_Params { + // This is the type of Context Node that the context menu was invoked on. + ContextNode::Type type; + + // These values represent the coordinates of the mouse when the context menu + // was invoked. Coords are relative to the associated RenderView's origin. + int x; + int y; + + // This is the URL of the link that encloses the node the context menu was + // invoked on. + GURL link_url; + + // This is the URL of the image the context menu was invoked on. + GURL image_url; + + // This is the URL of the top level page that the context menu was invoked + // on. + GURL page_url; + + // This is the URL of the subframe that the context menu was invoked on. + GURL frame_url; + + // This is the text of the selection that the context menu was invoked on. + std::wstring selection_text; + + // The misspelled word under the cursor, if any. Used to generate the + // |dictionary_suggestions| list. + std::wstring misspelled_word; + + // Suggested replacements for a misspelled word under the cursor. + // This vector gets populated in the render process host + // by intercepting ViewHostMsg_ContextMenu in ResourceMessageFilter + // and populating dictionary_suggestions if the type is EDITABLE + // and the misspelled_word is not empty. + std::vector<std::wstring> dictionary_suggestions; + + // These flags indicate to the browser whether the renderer believes it is + // able to perform the corresponding action. + int edit_flags; +}; + +// Values that may be OR'd together to form the 'flags' parameter of a +// ViewHostMsg_PaintRect message. +struct ViewHostMsg_PaintRect_Flags { + enum { + IS_RESIZE_ACK = 1 << 0, + IS_RESTORE_ACK = 1 << 1, + }; + static bool is_resize_ack(int flags) { + return (flags & IS_RESIZE_ACK) != 0; + } + static bool is_restore_ack(int flags) { + return (flags & IS_RESTORE_ACK) != 0; + } +}; + +struct ViewHostMsg_PaintRect_Params { + // The bitmap to be painted into the rect given by bitmap_rect. Valid only + // in the context of the renderer process. + SharedMemoryHandle bitmap; + + // The position and size of the bitmap. + gfx::Rect bitmap_rect; + + // The size of the RenderView when this message was generated. This is + // included so the host knows how large the view is from the perspective of + // the renderer process. This is necessary in case a resize operation is in + // progress. + gfx::Size view_size; + + // New window locations for plugin child windows. + std::vector<WebPluginGeometry> plugin_window_moves; + + // The following describes the various bits that may be set in flags: + // + // ViewHostMsg_PaintRect_Flags::IS_RESIZE_ACK + // Indicates that this is a response to a ViewMsg_Resize message. + // + // ViewHostMsg_PaintRect_Flags::IS_RESTORE_ACK + // Indicates that this is a response to a ViewMsg_WasRestored message. + // + // If flags is zero, then this message corresponds to an unsoliticed paint + // request by the render view. Both of the above bits may be set in flags, + // which would indicate that this paint message is an ACK for multiple + // request messages. + int flags; +}; + +// Parameters structure for ViewHostMsg_ScrollRect, which has too many data +// parameters to be reasonably put in a predefined IPC message. +struct ViewHostMsg_ScrollRect_Params { + // The bitmap to be painted into the rect exposed by scrolling. This handle + // is valid only in the context of the renderer process. + SharedMemoryHandle bitmap; + + // The position and size of the bitmap. + gfx::Rect bitmap_rect; + + // The scroll offset. Only one of these can be non-zero. + int dx; + int dy; + + // The rectangular region to scroll. + gfx::Rect clip_rect; + + // The size of the RenderView when this message was generated. + gfx::Size view_size; + + // New window locations for plugin child windows. + std::vector<WebPluginGeometry> plugin_window_moves; +}; + +// Parameters structure for ViewMsg_UploadFile. +struct ViewMsg_UploadFile_Params { + // See WebContents::StartFileUpload for a description of these fields. + std::wstring file_path; + std::wstring form; + std::wstring file; + std::wstring submit; + std::wstring other_values; +}; + +// Parameters for a resource request. +struct ViewHostMsg_Resource_Request { + // The request method: GET, POST, etc. + std::string method; + + // The requested URL. + GURL url; + + // The URL of the document in the top-level window, which may be checked by + // the third-party cookie blocking policy. Leaving it empty may lead to + // undesired cookie blocking. Third-party cookie blocking can be bypassed by + // setting policy_url = url, but this should ideally only be done if there + // really is no way to determine the correct value. + GURL policy_url; + + // The referrer to use (may be empty). + GURL referrer; + + // Additional HTTP request headers. + std::string headers; + + // URLRequest load flags (0 by default). + int load_flags; + + // Process ID of process that originated this request. + int origin_pid; + + // What this resource load is for (main frame, sub-frame, sub-resource, + // object). + ResourceType::Type resource_type; + + // True if this request is for a resource loaded over HTTP when the main page + // was loaded over HTTPS. + bool mixed_content; + + // Used by plugin->browser requests to get the correct URLRequestContext. + uint32 request_context; + + // Optional upload data (may be empty). + std::vector<net::UploadData::Element> upload_content; +}; + +// Parameters for a resource response header. +struct ViewMsg_Resource_ResponseHead : + webkit_glue::ResourceLoaderBridge::ResponseInfo { + // The response status. + URLRequestStatus status; + + // Specifies if the resource should be filtered before being displayed + // (insecure resources can be filtered to keep the page secure). + FilterPolicy::Type filter_policy; +}; + +// Parameters for a synchronous resource response. +struct ViewHostMsg_SyncLoad_Result : ViewMsg_Resource_ResponseHead { + // The final URL after any redirects. + GURL final_url; + + // The response data. + std::string data; +}; + +// Parameters for a render request. +struct ViewMsg_Print_Params { + // In pixels according to dpi_x and dpi_y. + gfx::Size printable_size; + + // Specifies dots per inch. + double dpi; + + // Minimum shrink factor. See PrintSettings::min_shrink for more information. + double min_shrink; + + // Maximum shrink factor. See PrintSettings::max_shrink for more information. + double max_shrink; + + // Desired apparent dpi on paper. + int desired_dpi; + + // Cookie for the document to ensure correctness. + int document_cookie; + + // Warning: do not compare document_cookie. + bool Equals(const ViewMsg_Print_Params& rhs) const { + return printable_size == rhs.printable_size && + dpi == rhs.dpi && + min_shrink == rhs.min_shrink && + max_shrink == rhs.max_shrink && + desired_dpi == rhs.desired_dpi; + } +}; + +struct ViewMsg_PrintPage_Params { + // Parameters to render the page as a printed page. It must always be the same + // value for all the document. + ViewMsg_Print_Params params; + + // The page number is the indicator of the square that should be rendered + // according to the layout specified in ViewMsg_Print_Params. + int page_number; +}; + +struct ViewMsg_PrintPages_Params { + // Parameters to render the page as a printed page. It must always be the same + // value for all the document. + ViewMsg_Print_Params params; + + // If empty, this means a request to render all the printed pages. + std::vector<int> pages; +}; + +// Parameters to describe a rendered page. +struct ViewHostMsg_DidPrintPage_Params { + // A shared memory handle to the EMF data. This data can be quite large so a + // memory map needs to be used. + SharedMemoryHandle emf_data_handle; + + // Size of the EMF data. + unsigned data_size; + + // Cookie for the document to ensure correctness. + int document_cookie; + + // Page number. + int page_number; + + // Shrink factor used to render this page. + double actual_shrink; +}; + +// The first parameter for the ViewHostMsg_ImeUpdateStatus message. +enum ViewHostMsg_ImeControl { + IME_DISABLE = 0, + IME_MOVE_WINDOWS, + IME_COMPLETE_COMPOSITION, +}; + +// Multi-pass include of render_messages_internal. Preprocessor magic allows +// us to use 1 header to define the enums and classes for our render messages. +#define IPC_MESSAGE_MACROS_ENUMS +#include "chrome/common/render_messages_internal.h" + +#ifdef IPC_MESSAGE_MACROS_LOG_ENABLED +// When we are supposed to create debug strings, we run it through twice, once +// with debug strings on, and once with only CLASSES on to generate both types +// of messages. +# undef IPC_MESSAGE_MACROS_LOG +# define IPC_MESSAGE_MACROS_CLASSES +# include "chrome/common/render_messages_internal.h" + +# undef IPC_MESSAGE_MACROS_CLASSES +# define IPC_MESSAGE_MACROS_LOG +# include "chrome/common/render_messages_internal.h" +#else +// No debug strings requested, just define the classes +# define IPC_MESSAGE_MACROS_CLASSES +# include "chrome/common/render_messages_internal.h" +#endif + + +namespace IPC { + +template <> +struct ParamTraits<ResourceType::Type> { + typedef ResourceType::Type param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type) || !ResourceType::ValidType(type)) + return false; + *p = ResourceType::FromInt(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring type; + switch(p) { + case ResourceType::MAIN_FRAME: + type = L"MAIN_FRAME"; + break; + case ResourceType::SUB_FRAME: + type = L"SUB_FRAME"; + break; + case ResourceType::SUB_RESOURCE: + type = L"SUB_RESOURCE"; + break; + case ResourceType::OBJECT: + type = L"OBJECT"; + break; + default: + type = L"UNKNOWN"; + break; + } + + LogParam(type, l); + } +}; + +template <> +struct ParamTraits<FilterPolicy::Type> { + typedef FilterPolicy::Type param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type) || !FilterPolicy::ValidType(type)) + return false; + *p = FilterPolicy::FromInt(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring type; + switch(p) { + case FilterPolicy::DONT_FILTER: + type = L"DONT_FILTER"; + break; + case FilterPolicy::FILTER_ALL: + type = L"FILTER_ALL"; + break; + case FilterPolicy::FILTER_ALL_EXCEPT_IMAGES: + type = L"FILTER_ALL_EXCEPT_IMAGES"; + break; + default: + type = L"UNKNOWN"; + break; + } + + LogParam(type, l); + } +}; + +template <> +struct ParamTraits<ContextNode::Type> { + typedef ContextNode::Type param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = ContextNode::FromInt(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring type; + switch(p) { + case WebInputEvent::MOUSE_DOWN: + type = L"MOUSE_DOWN"; + break; + case WebInputEvent::MOUSE_UP: + type = L"MOUSE_UP"; + break; + case WebInputEvent::MOUSE_MOVE: + type = L"MOUSE_MOVE"; + break; + case WebInputEvent::MOUSE_LEAVE: + type = L"MOUSE_LEAVE"; + break; + case WebInputEvent::MOUSE_DOUBLE_CLICK: + type = L"MOUSE_DOUBLE_CLICK"; + break; + case WebInputEvent::MOUSE_WHEEL: + type = L"MOUSE_WHEEL"; + break; + case WebInputEvent::KEY_DOWN: + type = L"KEY_DOWN"; + break; + case WebInputEvent::KEY_UP: + type = L"KEY_UP"; + break; + default: + type = L"UNKNOWN"; + break; + } + + LogParam(type, l); + } +}; + +template <> +struct ParamTraits<WebInputEvent::Type> { + typedef WebInputEvent::Type param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast<WebInputEvent::Type>(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring event; + switch(p) { + case ContextNode::NONE: + event = L"NONE"; + break; + case ContextNode::PAGE: + event = L"PAGE"; + break; + case ContextNode::FRAME: + event = L"FRAME"; + break; + case ContextNode::LINK: + event = L"LINK"; + break; + case ContextNode::IMAGE: + event = L"IMAGE"; + break; + case ContextNode::IMAGE_LINK: + event = L"IMAGE_LINK"; + break; + case ContextNode::SELECTION: + event = L"SELECTION"; + break; + case ContextNode::EDITABLE: + event = L"EDITABLE"; + break; + case ContextNode::MISPELLED_WORD: + event = L"MISPELLED_WORD"; + break; + default: + event = L"UNKNOWN"; + break; + } + + LogParam(event, l); + } +}; + +template <> +struct ParamTraits<ViewHostMsg_ImeControl> { + typedef ViewHostMsg_ImeControl param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast<ViewHostMsg_ImeControl>(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring control; + switch(p) { + case IME_DISABLE: + control = L"IME_DISABLE"; + break; + case IME_MOVE_WINDOWS: + control = L"IME_MOVE_WINDOWS"; + break; + case IME_COMPLETE_COMPOSITION: + control = L"IME_COMPLETE_COMPOSITION"; + break; + default: + control = L"UNKNOWN"; + break; + } + + LogParam(control, l); + } +}; + +// Traits for ViewMsg_Navigate_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewMsg_Navigate_Params> { + typedef ViewMsg_Navigate_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.page_id); + WriteParam(m, p.url); + WriteParam(m, p.transition); + WriteParam(m, p.state); + WriteParam(m, p.reload); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->page_id) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->transition) && + ReadParam(m, iter, &p->state) && + ReadParam(m, iter, &p->reload); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.page_id, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.transition, l); + l->append(L", "); + LogParam(p.state, l); + l->append(L", "); + LogParam(p.reload, l); + l->append(L")"); + } +}; + +// Traits for PasswordForm_Params structure to pack/unpack. +template <> +struct ParamTraits<PasswordForm> { + typedef PasswordForm param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.signon_realm); + WriteParam(m, p.origin); + WriteParam(m, p.action); + WriteParam(m, p.submit_element); + WriteParam(m, p.username_element); + WriteParam(m, p.username_value); + WriteParam(m, p.password_element); + WriteParam(m, p.password_value); + WriteParam(m, p.old_password_element); + WriteParam(m, p.old_password_value); + WriteParam(m, p.ssl_valid); + WriteParam(m, p.preferred); + WriteParam(m, p.blacklisted_by_user); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->signon_realm) && + ReadParam(m, iter, &p->origin) && + ReadParam(m, iter, &p->action) && + ReadParam(m, iter, &p->submit_element) && + ReadParam(m, iter, &p->username_element) && + ReadParam(m, iter, &p->username_value) && + ReadParam(m, iter, &p->password_element) && + ReadParam(m, iter, &p->password_value) && + ReadParam(m, iter, &p->old_password_element) && + ReadParam(m, iter, &p->old_password_value) && + ReadParam(m, iter, &p->ssl_valid) && + ReadParam(m, iter, &p->preferred) && + ReadParam(m, iter, &p->blacklisted_by_user); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<PasswordForm>"); + } +}; + +// Traits for ViewHostMsg_FrameNavigate_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewHostMsg_FrameNavigate_Params> { + typedef ViewHostMsg_FrameNavigate_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.page_id); + WriteParam(m, p.url); + WriteParam(m, p.referrer); + WriteParam(m, p.transition); + WriteParam(m, p.redirects); + WriteParam(m, p.should_update_history); + WriteParam(m, p.searchable_form_url); + WriteParam(m, p.searchable_form_element_name); + WriteParam(m, p.searchable_form_encoding); + WriteParam(m, p.password_form); + WriteParam(m, p.security_info); + WriteParam(m, p.gesture); + WriteParam(m, p.contents_mime_type); + WriteParam(m, p.is_post); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->page_id) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->referrer) && + ReadParam(m, iter, &p->transition) && + ReadParam(m, iter, &p->redirects) && + ReadParam(m, iter, &p->should_update_history) && + ReadParam(m, iter, &p->searchable_form_url) && + ReadParam(m, iter, &p->searchable_form_element_name) && + ReadParam(m, iter, &p->searchable_form_encoding) && + ReadParam(m, iter, &p->password_form) && + ReadParam(m, iter, &p->security_info) && + ReadParam(m, iter, &p->gesture) && + ReadParam(m, iter, &p->contents_mime_type) && + ReadParam(m, iter, &p->is_post); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.page_id, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.referrer, l); + l->append(L", "); + LogParam(p.transition, l); + l->append(L", "); + LogParam(p.redirects, l); + l->append(L", "); + LogParam(p.should_update_history, l); + l->append(L", "); + LogParam(p.searchable_form_url, l); + l->append(L", "); + LogParam(p.searchable_form_element_name, l); + l->append(L", "); + LogParam(p.searchable_form_encoding, l); + l->append(L", "); + LogParam(p.password_form, l); + l->append(L", "); + LogParam(p.security_info, l); + l->append(L", "); + LogParam(p.gesture, l); + l->append(L", "); + LogParam(p.contents_mime_type, l); + l->append(L", "); + LogParam(p.is_post, l); + l->append(L")"); + } +}; + +// Traits for ViewHostMsg_ContextMenu_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewHostMsg_ContextMenu_Params> { + typedef ViewHostMsg_ContextMenu_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.type); + WriteParam(m, p.x); + WriteParam(m, p.y); + WriteParam(m, p.link_url); + WriteParam(m, p.image_url); + WriteParam(m, p.page_url); + WriteParam(m, p.frame_url); + WriteParam(m, p.selection_text); + WriteParam(m, p.misspelled_word); + WriteParam(m, p.dictionary_suggestions); + WriteParam(m, p.edit_flags); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->type) && + ReadParam(m, iter, &p->x) && + ReadParam(m, iter, &p->y) && + ReadParam(m, iter, &p->link_url) && + ReadParam(m, iter, &p->image_url) && + ReadParam(m, iter, &p->page_url) && + ReadParam(m, iter, &p->frame_url) && + ReadParam(m, iter, &p->selection_text) && + ReadParam(m, iter, &p->misspelled_word) && + ReadParam(m, iter, &p->dictionary_suggestions) && + ReadParam(m, iter, &p->edit_flags); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<ViewHostMsg_ContextMenu_Params>"); + } +}; + +// Traits for ViewHostMsg_PaintRect_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewHostMsg_PaintRect_Params> { + typedef ViewHostMsg_PaintRect_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.bitmap); + WriteParam(m, p.bitmap_rect); + WriteParam(m, p.view_size); + WriteParam(m, p.plugin_window_moves); + WriteParam(m, p.flags); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->bitmap) && + ReadParam(m, iter, &p->bitmap_rect) && + ReadParam(m, iter, &p->view_size) && + ReadParam(m, iter, &p->plugin_window_moves) && + ReadParam(m, iter, &p->flags); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.bitmap, l); + l->append(L", "); + LogParam(p.bitmap_rect, l); + l->append(L", "); + LogParam(p.view_size, l); + l->append(L", "); + LogParam(p.plugin_window_moves, l); + l->append(L", "); + LogParam(p.flags, l); + l->append(L")"); + } +}; + +// Traits for ViewHostMsg_ScrollRect_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewHostMsg_ScrollRect_Params> { + typedef ViewHostMsg_ScrollRect_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.bitmap); + WriteParam(m, p.bitmap_rect); + WriteParam(m, p.dx); + WriteParam(m, p.dy); + WriteParam(m, p.clip_rect); + WriteParam(m, p.view_size); + WriteParam(m, p.plugin_window_moves); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->bitmap) && + ReadParam(m, iter, &p->bitmap_rect) && + ReadParam(m, iter, &p->dx) && + ReadParam(m, iter, &p->dy) && + ReadParam(m, iter, &p->clip_rect) && + ReadParam(m, iter, &p->view_size) && + ReadParam(m, iter, &p->plugin_window_moves); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.bitmap, l); + l->append(L", "); + LogParam(p.bitmap_rect, l); + l->append(L", "); + LogParam(p.dx, l); + l->append(L", "); + LogParam(p.dy, l); + l->append(L", "); + LogParam(p.clip_rect, l); + l->append(L", "); + LogParam(p.view_size, l); + l->append(L", "); + LogParam(p.plugin_window_moves, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits<WebPluginGeometry> { + typedef WebPluginGeometry param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.window); + WriteParam(m, p.window_rect); + WriteParam(m, p.clip_rect); + WriteParam(m, p.visible); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->window) && + ReadParam(m, iter, &p->window_rect) && + ReadParam(m, iter, &p->clip_rect) && + ReadParam(m, iter, &p->visible); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.window, l); + l->append(L", "); + LogParam(p.window_rect, l); + l->append(L", "); + LogParam(p.clip_rect, l); + l->append(L", "); + LogParam(p.visible, l); + l->append(L")"); + } +}; + +// Traits for ViewMsg_GetPlugins_Reply structure to pack/unpack. +template <> +struct ParamTraits<WebPluginMimeType> { + typedef WebPluginMimeType param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.mime_type); + WriteParam(m, p.file_extensions); + WriteParam(m, p.description); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->mime_type) && + ReadParam(m, iter, &r->file_extensions) && + ReadParam(m, iter, &r->description); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.mime_type, l); + l->append(L", "); + LogParam(p.file_extensions, l); + l->append(L", "); + LogParam(p.description, l); + l->append(L")"); + } +}; + + +template <> +struct ParamTraits<WebPluginInfo> { + typedef WebPluginInfo param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.name); + WriteParam(m, p.file); + WriteParam(m, p.version); + WriteParam(m, p.desc); + WriteParam(m, p.mime_types); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->name) && + ReadParam(m, iter, &r->file) && + ReadParam(m, iter, &r->version) && + ReadParam(m, iter, &r->desc) && + ReadParam(m, iter, &r->mime_types); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.name, l); + l->append(L", "); + LogParam(p.file, l); + l->append(L", "); + LogParam(p.version, l); + l->append(L", "); + LogParam(p.desc, l); + l->append(L", "); + LogParam(p.mime_types, l); + l->append(L")"); + } +}; + +// Traits for ViewMsg_UploadFile_Params structure to pack/unpack. +template <> +struct ParamTraits<ViewMsg_UploadFile_Params> { + typedef ViewMsg_UploadFile_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.file_path); + WriteParam(m, p.form); + WriteParam(m, p.file); + WriteParam(m, p.submit); + WriteParam(m, p.other_values); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->file_path) && + ReadParam(m, iter, &p->form) && + ReadParam(m, iter, &p->file) && + ReadParam(m, iter, &p->submit) && + ReadParam(m, iter, &p->other_values); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<ViewMsg_UploadFile_Params>"); + } +}; + +// Traits for ViewMsg_FindInPageMsg_Request structure to pack/unpack. +template <> +struct ParamTraits<FindInPageRequest> { + typedef FindInPageRequest param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.request_id); + WriteParam(m, p.search_string); + WriteParam(m, p.forward); + WriteParam(m, p.match_case); + WriteParam(m, p.find_next); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->request_id) && + ReadParam(m, iter, &p->search_string) && + ReadParam(m, iter, &p->forward) && + ReadParam(m, iter, &p->match_case) && + ReadParam(m, iter, &p->find_next); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<FindInPageRequest>"); + } +}; + +// Traits for net::UploadData::Element. +template <> +struct ParamTraits<net::UploadData::Element> { + typedef net::UploadData::Element param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.type())); + if (p.type() == net::UploadData::TYPE_BYTES) { + m->WriteData(&p.bytes()[0], static_cast<int>(p.bytes().size())); + } else { + WriteParam(m, p.file_path()); + WriteParam(m, p.file_range_offset()); + WriteParam(m, p.file_range_length()); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type)) + return false; + if (type == net::UploadData::TYPE_BYTES) { + const char* data; + int len; + if (!m->ReadData(iter, &data, &len)) + return false; + r->SetToBytes(data, len); + } else { + DCHECK(type == net::UploadData::TYPE_FILE); + std::wstring file_path; + uint64 offset, length; + if (!ReadParam(m, iter, &file_path)) + return false; + if (!ReadParam(m, iter, &offset)) + return false; + if (!ReadParam(m, iter, &length)) + return false; + r->SetToFilePathRange(file_path, offset, length); + } + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<net::UploadData::Element>"); + } +}; + +// Traits for CacheManager::UsageStats +template <> +struct ParamTraits<CacheManager::UsageStats> { + typedef CacheManager::UsageStats param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.min_dead_capacity); + WriteParam(m, p.max_dead_capacity); + WriteParam(m, p.capacity); + WriteParam(m, p.live_size); + WriteParam(m, p.dead_size); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->min_dead_capacity) && + ReadParam(m, iter, &r->max_dead_capacity) && + ReadParam(m, iter, &r->capacity) && + ReadParam(m, iter, &r->live_size) && + ReadParam(m, iter, &r->dead_size); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<CacheManager::UsageStats>"); + } +}; + +// Traits for PasswordFormDomManager::FillData. +template <> +struct ParamTraits<PasswordFormDomManager::FillData> { + typedef PasswordFormDomManager::FillData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.basic_data); + WriteParam(m, p.additional_logins); + WriteParam(m, p.wait_for_username); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->basic_data) && + ReadParam(m, iter, &r->additional_logins) && + ReadParam(m, iter, &r->wait_for_username); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<PasswordFormDomManager::FillData>"); + } +}; + +template<> +struct ParamTraits<NavigationGesture> { + typedef NavigationGesture param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast<NavigationGesture>(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring event; + switch(p) { + case NavigationGestureUser: + event = L"GESTURE_USER"; + break; + case NavigationGestureAuto: + event = L"GESTURE_AUTO"; + break; + default: + event = L"GESTURE_UNKNOWN"; + break; + } + LogParam(event, l); + } +}; + +// Traits for ViewHostMsg_Resource_Request +template <> +struct ParamTraits<ViewHostMsg_Resource_Request> { + typedef ViewHostMsg_Resource_Request param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.method); + WriteParam(m, p.url); + WriteParam(m, p.policy_url); + WriteParam(m, p.referrer); + WriteParam(m, p.headers); + WriteParam(m, p.load_flags); + WriteParam(m, p.origin_pid); + WriteParam(m, p.resource_type); + WriteParam(m, p.mixed_content); + WriteParam(m, p.request_context); + WriteParam(m, p.upload_content); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->method) && + ReadParam(m, iter, &r->url) && + ReadParam(m, iter, &r->policy_url) && + ReadParam(m, iter, &r->referrer) && + ReadParam(m, iter, &r->headers) && + ReadParam(m, iter, &r->load_flags) && + ReadParam(m, iter, &r->origin_pid) && + ReadParam(m, iter, &r->resource_type) && + ReadParam(m, iter, &r->mixed_content) && + ReadParam(m, iter, &r->request_context) && + ReadParam(m, iter, &r->upload_content); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.method, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.referrer, l); + l->append(L", "); + LogParam(p.load_flags, l); + l->append(L", "); + LogParam(p.origin_pid, l); + l->append(L", "); + LogParam(p.resource_type, l); + l->append(L", "); + LogParam(p.mixed_content, l); + l->append(L", "); + LogParam(p.request_context, l); + l->append(L")"); + } +}; + +// Traits for URLRequestStatus +template <> +struct ParamTraits<URLRequestStatus> { + typedef URLRequestStatus param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.status())); + WriteParam(m, p.os_error()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int status, os_error; + if (!ReadParam(m, iter, &status) || + !ReadParam(m, iter, &os_error)) + return false; + r->set_status(static_cast<URLRequestStatus::Status>(status)); + r->set_os_error(os_error); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring status; + switch(p.status()) { + case URLRequestStatus::SUCCESS: + status = L"SUCCESS"; + break; + case URLRequestStatus::IO_PENDING: + status = L"IO_PENDING "; + break; + case URLRequestStatus::HANDLED_EXTERNALLY: + status = L"HANDLED_EXTERNALLY"; + break; + case URLRequestStatus::CANCELED: + status = L"CANCELED"; + break; + case URLRequestStatus::FAILED: + status = L"FAILED"; + break; + default: + status = L"UNKNOWN"; + break; + } + if (p.status() == URLRequestStatus::FAILED) + l->append(L"("); + + LogParam(status, l); + + if (p.status() == URLRequestStatus::FAILED) { + l->append(L", "); + LogParam(p.os_error(), l); + l->append(L")"); + } + } +}; + +template <> +struct ParamTraits<scoped_refptr<net::HttpResponseHeaders>> { + typedef scoped_refptr<net::HttpResponseHeaders> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.get() != NULL); + if (p) + p->Persist(m, false); + } + static bool Read(const Message* m, void** iter, param_type* r) { + bool has_object; + if (!ReadParam(m, iter, &has_object)) + return false; + if (has_object) + *r = new net::HttpResponseHeaders(*m, iter); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<HttpResponseHeaders>"); + } +}; + +// Traits for webkit_glue::ResourceLoaderBridge::ResponseInfo +template <> +struct ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo> { + typedef webkit_glue::ResourceLoaderBridge::ResponseInfo param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.request_time); + WriteParam(m, p.response_time); + WriteParam(m, p.headers); + WriteParam(m, p.mime_type); + WriteParam(m, p.charset); + WriteParam(m, p.security_info); + WriteParam(m, p.content_length); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->request_time) && + ReadParam(m, iter, &r->response_time) && + ReadParam(m, iter, &r->headers) && + ReadParam(m, iter, &r->mime_type) && + ReadParam(m, iter, &r->charset) && + ReadParam(m, iter, &r->security_info) && + ReadParam(m, iter, &r->content_length); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.request_time, l); + l->append(L", "); + LogParam(p.response_time, l); + l->append(L", "); + LogParam(p.headers, l); + l->append(L", "); + LogParam(p.mime_type, l); + l->append(L", "); + LogParam(p.charset, l); + l->append(L", "); + LogParam(p.security_info, l); + l->append(L")"); + } +}; + +// Traits for ViewMsg_Resource_ResponseHead +template <> +struct ParamTraits<ViewMsg_Resource_ResponseHead> { + typedef ViewMsg_Resource_ResponseHead param_type; + static void Write(Message* m, const param_type& p) { + ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo>::Write(m, p); + WriteParam(m, p.status); + WriteParam(m, p.filter_policy); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo>::Read(m, iter, r) && + ReadParam(m, iter, &r->status) && + ReadParam(m, iter, &r->filter_policy); + } + static void Log(const param_type& p, std::wstring* l) { + // log more? + ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo>::Log(p, l); + } +}; + +// Traits for ViewHostMsg_Resource_SyncLoad_Response +template <> +struct ParamTraits<ViewHostMsg_SyncLoad_Result> { + typedef ViewHostMsg_SyncLoad_Result param_type; + static void Write(Message* m, const param_type& p) { + ParamTraits<ViewMsg_Resource_ResponseHead>::Write(m, p); + WriteParam(m, p.final_url); + WriteParam(m, p.data); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ParamTraits<ViewMsg_Resource_ResponseHead>::Read(m, iter, r) && + ReadParam(m, iter, &r->final_url) && + ReadParam(m, iter, &r->data); + } + static void Log(const param_type& p, std::wstring* l) { + // log more? + ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo>::Log(p, l); + } +}; + +// Traits for FormData structure to pack/unpack. +template <> +struct ParamTraits<FormData> { + typedef FormData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.origin); + WriteParam(m, p.action); + WriteParam(m, p.elements); + WriteParam(m, p.values); + WriteParam(m, p.submit); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->origin) && + ReadParam(m, iter, &p->action) && + ReadParam(m, iter, &p->elements) && + ReadParam(m, iter, &p->values) && + ReadParam(m, iter, &p->submit); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<FormData>"); + } +}; + +// Traits for ViewMsg_Print_Params +template <> +struct ParamTraits<ViewMsg_Print_Params> { + typedef ViewMsg_Print_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.printable_size); + WriteParam(m, p.dpi); + WriteParam(m, p.min_shrink); + WriteParam(m, p.max_shrink); + WriteParam(m, p.desired_dpi); + WriteParam(m, p.document_cookie); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->printable_size) && + ReadParam(m, iter, &p->dpi) && + ReadParam(m, iter, &p->min_shrink) && + ReadParam(m, iter, &p->max_shrink) && + ReadParam(m, iter, &p->desired_dpi) && + ReadParam(m, iter, &p->document_cookie); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<ViewMsg_Print_Params>"); + } +}; + +// Traits for ViewMsg_PrintPage_Params +template <> +struct ParamTraits<ViewMsg_PrintPage_Params> { + typedef ViewMsg_PrintPage_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.params); + WriteParam(m, p.page_number); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->params) && + ReadParam(m, iter, &p->page_number); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<ViewMsg_PrintPage_Params>"); + } +}; + +// Traits for ViewMsg_PrintPages_Params +template <> +struct ParamTraits<ViewMsg_PrintPages_Params> { + typedef ViewMsg_PrintPages_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.params); + WriteParam(m, p.pages); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->params) && + ReadParam(m, iter, &p->pages); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<ViewMsg_PrintPages_Params>"); + } +}; + +// Traits for ViewHostMsg_DidPrintPage_Params +template <> +struct ParamTraits<ViewHostMsg_DidPrintPage_Params> { + typedef ViewHostMsg_DidPrintPage_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.emf_data_handle); + WriteParam(m, p.data_size); + WriteParam(m, p.document_cookie); + WriteParam(m, p.page_number); + WriteParam(m, p.actual_shrink); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->emf_data_handle) && + ReadParam(m, iter, &p->data_size) && + ReadParam(m, iter, &p->document_cookie) && + ReadParam(m, iter, &p->page_number) && + ReadParam(m, iter, &p->actual_shrink); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<ViewHostMsg_DidPrintPage_Params>"); + } +}; + +// Traits for WebPreferences structure to pack/unpack. +template <> +struct ParamTraits<WebPreferences> { + typedef WebPreferences param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.standard_font_family); + WriteParam(m, p.fixed_font_family); + WriteParam(m, p.serif_font_family); + WriteParam(m, p.sans_serif_font_family); + WriteParam(m, p.cursive_font_family); + WriteParam(m, p.fantasy_font_family); + WriteParam(m, p.default_font_size); + WriteParam(m, p.default_fixed_font_size); + WriteParam(m, p.minimum_font_size); + WriteParam(m, p.minimum_logical_font_size); + WriteParam(m, p.default_encoding); + WriteParam(m, p.javascript_enabled); + WriteParam(m, p.javascript_can_open_windows_automatically); + WriteParam(m, p.loads_images_automatically); + WriteParam(m, p.plugins_enabled); + WriteParam(m, p.dom_paste_enabled); + WriteParam(m, p.developer_extras_enabled); + WriteParam(m, p.shrinks_standalone_images_to_fit); + WriteParam(m, p.uses_universal_detector); + WriteParam(m, p.text_areas_are_resizable); + WriteParam(m, p.dashboard_compatibility_mode); + WriteParam(m, p.java_enabled); + WriteParam(m, p.user_style_sheet_enabled); + WriteParam(m, p.user_style_sheet_location); + WriteParam(m, p.user_agent); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->standard_font_family) && + ReadParam(m, iter, &p->fixed_font_family) && + ReadParam(m, iter, &p->serif_font_family) && + ReadParam(m, iter, &p->sans_serif_font_family) && + ReadParam(m, iter, &p->cursive_font_family) && + ReadParam(m, iter, &p->fantasy_font_family) && + ReadParam(m, iter, &p->default_font_size) && + ReadParam(m, iter, &p->default_fixed_font_size) && + ReadParam(m, iter, &p->minimum_font_size) && + ReadParam(m, iter, &p->minimum_logical_font_size) && + ReadParam(m, iter, &p->default_encoding) && + ReadParam(m, iter, &p->javascript_enabled) && + ReadParam(m, iter, &p->javascript_can_open_windows_automatically) && + ReadParam(m, iter, &p->loads_images_automatically) && + ReadParam(m, iter, &p->plugins_enabled) && + ReadParam(m, iter, &p->dom_paste_enabled) && + ReadParam(m, iter, &p->developer_extras_enabled) && + ReadParam(m, iter, &p->shrinks_standalone_images_to_fit) && + ReadParam(m, iter, &p->uses_universal_detector) && + ReadParam(m, iter, &p->text_areas_are_resizable) && + ReadParam(m, iter, &p->dashboard_compatibility_mode) && + ReadParam(m, iter, &p->java_enabled) && + ReadParam(m, iter, &p->user_style_sheet_enabled) && + ReadParam(m, iter, &p->user_style_sheet_location) && + ReadParam(m, iter, &p->user_agent); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<WebPreferences>"); + } +}; + +// Traits for WebDropData +template <> +struct ParamTraits<WebDropData> { + typedef WebDropData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.url); + WriteParam(m, p.url_title); + WriteParam(m, p.filenames); + WriteParam(m, p.plain_text); + WriteParam(m, p.cf_html); + WriteParam(m, p.text_html); + WriteParam(m, p.file_description_filename); + WriteParam(m, p.file_contents); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->url_title) && + ReadParam(m, iter, &p->filenames) && + ReadParam(m, iter, &p->plain_text) && + ReadParam(m, iter, &p->cf_html) && + ReadParam(m, iter, &p->text_html) && + ReadParam(m, iter, &p->file_description_filename) && + ReadParam(m, iter, &p->file_contents); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<WebDropData>"); + } +}; + +} // namespace IPC + +#endif // CHROME_COMMON_RENDER_MESSAGES_H__ diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h new file mode 100644 index 0000000..e7f7ea3 --- /dev/null +++ b/chrome/common/render_messages_internal.h @@ -0,0 +1,1007 @@ +// 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 header is meant to be included in multiple passes, hence no traditional +// header guard. +// See ipc_message_macros.h for explanation of the macros and passes. + +#include <string> +#include <vector> + +#include "base/gfx/rect.h" +#include "base/gfx/size.h" +#include "base/shared_memory.h" +#include "chrome/common/ipc_message_macros.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/find_in_page_request.h" +#include "webkit/glue/cache_manager.h" +#include "webkit/glue/console_message_level.h" +#include "webkit/glue/context_node_types.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webinputevent.h" +#include "webkit/glue/window_open_disposition.h" + +void RenderMessagesInit(); + +// TODO(mpcomplete): rename ViewMsg and ViewHostMsg to something that makes +// more sense with our current design. + +//----------------------------------------------------------------------------- +// RenderView messages +// These are messages sent from the browser to the renderer process. + +IPC_BEGIN_MESSAGES(View, 1) + // Used typically when recovering from a crash. The new rendering process + // sets its global "next page id" counter to the given value. + IPC_MESSAGE_CONTROL1(ViewMsg_SetNextPageID, + int32 /* next_page_id */) + + // Tells the renderer to create a new view. + // This message is slightly different, the view it takes is the view to + // create, the message itself is sent as a non-view control message. + IPC_MESSAGE_CONTROL4(ViewMsg_New, HWND, HANDLE, WebPreferences, int32) + + // Tells the renderer to set its maximum cache size to the supplied value + IPC_MESSAGE_CONTROL3(ViewMsg_SetCacheCapacities, + size_t /* min_dead_capacity */, + size_t /* max_dead_capacity */, + size_t /* capacity */) + + // Reply in response to ViewHostMsg_ShowView or ViewHostMsg_ShowWidget. + // similar to the new command, but used when the renderer created a view + // first, and we need to update it + IPC_MESSAGE_ROUTED1(ViewMsg_CreatingNew_ACK, + HWND /* parent_hwnd */) + + // Tells the render view to close. + IPC_MESSAGE_ROUTED0(ViewMsg_Close) + + // Tells the render view to change its size. A ViewHostMsg_PaintRect message + // is generated in response provided new_size is not empty and not equal to + // the view's current size. The generated ViewHostMsg_PaintRect message will + // have the IS_RESIZE_ACK flag set. + IPC_MESSAGE_ROUTED1(ViewMsg_Resize, + gfx::Size /* new_size */) + + // Sent to inform the view that it was hidden. This allows it to reduce its + // resource utilization. + IPC_MESSAGE_ROUTED0(ViewMsg_WasHidden) + + // Tells the render view that it is no longer hidden (see WasHidden), and the + // render view is expected to respond with a full repaint if needs_repainting + // is true. In that case, the generated ViewHostMsg_PaintRect message will + // have the IS_RESTORE_ACK flag set. If needs_repainting is false, then this + // message does not trigger a message in response. + IPC_MESSAGE_ROUTED1(ViewMsg_WasRestored, + bool /* needs_repainting */) + + // Tells the render view to capture a thumbnail image of the page. The + // render view responds with a ViewHostMsg_Thumbnail. + IPC_MESSAGE_ROUTED0(ViewMsg_CaptureThumbnail) + + // Tells the render view that a ViewHostMsg_PaintRect message was processed. + // This signals the render view that it can send another PaintRect message. + IPC_MESSAGE_ROUTED0(ViewMsg_PaintRect_ACK) + + // Asks the renderer to calculate the number of printed pages according to the + // supplied settings. The renderer will reply with + // ViewHostMsg_DidGetPrintedPagesCount. + IPC_MESSAGE_ROUTED1(ViewMsg_GetPrintedPagesCount, + ViewMsg_Print_Params) + + // Tells the render view to switch the CSS to print media type, renders every + // requested pages and switch back the CSS to display media type. + IPC_MESSAGE_ROUTED1(ViewMsg_PrintPages, ViewMsg_PrintPages_Params) + + // Tells the render view that a ViewHostMsg_ScrollRect message was processed. + // This signals the render view that it can send another ScrollRect message. + IPC_MESSAGE_ROUTED0(ViewMsg_ScrollRect_ACK) + + // Message payload is a blob that should be cast to WebInputEvent + IPC_MESSAGE_ROUTED0(ViewMsg_HandleInputEvent) + + IPC_MESSAGE_ROUTED0(ViewMsg_MouseCaptureLost) + + // TODO(darin): figure out how this meshes with RestoreFocus + IPC_MESSAGE_ROUTED1(ViewMsg_SetFocus, bool /* enable */) + + // Tells the renderer to focus the first (last if reverse is true) focusable + // node. + IPC_MESSAGE_ROUTED1(ViewMsg_SetInitialFocus, bool /* reverse */) + + // Tells the renderer to perform the specified navigation, interrupting any + // existing navigation. + IPC_MESSAGE_ROUTED1(ViewMsg_Navigate, ViewMsg_Navigate_Params) + + IPC_MESSAGE_ROUTED0(ViewMsg_Stop) + + // Tells the renderer to load the specified html text and report a navigation + // to display_url if passing true for new navigation. + IPC_MESSAGE_ROUTED4(ViewMsg_LoadAlternateHTMLText, + std::string /* utf8 html text */, + bool, /* new navigation */ + GURL /* display url */, + std::string /* security info */) + + // This message notifies the renderer that the user has closed the FindInPage + // window (and that the selection should be cleared and the tick-marks + // erased). If |clear_selection| is true, it will also clear the current + // selection. + IPC_MESSAGE_ROUTED1(ViewMsg_StopFinding, bool /* clear_selection */) + + // These messages are typically generated from context menus and request the + // renderer to apply the specified operation to the current selection. + IPC_MESSAGE_ROUTED0(ViewMsg_Undo) + IPC_MESSAGE_ROUTED0(ViewMsg_Redo) + IPC_MESSAGE_ROUTED0(ViewMsg_Cut) + IPC_MESSAGE_ROUTED0(ViewMsg_Copy) + IPC_MESSAGE_ROUTED0(ViewMsg_Paste) + IPC_MESSAGE_ROUTED1(ViewMsg_Replace, std::wstring) + IPC_MESSAGE_ROUTED0(ViewMsg_Delete) + IPC_MESSAGE_ROUTED0(ViewMsg_SelectAll) + + // Copies the image at location x, y to the clipboard (if there indeed is an + // image at that location). + IPC_MESSAGE_ROUTED2(ViewMsg_CopyImageAt, + int /* x */, + int /* y */) + + // History system notification that the visited link database has been + // replaced. It has one SharedMemoryHandle argument consisting of the table + // handle. This handle is valid in the context of the renderer + IPC_MESSAGE_CONTROL1(ViewMsg_VisitedLink_NewTable, SharedMemoryHandle) + + // Sent when the user wants to search for a word on the page (find in page). + // Request parameters are passed in as a FindInPageMsg_Request struct. + IPC_MESSAGE_ROUTED1(ViewMsg_Find, FindInPageRequest) + + // Sent when the headers are available for a resource request. + IPC_MESSAGE_ROUTED2(ViewMsg_Resource_ReceivedResponse, + int /* request_id */, + ViewMsg_Resource_ResponseHead) + + // Sent as upload progress is being made + IPC_MESSAGE_ROUTED3(ViewMsg_Resource_UploadProgress, + int /* request_id */, + int64 /* position */, + int64 /* size */) + + // Sent when the request has been redirected. + IPC_MESSAGE_ROUTED2(ViewMsg_Resource_ReceivedRedirect, + int /* request_id */, + GURL /* new_url */) + + // Sent when some data from a resource request is ready. The handle should + // already be mapped into the process that receives this message. + IPC_MESSAGE_ROUTED3(ViewMsg_Resource_DataReceived, + int /* request_id */, + SharedMemoryHandle /* data */, + int /* data_len */) + + // Sent when the request has been completed. + IPC_MESSAGE_ROUTED2(ViewMsg_Resource_RequestComplete, + int /* request_id */, + URLRequestStatus /* status */) + + // Request for the renderer to evaluate an xpath to a frame and execute a + // javascript: url in that frame's context. The message is completely + // asynchronous and no corresponding response message is sent back. + // + // frame_xpath contains the modified xpath notation to identify an inner + // subframe (starting from the root frame). It is a concatenation of + // number of smaller xpaths delimited by '\n'. Each chunk in the string can + // be evaluated to a frame in its parent-frame's context. + // + // 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 + // + // jscript_url is the string containing the javascript: url to be executed + // in the target frame's context. The string should start with "javascript:" + // and continue with a valid JS text. + IPC_MESSAGE_ROUTED2(ViewMsg_ScriptEvalRequest, + std::wstring, /* frame_xpath */ + std::wstring /* jscript_url */) + + // Log a message to the console of the target frame + IPC_MESSAGE_ROUTED3(ViewMsg_AddMessageToConsole, + std::wstring, /* frame_xpath */ + std::wstring, /* msg */ + ConsoleMessageLevel /* level */) + + // Initialize the V8 debugger in the renderer. + IPC_MESSAGE_ROUTED0(ViewMsg_DebugAttach) + + // Send a command to the V8 debugger. + IPC_MESSAGE_ROUTED1(ViewMsg_SendToDebugger, + std::wstring /* cmd */) + + // Change the text size in the renderer. + IPC_MESSAGE_ROUTED1(ViewMsg_AlterTextSize, + int /* enum text_zoom::TextSize from text_zoom.h */) + + // Change encoding of page in the renderer. + IPC_MESSAGE_ROUTED1(ViewMsg_SetPageEncoding, + std::wstring /*new encoding name*/) + + // Inspect the element at the specified coordinates + IPC_MESSAGE_ROUTED2(ViewMsg_InspectElement, + int /* x */, + int /* y */) + + // Show the JavaScript console + IPC_MESSAGE_ROUTED0(ViewMsg_ShowJavaScriptConsole) + + // Requests the renderer to reserve a range of page ids. + IPC_MESSAGE_ROUTED1(ViewMsg_ReservePageIDRange, + int /* size_of_range */) + + // Fill a form with data and optionally submit it + IPC_MESSAGE_ROUTED1(ViewMsg_FormFill, + FormData /* form */) + + // Fill a password form and prepare field autocomplete for multiple + // matching logins. + IPC_MESSAGE_ROUTED1(ViewMsg_FillPasswordForm, + PasswordFormDomManager::FillData /* form_data */) + + // D&d drop target messages. + IPC_MESSAGE_ROUTED3(ViewMsg_DragTargetDragEnter, + WebDropData /* drop_data */, + gfx::Point /* client_pt */, + gfx::Point /* screen_pt */) + IPC_MESSAGE_ROUTED2(ViewMsg_DragTargetDragOver, + gfx::Point /* client_pt */, + gfx::Point /* screen_pt */) + IPC_MESSAGE_ROUTED0(ViewMsg_DragTargetDragLeave) + IPC_MESSAGE_ROUTED2(ViewMsg_DragTargetDrop, + gfx::Point /* client_pt */, + gfx::Point /* screen_pt */) + + IPC_MESSAGE_ROUTED1(ViewMsg_UploadFile, ViewMsg_UploadFile_Params) + + // Notifies the renderer of updates in mouse position of an in-progress + // drag. if |ended| is true, then the user has ended the drag operation. + IPC_MESSAGE_ROUTED5(ViewMsg_DragSourceEndedOrMoved, + int /* client_x */, + int /* client_y */, + int /* screen_x */, + int /* screen_y */, + bool /* ended */) + + // Notifies the renderer that the system DoDragDrop call has ended. + IPC_MESSAGE_ROUTED0(ViewMsg_DragSourceSystemDragEnded) + + // Used to tell a render view whether it should expose DOM Automation bindings + // that allow JS content in the DOM to send a JSON-encoded value to the + // browser process. (By default this isn't allowed unless the app has + // been started up with the --dom-automation switch.) + IPC_MESSAGE_ROUTED1(ViewMsg_AllowDomAutomationBindings, + bool /* binding_allowed */) + + // Used to tell a render view whether it should expose DOM UI bindings + // that allow JS content in the DOM to send a JSON-encoded value to the + // browser process. This is for HTML-based UI. + IPC_MESSAGE_ROUTED0(ViewMsg_AllowDOMUIBindings) + + // Tell the renderer to add a property to the DOMUI binding object. This + // only works if we allowed DOMUI bindings. + IPC_MESSAGE_ROUTED2(ViewMsg_SetDOMUIProperty, + std::string /* property_name */, + std::string /* property_value_json */) + + // This message starts/stop monitoring the status of the focused edit + // control of a renderer process. + // Parameters + // * is_active (bool) + // Represents whether or not the IME is active in a browser process. + // The possible actions when a renderer process receives this message are + // listed below: + // Value Action + // true Start sending IPC messages, ViewHostMsg_ImeUpdateStatus + // to notify the status of the focused edit control. + // false Stop sending IPC messages, ViewHostMsg_ImeUpdateStatus. + IPC_MESSAGE_ROUTED1(ViewMsg_ImeSetInputMode, + bool /* is_active */) + + // This message sends a string being composed with IME. + // Parameters + // * string_type (int) + // Represents the type of the string in the 'ime_string' parameter. + // Its possible values and description are listed below: + // Value Description + // 0 The parameter is not used. + // GCS_RESULTSTR The parameter represents a result string. + // GCS_COMPSTR The parameter represents a composition string. + // * cursor_position (int) + // Represents the position of the cursor + // * target_start (int) + // Represents the position of the beginning of the selection + // * target_end (int) + // Represents the position of the end of the selection + // * ime_string (std::wstring) + // Represents the string retrieved from IME (Input Method Editor) + IPC_MESSAGE_ROUTED5(ViewMsg_ImeSetComposition, + int, /* string_type */ + int, /* cursor_position */ + int, /* target_start */ + int, /* target_end */ + std::wstring /* ime_string */ ) + + // This passes a set of webkit preferences down to the renderer. + IPC_MESSAGE_ROUTED1(ViewMsg_UpdateWebPreferences, WebPreferences) + + // Used to notify the render-view that the browser has received a reply for + // the Find operation and is interested in receiving the next one. This is + // used to prevent the renderer from spamming the browser process with + // results. + IPC_MESSAGE_ROUTED0(ViewMsg_FindReplyACK) + + // Used to notify the render-view that we have received a target URL. Used + // to prevent target URLs spamming the browser. + IPC_MESSAGE_ROUTED0(ViewMsg_UpdateTargetURL_ACK) + + // Sets the alternate error page URL (link doctor) for the renderer process. + IPC_MESSAGE_ROUTED1(ViewMsg_SetAltErrorPageURL, GURL) + + // Install the first missing pluign. + IPC_MESSAGE_ROUTED0(ViewMsg_InstallMissingPlugin) + + IPC_MESSAGE_ROUTED1(ViewMsg_RunFileChooserResponse, + std::wstring /* file_name */) + + // Used to instruct the RenderView to go into "view source" mode. + IPC_MESSAGE_ROUTED0(ViewMsg_EnableViewSourceMode) + + IPC_MESSAGE_ROUTED2(ViewMsg_UpdateBackForwardListCount, + int /* back_list_count */, + int /* forward_list_count */) + + // Get all savable resource links from current webpage, include main + // frame and sub-frame. + IPC_MESSAGE_ROUTED1(ViewMsg_GetAllSavableResourceLinksForCurrentPage, + GURL /* url of page which is needed to save */) + + // Get html data by serializing all frames of current page with lists + // which contain all resource links that have local copy. + IPC_MESSAGE_ROUTED3(ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks, + std::vector<std::wstring> /* urls which have local copy */, + std::vector<std::wstring> /* paths of local copy */, + std::wstring /* local directory path */) + + // Requests application info for the page. The renderer responds back with + // ViewHostMsg_DidGetApplicationInfo. + IPC_MESSAGE_ROUTED1(ViewMsg_GetApplicationInfo, int32 /*page_id*/) + + // Requests the renderer to download the specified image encode it as PNG + // and send the PNG data back ala ViewHostMsg_DidDownloadImage. + IPC_MESSAGE_ROUTED3(ViewMsg_DownloadImage, + int /* identifier for the request */, + GURL /* URL of the image */, + int /* Size of the image. Normally 0, but set if you have + a preferred image size to request, such as when + downloading the favicon */) + + // When a renderer sends a ViewHostMsg_Focus to the browser process, + // the browser has the option of sending a ViewMsg_CantFocus back to + // the renderer. + IPC_MESSAGE_ROUTED0(ViewMsg_CantFocus) + + // Instructs the renderer to invoke the frame's shouldClose method, which + // runs the onbeforeunload event handler. Expects the result to be returned + // via ViewHostMsg_ShouldClose. + IPC_MESSAGE_ROUTED1(ViewMsg_ShouldClose, bool /* is_closing_browser */) + + // Instructs the renderer to close the current page, including running the + // onunload event handler. Expects a ClosePage_ACK message when finished. + IPC_MESSAGE_ROUTED3(ViewMsg_ClosePage, + int /* new_render_process_host_id */, + int /* new_request_id */, + bool /* is_closing_browser */) + + // Asks the renderer to send back stats on the WebCore cache broken down by + // resource types. + IPC_MESSAGE_CONTROL0(ViewMsg_GetCacheResourceStats) + + // Notifies the renderer about ui theme changes + IPC_MESSAGE_ROUTED0(ViewMsg_ThemeChanged) + +IPC_END_MESSAGES(View) + + +//----------------------------------------------------------------------------- +// WebContents messages +// These are messages sent from the renderer to the browser process. + +IPC_BEGIN_MESSAGES(ViewHost, 2) + // Sent by the renderer when it is creating a new view. The browser creates + // a tab for it and responds with a ViewMsg_CreatingNew_ACK. If route_id is + // MSG_ROUTING_NONE, the view couldn't be created. modal_dialog_event is set + // by the browser when a modal dialog is shown. + IPC_SYNC_MESSAGE_CONTROL2_2(ViewHostMsg_CreateView, + int /* opener_id */, + bool /* user_gesture */, + int /* route_id */, + HANDLE /* modal_dialog_event */) + + // Similar to ViewHostMsg_CreateView, except used for sub-widgets, like + // <select> dropdowns. This message is sent to the WebContents that + // contains the widget being created. + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_CreateWidget, + int /* opener_id */, + int /* route_id */) + + // These two messages are sent as a result of the above two, in the browser + // process, from RenderWidgetHelper to RenderViewHost. + IPC_MESSAGE_ROUTED2(ViewHostMsg_CreateViewWithRoute, + int /* route_id */, + HANDLE /* modal_dialog_event */) + + IPC_MESSAGE_ROUTED1(ViewHostMsg_CreateWidgetWithRoute, + int /* route_id */) + + // These two messages are sent to the parent RenderViewHost to display the + // page/widget that was created by CreateView/CreateWidget. routing_id + // refers to the id that was returned from the Create message above. + // The initial_position parameter is a rectangle in screen coordinates. + // + // FUTURE: there will probably be flags here to control if the result is + // in a new window. + IPC_MESSAGE_ROUTED4(ViewHostMsg_ShowView, + int /* route_id */, + WindowOpenDisposition /* disposition */, + gfx::Rect /* initial_pos */, + bool /* opened_by_user_gesture */) + + IPC_MESSAGE_ROUTED2(ViewHostMsg_ShowWidget, + int /* route_id */, + gfx::Rect /* initial_pos */) + + // This message is sent after ViewHostMsg_ShowView to cause the RenderView + // to run in a modal fashion until it is closed. + IPC_SYNC_MESSAGE_ROUTED0_0(ViewHostMsg_RunModal) + + IPC_MESSAGE_CONTROL1(ViewHostMsg_UpdatedCacheStats, + CacheManager::UsageStats /* stats */) + + // Indicates the renderer is ready in response to a ViewMsg_New or + // a ViewMsg_CreatingNew_ACK. + IPC_MESSAGE_ROUTED0(ViewHostMsg_RendererReady) + + // Indicates the renderer process is gone. This actually is sent by the + // browser process to itself, but keeps the interface cleaner. + IPC_MESSAGE_ROUTED0(ViewHostMsg_RendererGone) + + // Sent by the renderer process to request that the browser close the view. + // This corresponds to the window.close() API, and the browser may ignore + // this message. Otherwise, the browser will generates a ViewMsg_Close + // message to close the view. + IPC_MESSAGE_ROUTED0(ViewHostMsg_Close) + + // Sent by the renderer process to request that the browser move the view. + // This corresponds to the window.resizeTo() and window.moveTo() APIs, and + // the browser may ignore this message. + IPC_MESSAGE_ROUTED1(ViewHostMsg_RequestMove, + gfx::Rect /* position */) + + // Notifies the browser that a frame in the view has changed. This message + // has a lot of parameters and is packed/unpacked by functions defined in + // render_messages.h. + IPC_MESSAGE_ROUTED1(ViewHostMsg_FrameNavigate, + ViewHostMsg_FrameNavigate_Params) + + // Notifies the browser that we have session history information. + // page_id: unique ID that allows us to distinguish between history entries. + IPC_MESSAGE_ROUTED4(ViewHostMsg_UpdateState, + int32 /* page_id */, + GURL /* url */, + std::wstring /* title */, + std::string /* state */) + + // Changes the title for the page in the UI when the page is navigated or the + // title changes. + // TODO(darin): use a UTF-8 string to reduce data size + IPC_MESSAGE_ROUTED2(ViewHostMsg_UpdateTitle, int32, std::wstring) + + // Change the encoding name of the page in UI when the page has detected proper + // encoding name. + IPC_MESSAGE_ROUTED1(ViewHostMsg_UpdateEncoding, + std::wstring /* new encoding name */) + + // Notifies the browser that we want to show a destination url for a potential + // action (e.g. when the user is hovering over a link). + IPC_MESSAGE_ROUTED2(ViewHostMsg_UpdateTargetURL, int32, GURL) + + // Sent when the renderer is loading a frame + IPC_MESSAGE_ROUTED1(ViewHostMsg_DidStartLoading, int32) + + // Sent when the renderer is done loading a frame + IPC_MESSAGE_ROUTED1(ViewHostMsg_DidStopLoading, int32) + + // Sent when the renderer loads a resource from its memory cache. + // The security info is non empty if the resource was originally loaded over + // a secure connection. + // Note: May only be sent once per URL per frame per committed load. + IPC_MESSAGE_ROUTED2(ViewHostMsg_DidLoadResourceFromMemoryCache, + GURL /* url */, + std::string /* security info */) + + // Sent when the renderer starts a provisional load for a frame. + IPC_MESSAGE_ROUTED2(ViewHostMsg_DidStartProvisionalLoadForFrame, + bool /* true if it is the main frame */, + GURL /* url */) + + // Sent when the renderer fails a provisional load with an error. + IPC_MESSAGE_ROUTED4(ViewHostMsg_DidFailProvisionalLoadWithError, + bool /* true if it is the main frame */, + int /* error_code */, + GURL /* url */, + bool /* true if the failure is the result of + navigating to a POST again and we're going to + show the POST interstitial */ ) + + // Sent to paint part of the view. In response to this message, the host + // generates a ViewMsg_PaintRect_ACK message. + IPC_MESSAGE_ROUTED1(ViewHostMsg_PaintRect, + ViewHostMsg_PaintRect_Params) + + // Sent to scroll part of the view. In response to this message, the host + // generates a ViewMsg_ScrollRect_ACK message. + IPC_MESSAGE_ROUTED1(ViewHostMsg_ScrollRect, + ViewHostMsg_ScrollRect_Params) + + // Acknowledges receipt of a ViewMsg_HandleInputEvent message. + // Payload is a WebInputEvent::Type which is the type of the event, followed + // by an optional WebInputEvent which is provided only if the event was not + // processed. + IPC_MESSAGE_ROUTED0(ViewHostMsg_HandleInputEvent_ACK) + + IPC_MESSAGE_ROUTED0(ViewHostMsg_Focus) + IPC_MESSAGE_ROUTED0(ViewHostMsg_Blur) + + // Returns the window location of the given window. + IPC_SYNC_MESSAGE_ROUTED1_1(ViewHostMsg_GetWindowRect, + HWND /* window */, + gfx::Rect /* Out: Window location */) + + IPC_MESSAGE_ROUTED1(ViewHostMsg_SetCursor, WebCursor) + // Result of string search in the page. + // Response to ViewMsg_Find with the results of the requested find-in-page + // search, the number of matches found and the selection rect (in screen + // coordinates) for the string found. If |final_update| is false, it signals + // that this is not the last Find_Reply message - more will be sent as the + // scoping effort continues. + IPC_MESSAGE_ROUTED5(ViewHostMsg_Find_Reply, + int /* request_id */, + int /* number of matches */, + gfx::Rect /* selection_rect */, + int /* active_match_ordinal */, + bool /* final_update */) + + // Makes a resource request via the browser. + IPC_MESSAGE_ROUTED2(ViewHostMsg_RequestResource, + int /* request_id */, + ViewHostMsg_Resource_Request) + + // Cancels a resource request with the ID given as the parameter. + IPC_MESSAGE_ROUTED1(ViewHostMsg_CancelRequest, + int /* request_id */) + + // Makes a synchronous resource request via the browser. + IPC_SYNC_MESSAGE_ROUTED2_1(ViewHostMsg_SyncLoad, + int /* request_id */, + ViewHostMsg_Resource_Request, + ViewHostMsg_SyncLoad_Result) + + // Used to set a cookie. The cookie is set asynchronously, but will be + // available to a subsequent ViewHostMsg_GetCookies request. + IPC_MESSAGE_CONTROL3(ViewHostMsg_SetCookie, + GURL /* url */, + GURL /* policy_url */, + std::string /* cookie */) + + // Used to get cookies for the given URL + IPC_SYNC_MESSAGE_CONTROL2_1(ViewHostMsg_GetCookies, + GURL /* url */, + GURL /* policy_url */, + std::string /* cookies */) + + // Used to get the list of plugins + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_GetPlugins, + bool /* refresh*/, + std::vector<WebPluginInfo> /* plugins */) + + // Returns a path to a plugin dll for the given url and mime type. If there's + // no plugin, an empty string is returned. + IPC_SYNC_MESSAGE_CONTROL3_2(ViewHostMsg_GetPluginPath, + GURL /* url */, + std::string /* mime_type */, + std::string /* clsid */, + std::wstring /* filename */, + std::string /* actual mime type for url */) + + // Requests spellcheck for a word. + IPC_SYNC_MESSAGE_ROUTED1_2(ViewHostMsg_SpellCheck, + std::wstring /* word to check */, + int /* misspell location */, + int /* misspell length */) + + // Initiate a download based on user actions like 'ALT+click'. + IPC_MESSAGE_ROUTED2(ViewHostMsg_DownloadUrl, + GURL /* url */, + GURL /* referrer */) + + // Used to go to the session history entry at the given offset (ie, -1 will + // return the "back" item). + IPC_MESSAGE_ROUTED1(ViewHostMsg_GoToEntryAtOffset, + int /* offset (from current) of history item to get */) + + IPC_SYNC_MESSAGE_ROUTED3_2(ViewHostMsg_RunJavaScriptMessage, + std::wstring /* in - alert message */, + std::wstring /* in - default prompt */, + int /* in - dialog flags */, + bool /* out - success */, + std::wstring /* out - prompt field */) + + // Sets the contents for the given page (URL and page ID are the first two + // arguments) given the contents that is the 3rd. + IPC_MESSAGE_CONTROL3(ViewHostMsg_PageContents, GURL, int32, std::wstring) + + // Specifies the URL as the first parameter (a wstring) and thumbnail as + // binary data as the second parameter. Our macros don't handle binary data, + // so this is declared "empty," to be encoded by the caller/receiver. + IPC_MESSAGE_EMPTY(ViewHostMsg_Thumbnail) + + // Notification that the url for the favicon of a site has been determined. + IPC_MESSAGE_ROUTED2(ViewHostMsg_UpdateFavIconURL, + int32 /* page_id */, + GURL /* url of the favicon */) + + // Used to tell the parent that the user right clicked on an area of the + // content area, and a context menu should be shown for it. The params + // object contains information about the node(s) that were selected when the + // user right clicked. + IPC_MESSAGE_ROUTED1(ViewHostMsg_ContextMenu, ViewHostMsg_ContextMenu_Params) + + // Request that the given URL be opened in the specified manner. + IPC_MESSAGE_ROUTED2(ViewHostMsg_OpenURL, + GURL /* url */, + WindowOpenDisposition /* disposition */) + + // Following message is used to communicate the values received by the + // callback binding the JS to Cpp. + // An instance of browser that has an automation host listening to it can + // have a javascript send a native value (string, number, boolean) to the + // listener in Cpp. (DomAutomationController) + IPC_MESSAGE_ROUTED2(ViewHostMsg_DomOperationResponse, + std::string /* json_string */, + int /* automation_id */) + + // A message from HTML-based UI. When (trusted) Javascript calls + // send(message, args), this message is sent to the browser. + IPC_MESSAGE_ROUTED2(ViewHostMsg_DOMUISend, + std::string /* message */, + std::string /* args (as a JSON string) */) + + // A renderer sends this to the browser process when it wants to create a + // plugin. The browser will create the plugin process if necessary, and + // will return the channel name on success. On error an empty string is + // returned. + IPC_SYNC_MESSAGE_CONTROL4_2(ViewHostMsg_OpenChannelToPlugin, + GURL /* url */, + std::string /* mime_type */, + std::string /* clsid */, + std::wstring /* locale */, + std::wstring /* channel_name */, + std::wstring /* plugin_path */) + + // Clipboard IPC messages + IPC_MESSAGE_CONTROL0(ViewHostMsg_ClipboardClear) + IPC_MESSAGE_CONTROL1(ViewHostMsg_ClipboardWriteText, + std::wstring /* text */) + IPC_MESSAGE_CONTROL2(ViewHostMsg_ClipboardWriteHTML, + std::wstring /* html */, + GURL /* url */) + IPC_MESSAGE_CONTROL2(ViewHostMsg_ClipboardWriteBookmark, + std::wstring /* title */, + GURL /* url */) + // This message is synchronized so that the renderer known when it is safe to + // free the shared memory used to transfer the bitmap. + IPC_SYNC_MESSAGE_CONTROL2_0(ViewHostMsg_ClipboardWriteBitmap, + SharedMemoryHandle /* bitmap */, + gfx::Size /* size */) + IPC_MESSAGE_CONTROL0(ViewHostMsg_ClipboardWriteWebSmartPaste) + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_ClipboardIsFormatAvailable, + int /* format */, + bool /* result */) + IPC_SYNC_MESSAGE_CONTROL0_1(ViewHostMsg_ClipboardReadText, + std::wstring /* result */) + IPC_SYNC_MESSAGE_CONTROL0_1(ViewHostMsg_ClipboardReadAsciiText, + std::string /* result */) + IPC_SYNC_MESSAGE_CONTROL0_2(ViewHostMsg_ClipboardReadHTML, + std::wstring /* markup */, + GURL /* url */) + + // Request that the given font be loaded by the browser. + // Please see ResourceMessageFilter::OnLoadFont for details. + IPC_SYNC_MESSAGE_CONTROL1_0(ViewHostMsg_LoadFont, + LOGFONT /* font data */) + + // Returns the monitor information corresponding to the HWND. + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_GetMonitorInfoForWindow, + HWND /* In: Window handle */, + MONITORINFOEX /* Out: Monitor information */) + + // Send the tooltip text for the current mouse position to the browser. + IPC_MESSAGE_ROUTED1(ViewHostMsg_SetTooltipText, + std::wstring /* tooltip text string */) + + // Asks the browser to display the file chooser. The result is returned in a + // ViewHost_RunFileChooserResponse message. + IPC_MESSAGE_ROUTED1(ViewHostMsg_RunFileChooser, + std::wstring /* Default file name */) + + // Notification that password forms have been seen that are candidates for + // filling/submitting by the password manager + IPC_MESSAGE_ROUTED1(ViewHostMsg_PasswordFormsSeen, + std::vector<PasswordForm> /* forms */) + + // Used to tell the parent the user started dragging in the content area. The + // WebDropData struct contains contextual information about the pieces of the + // page the user dragged. The parent uses this notification to initiate a + // drag session at the OS level. + IPC_MESSAGE_ROUTED1(ViewHostMsg_StartDragging, + WebDropData /* drop_data */) + + // The page wants to update the mouse cursor during a drag & drop operation. + // |is_drop_target| is true if the mouse is over a valid drop target. + IPC_MESSAGE_ROUTED1(ViewHostMsg_UpdateDragCursor, + bool /* is_drop_target */) + + // Tells the browser to move the focus to the next (previous if reverse is + // true) focusable element. + IPC_MESSAGE_ROUTED1(ViewHostMsg_TakeFocus, bool /* reverse */) + + // Notification that the page has an OpenSearch description document + // associated with it. + IPC_MESSAGE_ROUTED3(ViewHostMsg_PageHasOSDD, + int32 /* page_id */, + GURL /* url of OS description document */, + bool /* autodetected */) + + // required for synchronizing IME windows. + // Parameters + // * control (ViewHostMsg_ImeControl) + // It specifies the code for controlling the IME attached to + // the browser process. This parameter should be one of the values + // listed below. + // + IME_DISABLE + // Deactivate the IME attached to a browser process. + // This code is typically used for notifying a renderer process + // moves its input focus to a password input. A browser process + // finishes the current composition and deactivate IME. + // If a renderer process sets its input focus to another edit + // control which is not a password input, it needs to re-activate + // IME, it has to send another message with this code IME_MOVE_WINDOWS + // and set the new caret position. + // + IME_MOVE_WINDOWS + // Activate the IME attached to a browser process and set the position + // of its IME windows. + // This code is typically used for the following cases: + // - Notifying a renderer process moves the caret position of the + // focused edit control, or; + // - Notifying a renderer process moves its input focus from a + // password input to an editable control which is NOT a password + // input. + // A renderer process also has to set caret_x and caret_y and + // specify the new caret position. + // + IME_COMPLETE_COMPOSITION + // Finish the current composition. + // This code is used for notifying a renderer process moves its + // input focus from an editable control being composed to another one + // which is NOT a password input. A browser process closes its IME + // windows without changing the activation status of its IME, i.e. it + // keeps activating its IME. + // * caret_x (int) + // * caret_y (int) + // They specify the position of the input caret. + IPC_MESSAGE_ROUTED3(ViewHostMsg_ImeUpdateStatus, + ViewHostMsg_ImeControl, /* control */ + int, /* caret_x */ + int /* caret_y */) + + // Response for InspectElement request. Returns the number of resources + // identified by InspectorController. + IPC_MESSAGE_ROUTED1(ViewHostMsg_InspectElement_Reply, + int /* number of resources */) + + // Tells the browser that the renderer is done calculating the number of + // rendered pages according to the specified settings. + IPC_MESSAGE_ROUTED2(ViewHostMsg_DidGetPrintedPagesCount, + int /* rendered document cookie */, + int /* number of rendered pages */) + + // Sends back to the browser the rendered "printed page" that was requested by + // a ViewMsg_PrintPage message or from scripted printing. The memory handle in + // this message is already valid in the browser process. + IPC_MESSAGE_ROUTED1(ViewHostMsg_DidPrintPage, + ViewHostMsg_DidPrintPage_Params /* page content */) + + // The renderer wants to know the default print settings. + IPC_SYNC_MESSAGE_ROUTED0_1(ViewHostMsg_GetDefaultPrintSettings, + ViewMsg_Print_Params /* default_settings */) + + // It's the renderer that controls the printing process when it is generated + // by javascript. This step is about showing UI to the user to select the + // final print settings. The output parameter is the same as + // ViewMsg_PrintPages which is executed implicitly. + IPC_SYNC_MESSAGE_ROUTED3_1(ViewHostMsg_ScriptedPrint, + HWND /* host_window */, + int /* cookie */, + int /* expected_pages_count */, + ViewMsg_PrintPages_Params /* settings choosen by + the user*/) + + // WebKit and JavaScript error messages to log to the console + // or debugger UI. + IPC_MESSAGE_ROUTED3(ViewHostMsg_AddMessageToConsole, + std::wstring, /* msg */ + int32, /* line number */ + std::wstring /* source id */) + + // Response message for ViewMsg_DebugAttach. + IPC_MESSAGE_ROUTED0(ViewHostMsg_DidDebugAttach) + + // WebKit and JavaScript error messages to log to the console + // or debugger UI. + IPC_MESSAGE_ROUTED1(ViewHostMsg_DebuggerOutput, + std::wstring /* msg */) + + // Send back a string to be recorded by UserMetrics. + IPC_MESSAGE_ROUTED1(ViewHostMsg_UserMetricsRecordAction, + std::wstring /* action */) + + // Request for a DNS prefetch of the names in the array. + // NameList is typedef'ed std::vector<std::string> + IPC_MESSAGE_CONTROL1(ViewHostMsg_DnsPrefetch, + std::vector<std::string> /* hostnames */) + + // Notifies when default plugin updates status of the missing plugin. + IPC_MESSAGE_ROUTED1(ViewHostMsg_MissingPluginStatus, + int /* status */) + + // Sent by the renderer process to indicate that a plugin instance has + // crashed. + IPC_MESSAGE_ROUTED1(ViewHostMsg_CrashedPlugin, + std::wstring /* plugin_path */) + + // Dsiplays a JavaScript out-of-memory message in the infobar. + IPC_MESSAGE_ROUTED0(ViewHostMsg_JSOutOfMemory) + + // Displays a box to confirm that the user wants to navigate away from the + // page. Replies true if yes, false otherwise, the reply string is ignored, + // but is included so that we can use OnJavaScriptMessageBoxClosed. + IPC_SYNC_MESSAGE_ROUTED1_2(ViewHostMsg_RunBeforeUnloadConfirm, + std::wstring /* in - alert message */, + bool /* out - success */, + std::wstring /* out - This is ignored.*/) + + IPC_MESSAGE_ROUTED3(ViewHostMsg_SendCurrentPageAllSavableResourceLinks, + std::vector<GURL> /* all savable resource links */, + std::vector<GURL> /* all referrers of resource links */, + std::vector<GURL> /* all frame links */) + + IPC_MESSAGE_ROUTED3(ViewHostMsg_SendSerializedHtmlData, + GURL /* frame's url */, + std::string /* data buffer */, + int32 /* complete status */) + + IPC_SYNC_MESSAGE_ROUTED4_1(ViewHostMsg_ShowModalHTMLDialog, + GURL /* url */, + int /* width */, + int /* height */, + std::string /* json_arguments */, + std::string /* json_retval */) + + IPC_MESSAGE_ROUTED2(ViewHostMsg_DidGetApplicationInfo, + int32 /* page_id */, + webkit_glue::WebApplicationInfo) + + // Provides the result from running OnMsgShouldClose. |proceed| matches the + // return value of the the frame's shouldClose method (which includes the + // onbeforeunload handler): true if the user decided to proceed with leaving + // the page. + IPC_MESSAGE_ROUTED2(ViewHostMsg_ShouldClose_ACK, + bool /* proceed */, + bool /* is_closing_browser */) + + // Indicates that the current page has been closed, after a ClosePage + // message. + IPC_MESSAGE_ROUTED3(ViewHostMsg_ClosePage_ACK, + int /* new_render_process_host_id */, + int /* new_request_id */, + bool /* is_closing_browser */) + + IPC_MESSAGE_ROUTED4(ViewHostMsg_DidDownloadImage, + int /* Identifier of the request */, + GURL /* URL of the image */, + bool /* true if there was a network error */, + SkBitmap /* image_data */) + + // Sent to query MIME information. + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_GetMimeTypeFromExtension, + std::wstring /* extension */, + std::string /* mime_type */) + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_GetMimeTypeFromFile, + std::wstring /* file_path */, + std::string /* mime_type */) + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_GetPreferredExtensionForMimeType, + std::string /* mime_type */, + std::wstring /* extension */) + + // Get the CPBrowsingContext associated with the renderer sending this + // message. + IPC_SYNC_MESSAGE_CONTROL0_1(ViewHostMsg_GetCPBrowsingContext, + uint32 /* context */) + + // Sent when the renderer process is done processing a DataReceived + // message. + IPC_MESSAGE_ROUTED1(ViewHostMsg_DataReceived_ACK, + int /* request_id */) + + // Sent when a provisional load on the main frame redirects. + IPC_MESSAGE_ROUTED3(ViewHostMsg_DidRedirectProvisionalLoad, + int /* page_id */, + GURL /* last url */, + GURL /* url redirected to */) + + // Sent when the renderer process to acknowlege receipt of and UploadProgress + // message. + IPC_MESSAGE_ROUTED1(ViewHostMsg_UploadProgress_ACK, + int /* request_id */) + + // Duplicates a shared memory handle from the renderer to the browser. Then + // the renderer can flush the handle. + IPC_SYNC_MESSAGE_ROUTED1_1(ViewHostMsg_DuplicateSection, + SharedMemoryHandle /* renderer handle */, + SharedMemoryHandle /* browser handle */) + + // Provide the browser process with information about the WebCore resource + // cache. + IPC_MESSAGE_CONTROL1(ViewHostMsg_ResourceTypeStats, + CacheManager::ResourceTypeStats) + + // Notify the browser that this render either has or doesn't have a + // beforeunload or unload handler. + IPC_MESSAGE_ROUTED1(ViewHostMsg_UnloadListenerChanged, + bool /* has_listener */) + +IPC_END_MESSAGES(ViewHost) diff --git a/chrome/common/resource_bundle.cc b/chrome/common/resource_bundle.cc new file mode 100644 index 0000000..3beccb4 --- /dev/null +++ b/chrome/common/resource_bundle.cc @@ -0,0 +1,333 @@ +// 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 "chrome/common/resource_bundle.h" + +#include <atlbase.h> + +#include "base/file_util.h" +#include "base/gfx/png_decoder.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/resource_util.h" +#include "base/scoped_ptr.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/win_util.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/win_util.h" +#include "SkBitmap.h" + +using namespace std; + +ResourceBundle *ResourceBundle::g_shared_instance_ = NULL; + +// Returns the flags that should be passed to LoadLibraryEx. +DWORD GetDataDllLoadFlags() { + if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) + return LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE; + + return 0; +} + +/* static */ +void ResourceBundle::InitSharedInstance(const std::wstring& pref_locale) { + DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; + g_shared_instance_ = new ResourceBundle(); + + g_shared_instance_->LoadLocaleResources(pref_locale); +} + +/* static */ +void ResourceBundle::CleanupSharedInstance() { + if (g_shared_instance_) { + delete g_shared_instance_; + g_shared_instance_ = NULL; + } +} + +/* static */ +ResourceBundle& ResourceBundle::GetSharedInstance() { + // Must call InitSharedInstance before this function. + CHECK(g_shared_instance_ != NULL); + return *g_shared_instance_; +} + +ResourceBundle::ResourceBundle() + : locale_resources_dll_(NULL), + theme_dll_(NULL) { +} + +ResourceBundle::~ResourceBundle() { + for (SkImageMap::iterator i = skia_images_.begin(); + i != skia_images_.end(); i++) { + delete i->second; + } + skia_images_.clear(); + + if (locale_resources_dll_) { + BOOL rv = FreeLibrary(locale_resources_dll_); + DCHECK(rv); + } + if (theme_dll_) { + BOOL rv = FreeLibrary(theme_dll_); + DCHECK(rv); + } +} + +void ResourceBundle::LoadLocaleResources(const std::wstring& pref_locale) { + DCHECK(NULL == locale_resources_dll_) << "locale dll already loaded"; + const std::wstring& locale_path = GetLocaleDllPath(pref_locale); + if (locale_path.empty()) { + // It's possible that there are no locale dlls found, in which case we just + // return. + NOTREACHED(); + return; + } + + // The dll should only have resources, not executable code. + locale_resources_dll_ = LoadLibraryEx(locale_path.c_str(), NULL, + GetDataDllLoadFlags()); + DCHECK(locale_resources_dll_ != NULL) << "unable to load generated resources"; +} + +std::wstring ResourceBundle::GetLocaleDllPath(const std::wstring& pref_locale) { + std::wstring locale_path; + PathService::Get(chrome::DIR_LOCALES, &locale_path); + + const std::wstring app_locale = l10n_util::GetApplicationLocale(pref_locale); + if (app_locale.empty()) + return app_locale; + + file_util::AppendToPath(&locale_path, app_locale + L".dll"); + return locale_path; +} + +void ResourceBundle::LoadThemeResources() { + DCHECK(NULL == theme_dll_) << "theme dll already loaded"; + std::wstring theme_dll_path; + PathService::Get(chrome::DIR_THEMES, &theme_dll_path); + file_util::AppendToPath(&theme_dll_path, L"default.dll"); + + // The dll should only have resources, not executable code. + theme_dll_ = LoadLibraryEx(theme_dll_path.c_str(), NULL, + GetDataDllLoadFlags()); + DCHECK(theme_dll_ != NULL) << "unable to load " << theme_dll_path; +} + +/* static */ +SkBitmap* ResourceBundle::LoadBitmap(HINSTANCE dll_inst, int resource_id) { + void* data_ptr = NULL; + size_t data_size; + bool success = base::GetDataResourceFromModule(dll_inst, resource_id, + &data_ptr, &data_size); + if (!success) + return NULL; + + unsigned char* data = static_cast<unsigned char*>(data_ptr); + + // Decode the PNG. + vector<unsigned char> png_data; + int image_width; + int image_height; + if (!PNGDecoder::Decode(data, data_size, PNGDecoder::FORMAT_BGRA, + &png_data, &image_width, &image_height)) { + NOTREACHED() << "Unable to decode theme resource " << resource_id; + return NULL; + } + + return PNGDecoder::CreateSkBitmapFromBGRAFormat(png_data, + image_width, + image_height); +} + +SkBitmap* ResourceBundle::GetBitmapNamed(int resource_id) { + AutoLock lock_scope(lock_); + + SkImageMap::const_iterator found = skia_images_.find(resource_id); + SkBitmap* bitmap = NULL; + + // If not found load and store the image + if (found == skia_images_.end()) { + // Load the image + bitmap = LoadBitmap(theme_dll_, resource_id); + // We did not find the bitmap in the theme DLL, try the current one. + if (!bitmap) + bitmap = LoadBitmap(_AtlBaseModule.GetModuleInstance(), resource_id); + skia_images_[resource_id] = bitmap; + } else { + bitmap = found->second; + } + + // This bitmap will be returned when a bitmap is requested that can not be + // found. The data inside is lazily initialized, so users must lock and + static SkBitmap* empty_bitmap = NULL; + + // Handle the case where loading the bitmap failed. + if (!bitmap) { + LOG(WARNING) << "Unable to load bitmap with id " << resource_id; + NOTREACHED(); // Want to assert in debug mode. + if (!empty_bitmap) { + // The placeholder bitmap is bright red so people notice the problem. + empty_bitmap = new SkBitmap(); + empty_bitmap->setConfig(SkBitmap::kARGB_8888_Config, 32, 32); + empty_bitmap->allocPixels(); + empty_bitmap->eraseARGB(255, 255, 0, 0); + } + return empty_bitmap; + } + return bitmap; +} + +bool ResourceBundle::LoadImageResourceBytes(int resource_id, + vector<unsigned char>* bytes) { + return LoadModuleResourceBytes(theme_dll_, resource_id, bytes); +} + +bool ResourceBundle::LoadDataResourceBytes(int resource_id, + vector<unsigned char>* bytes) { + return LoadModuleResourceBytes(_AtlBaseModule.GetModuleInstance(), + resource_id, bytes); +} + +bool ResourceBundle::LoadModuleResourceBytes( + HINSTANCE module, + int resource_id, + std::vector<unsigned char>* bytes) { + void* data_ptr; + size_t data_size; + if (base::GetDataResourceFromModule(module, resource_id, &data_ptr, + &data_size)) { + bytes->resize(data_size); + memcpy(&(bytes->front()), data_ptr, data_size); + return true; + } else { + return false; + } +} + +HICON ResourceBundle::LoadThemeIcon(int icon_id) { + return ::LoadIcon(theme_dll_, MAKEINTRESOURCE(icon_id)); +} + +std::string ResourceBundle::GetDataResource(int resource_id) { + return GetRawDataResource(resource_id).as_string(); +} + +StringPiece ResourceBundle::GetRawDataResource(int resource_id) { + void* data_ptr; + size_t data_size; + if (base::GetDataResourceFromModule( + _AtlBaseModule.GetModuleInstance(), resource_id, &data_ptr, &data_size)) + return StringPiece(static_cast<const char*>(data_ptr), data_size); + return StringPiece(); +} + +// Loads and returns the global accelerators from the current module. +HACCEL ResourceBundle::GetGlobalAccelerators() { + return ::LoadAccelerators(_AtlBaseModule.GetModuleInstance(), + MAKEINTRESOURCE(IDR_MAINFRAME)); +} + +// Loads and returns a cursor from the current module. +HCURSOR ResourceBundle::LoadCursor(int cursor_id) { + return ::LoadCursor(_AtlBaseModule.GetModuleInstance(), + MAKEINTRESOURCE(cursor_id)); +} + +std::wstring ResourceBundle::GetLocalizedString(int message_id) { + // If for some reason we were unable to load a resource dll, return an empty + // string (better than crashing). + if (!locale_resources_dll_) + return std::wstring(); + + DCHECK(IS_INTRESOURCE(message_id)); + + // Get a reference directly to the string resource. + HINSTANCE hinstance = locale_resources_dll_; + const ATLSTRINGRESOURCEIMAGE* image = AtlGetStringResourceImage(hinstance, + message_id); + if (!image) { + // Fall back on the current module (shouldn't be any strings here except + // in unittests). + image = AtlGetStringResourceImage(_AtlBaseModule.GetModuleInstance(), + message_id); + if (!image) { + NOTREACHED() << "unable to find resource: " << message_id; + return std::wstring(); + } + } + // Copy into a wstring and return. + return std::wstring(image->achString, image->nLength); +} + +void ResourceBundle::LoadFontsIfNecessary() { + AutoLock lock_scope(lock_); + if (!base_font_.get()) { + base_font_.reset(new ChromeFont()); + + small_font_.reset(new ChromeFont()); + *small_font_ = base_font_->DeriveFont(-2); + + medium_font_.reset(new ChromeFont()); + *medium_font_ = base_font_->DeriveFont(3); + + medium_bold_font_.reset(new ChromeFont()); + *medium_bold_font_ = + base_font_->DeriveFont(3, base_font_->style() | ChromeFont::BOLD); + + large_font_.reset(new ChromeFont()); + *large_font_ = base_font_->DeriveFont(8); + + web_font_.reset(new ChromeFont()); + *web_font_ = base_font_->DeriveFont(1, + base_font_->style() | ChromeFont::WEB); + } +} + +ChromeFont ResourceBundle::GetFont(FontStyle style) { + LoadFontsIfNecessary(); + switch(style) { + case SmallFont: + return *small_font_; + case MediumFont: + return *medium_font_; + case MediumBoldFont: + return *medium_bold_font_; + case LargeFont: + return *large_font_; + case WebFont: + return *web_font_; + default: + return *base_font_; + } +} diff --git a/chrome/common/resource_bundle.h b/chrome/common/resource_bundle.h new file mode 100644 index 0000000..7d2b9d9 --- /dev/null +++ b/chrome/common/resource_bundle.h @@ -0,0 +1,183 @@ +// 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 CHROME_COMMON_RESOURCE_BUNDLE_H__ +#define CHROME_COMMON_RESOURCE_BUNDLE_H__ + +#include <windows.h> +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/scoped_ptr.h" +#include "chrome/common/ref_counted_util.h" + +class ChromeFont; +class SkBitmap; +class StringPiece; + +//////////////////////////////////////////////////////////////////////////////// +// +// ResourceBundle class +// +// ResourceBundle is a central facility to load images and other resources. +// +// Every resource is loaded only once +// +//////////////////////////////////////////////////////////////////////////////// +class ResourceBundle { + public: + // An enumeration of the various font styles used throughout Chrome. + // The following holds true for the font sizes: + // Small <= Base <= Medium <= MediumBold <= Large. + enum FontStyle { + SmallFont, + BaseFont, + MediumFont, + // NOTE: depending upon the locale, this may *not* result in a bold + // font. + MediumBoldFont, + LargeFont, + WebFont + }; + + // Initialize the ResrouceBundle for this process. + static void InitSharedInstance(const std::wstring& pref_locale); + + // Delete the ResourceBundle for this process if it exists. + static void CleanupSharedInstance(); + + // Return the global resource loader instance; + static ResourceBundle& GetSharedInstance(); + + // Load the dll that contains theme resources if present. + void LoadThemeResources(); + + // Gets the bitmap with the specified resource_id, first by looking into the + // theme dll, than in the current dll. Returns a pointer to a shared instance + // of the SkBitmap in the given out parameter. This shared bitmap is owned by + // the resource bundle and should not be freed. + // + // The bitmap is assumed to exist. This function will log in release, and + // assert in debug mode if it does not. On failure, this will return a + // pointer to a shared empty placeholder bitmap so it will be visible what + // is missing. + SkBitmap* GetBitmapNamed(int resource_id); + + // Loads the raw bytes of an image resource into |bytes|, + // without doing any processing or interpretation of + // the resource. Returns whether we successfully read the resource. + bool LoadImageResourceBytes(int resource_id, + std::vector<unsigned char>* bytes); + + // Loads the raw bytes of a data resource into |bytes|, + // without doing any processing or interpretation of + // the resource. Returns whether we successfully read the resource. + bool LoadDataResourceBytes(int resource_id, + std::vector<unsigned char>* bytes); + + // Loads and returns an icon from the theme dll. + HICON LoadThemeIcon(int icon_id); + + // Return the contents of a file in a string given the resource id. + // This will copy the data from the resource and return it as a string. + std::string GetDataResource(int resource_id); + + // Like GetDataResource(), but avoids copying the resource. Instead, it + // returns a StringPiece which points into the actual resource in the image. + StringPiece GetRawDataResource(int resource_id); + + // Loads and returns the global accelerators. + HACCEL GetGlobalAccelerators(); + + // Loads and returns a cursor from the app module. + HCURSOR LoadCursor(int cursor_id); + + // Get 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 font for the specified style. + ChromeFont GetFont(FontStyle style); + + // Creates and returns a new SkBitmap given the dll to look in and the + // resource id. It's up to the caller to free the returned bitmap when + // done. + static SkBitmap* LoadBitmap(HINSTANCE dll_inst, int resource_id); + + private: + ResourceBundle(); + ~ResourceBundle(); + + // Try to load the locale specific strings from an external DLL. + void LoadLocaleResources(const std::wstring& pref_locale); + + void LoadFontsIfNecessary(); + + // Returns the full pathname of the locale dll to load. May return an empty + // string if no locale dlls are found. + std::wstring GetLocaleDllPath(const std::wstring& pref_locale); + + // Loads the raw bytes of a resource from |module| into |bytes|, + // without doing any processing or interpretation of + // the resource. Returns whether we successfully read the resource. + bool LoadModuleResourceBytes(HINSTANCE module, + int resource_id, + std::vector<unsigned char>* bytes); + + // Class level lock. Used to protect internal data structures that may be + // accessed from other threads (e.g., skia_images_). + Lock lock_; + + std::wstring theme_path_; + + // Handle to dlls + HINSTANCE locale_resources_dll_; + HINSTANCE theme_dll_; + + // Cached images. The ResourceBundle caches all retrieved bitmaps and keeps + // ownership of the pointers. + typedef std::map<int, SkBitmap*> SkImageMap; + SkImageMap skia_images_; + + // The various fonts used. Cached to avoid repeated GDI creation/destruction. + scoped_ptr<ChromeFont> base_font_; + scoped_ptr<ChromeFont> small_font_; + scoped_ptr<ChromeFont> medium_font_; + scoped_ptr<ChromeFont> medium_bold_font_; + scoped_ptr<ChromeFont> large_font_; + scoped_ptr<ChromeFont> web_font_; + + static ResourceBundle *g_shared_instance_; + + DISALLOW_EVIL_CONSTRUCTORS(ResourceBundle); +}; + +#endif // CHROME_COMMON_RESOURCE_BUNDLE_H__ diff --git a/chrome/common/resource_dispatcher.cc b/chrome/common/resource_dispatcher.cc new file mode 100644 index 0000000..d8d1171 --- /dev/null +++ b/chrome/common/resource_dispatcher.cc @@ -0,0 +1,534 @@ +// 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. + +// See http://wiki.corp.google.com/twiki/bin/view/Main/ChromeMultiProcessResourceLoading + +#include "chrome/common/resource_dispatcher.h" + +#include "base/basictypes.h" +#include "base/shared_memory.h" +#include "base/string_util.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/security_filter_peer.h" +#include "net/base/net_errors.h" +#include "net/base/net_util.h" +#include "webkit/glue/resource_type.h" +#include "webkit/glue/webkit_glue.h" + +// Uncomment to enable logging of request traffic +//#define LOG_RESOURCE_REQUESTS + +#ifdef LOG_RESOURCE_REQUESTS +# define RESOURCE_LOG(stuff) LOG(INFO) << stuff +#else +# define RESOURCE_LOG(stuff) +#endif + +// Each resource request is assigned an ID scoped to this process. +static int MakeRequestID() { + // NOTE: The resource_dispatcher_host also needs probably unique + // request_ids, so they count down from -2 (-1 is a special we're + // screwed value), while the renderer process counts up. + static int next_request_id = 0; + return next_request_id++; +} + +// ResourceLoaderBridge implementation ---------------------------------------- + +namespace webkit_glue { + +class IPCResourceLoaderBridge : public ResourceLoaderBridge { + public: + IPCResourceLoaderBridge(ResourceDispatcher* dispatcher, + 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 resource_type, + bool mixed_content, + uint32 request_context); + virtual ~IPCResourceLoaderBridge(); + + // ResourceLoaderBridge + virtual void AppendDataToUpload(const char* data, int data_len); + virtual void AppendFileRangeToUpload(const std::wstring& path, + uint64 offset, uint64 length); + virtual bool Start(Peer* peer); + virtual void Cancel(); + virtual void SetDefersLoading(bool value); + virtual void SyncLoad(SyncLoadResponse* response); + +#ifdef LOG_RESOURCE_REQUESTS + const std::string& url() const { return url_; } +#endif + + private: + ResourceLoaderBridge::Peer* peer_; + + // The resource dispatcher for this loader. + scoped_refptr<ResourceDispatcher> dispatcher_; + + // The request to send, created on initialization for modification and + // appending data. + ViewHostMsg_Resource_Request request_; + + // ID for the request, valid once Start()ed, -1 if not valid yet. + int request_id_; + +#ifdef LOG_RESOURCE_REQUESTS + // indicates the URL of this resource request for help debugging + std::string url_; +#endif +}; + +IPCResourceLoaderBridge::IPCResourceLoaderBridge( + ResourceDispatcher* dispatcher, + 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 resource_type, + bool mixed_content, + uint32 request_context) + : peer_(NULL), + dispatcher_(dispatcher), + request_id_(-1) { + DCHECK(dispatcher_) << "no resource dispatcher"; + request_.method = method; + request_.url = url; + request_.policy_url = policy_url; + request_.referrer = referrer; + request_.headers = headers; + request_.load_flags = load_flags; + request_.origin_pid = origin_pid; + request_.resource_type = resource_type; + request_.mixed_content = mixed_content; + request_.request_context = request_context; + +#ifdef LOG_RESOURCE_REQUESTS + url_ = url.possibly_invalid_spec(); +#endif +} + +IPCResourceLoaderBridge::~IPCResourceLoaderBridge() { + // we remove our hook for the resource dispatcher only when going away, since + // it doesn't keep track of whether we've force terminated the request + if (request_id_ >= 0) { + // this operation may fail, as the dispatcher will have preemptively + // removed us when the renderer sends the ReceivedAllData message. + dispatcher_->RemovePendingRequest(request_id_); + } +} + +void IPCResourceLoaderBridge::AppendDataToUpload(const char* data, + int data_len) { + DCHECK(request_id_ == -1) << "request already started"; + + // don't bother appending empty data segments + if (data_len == 0) + return; + + request_.upload_content.push_back(net::UploadData::Element()); + request_.upload_content.back().SetToBytes(data, data_len); +} + +void IPCResourceLoaderBridge::AppendFileRangeToUpload( + const std::wstring& path, uint64 offset, uint64 length) { + DCHECK(request_id_ == -1) << "request already started"; + + request_.upload_content.push_back(net::UploadData::Element()); + request_.upload_content.back().SetToFilePathRange(path, offset, length); +} + +// Writes a footer on the message and sends it +bool IPCResourceLoaderBridge::Start(Peer* peer) { + if (request_id_ != -1) { + NOTREACHED() << "Starting a request twice"; + return false; + } + + RESOURCE_LOG("Starting request for " << url_); + + peer_ = peer; + + // generate the request ID, and append it to the message + request_id_ = dispatcher_->AddPendingRequest(peer_, request_.resource_type, + request_.mixed_content); + + IPC::Message::Sender* sender = dispatcher_->message_sender(); + bool ret = false; + if (sender) + ret = sender->Send(new ViewHostMsg_RequestResource(MSG_ROUTING_NONE, + request_id_, + request_)); + return ret; +} + +void IPCResourceLoaderBridge::Cancel() { + if (request_id_ < 0) { + NOTREACHED() << "Trying to cancel an unstarted request"; + return; + } + + RESOURCE_LOG("Canceling request for " << url_); + + IPC::Message::Sender* sender = dispatcher_->message_sender(); + if (sender) + sender->Send(new ViewHostMsg_CancelRequest(MSG_ROUTING_NONE, request_id_)); + + // We can't remove the request ID from the resource dispatcher because more + // data might be pending. Sending the cancel message may cause more data + // to be flushed, and will then cause a complete message to be sent. +} + +void IPCResourceLoaderBridge::SetDefersLoading(bool value) { + if (request_id_ < 0) { + NOTREACHED() << "Trying to (un)defer an unstarted request"; + return; + } + + dispatcher_->SetDefersLoading(request_id_, value); +} + +void IPCResourceLoaderBridge::SyncLoad(SyncLoadResponse* response) { + if (request_id_ != -1) { + NOTREACHED() << "Starting a request twice"; + response->status.set_status(URLRequestStatus::FAILED); + return; + } + + RESOURCE_LOG("Making sync request for " << url_); + + request_id_ = MakeRequestID(); + + ViewHostMsg_SyncLoad_Result result; + IPC::Message::Sender* sender = dispatcher_->message_sender(); + + if (sender) { + IPC::Message* msg = new ViewHostMsg_SyncLoad(MSG_ROUTING_NONE, request_id_, + request_, &result); + if (!sender->Send(msg)) { + response->status.set_status(URLRequestStatus::FAILED); + return; + } + } + + response->status = result.status; + response->url = result.final_url; + response->headers = result.headers; + response->mime_type = result.mime_type; + response->charset = result.charset; + response->data.swap(result.data); +} + +} // namespace webkit_glue + +// ResourceDispatcher --------------------------------------------------------- + +ResourceDispatcher::ResourceDispatcher(IPC::Message::Sender* sender) + : message_sender_(sender), +#pragma warning(suppress: 4355) + method_factory_(this) { +} + +ResourceDispatcher::~ResourceDispatcher() { +} + +// ResourceDispatcher implementation ------------------------------------------ + +bool ResourceDispatcher::OnMessageReceived(const IPC::Message& message) { + switch (message.type()) { + case ViewMsg_Resource_UploadProgress::ID: + case ViewMsg_Resource_ReceivedResponse::ID: + case ViewMsg_Resource_ReceivedRedirect::ID: + case ViewMsg_Resource_DataReceived::ID: + case ViewMsg_Resource_RequestComplete::ID: + break; + default: + return false; + } + + int request_id; + + void* iter = NULL; + if (!message.ReadInt(&iter, &request_id)) { + NOTREACHED() << "malformed resource message"; + return true; + } + + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + // This might happen for kill()ed requests on the webkit end, so perhaps it + // shouldn't be a warning... + DLOG(WARNING) << "Got response for a nonexistant or finished request"; + return true; + } + + PendingRequestInfo& request_info = it->second; + if (request_info.is_deferred) { + request_info.deferred_message_queue.push_back(new IPC::Message(message)); + return true; + } + // Make sure any deferred messages are dispatched before we dispatch more. + if (!request_info.deferred_message_queue.empty()) + FlushDeferredMessages(request_id); + + DispatchMessage(message); + return true; +} + +void ResourceDispatcher::OnUploadProgress( + int request_id, int64 position, int64 size) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + // this might happen for kill()ed requests on the webkit end, so perhaps + // it shouldn't be a warning... + DLOG(WARNING) << "Got upload progress for a nonexistant or " + "finished request"; + return; + } + + PendingRequestInfo& request_info = it->second; + + RESOURCE_LOG("Dispatching upload progress for " << + request_info.peer->GetURLForDebugging()); + request_info.peer->OnUploadProgress(position, size); + + // Acknowlegde reciept + IPC::Message::Sender* sender = message_sender(); + if (sender) + sender->Send( + new ViewHostMsg_UploadProgress_ACK(MSG_ROUTING_NONE, request_id)); +} + +void ResourceDispatcher::OnReceivedResponse( + int request_id, + const ViewMsg_Resource_ResponseHead& response_head) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + // This might happen for kill()ed requests on the webkit end, so perhaps it + // shouldn't be a warning... + DLOG(WARNING) << "Got response for a nonexistant or finished request"; + return; + } + + PendingRequestInfo& request_info = it->second; + request_info.filter_policy = response_head.filter_policy; + webkit_glue::ResourceLoaderBridge::Peer* peer = request_info.peer; + if (request_info.filter_policy != FilterPolicy::DONT_FILTER) { + // TODO(jcampan): really pass the loader bridge. + webkit_glue::ResourceLoaderBridge::Peer* new_peer = + SecurityFilterPeer::CreateSecurityFilterPeer( + NULL, peer, + request_info.resource_type, response_head.mime_type, + request_info.filter_policy, + net::ERR_INSECURE_RESPONSE); + if (new_peer) { + request_info.peer = new_peer; + peer = new_peer; + } + } + + RESOURCE_LOG("Dispatching response for " << peer->GetURLForDebugging()); + peer->OnReceivedResponse(response_head); +} + +void ResourceDispatcher::OnReceivedData(int request_id, + SharedMemoryHandle shm_handle, + int data_len) { + // Acknowlegde the reception of this data. + IPC::Message::Sender* sender = message_sender(); + if (sender) + sender->Send( + new ViewHostMsg_DataReceived_ACK(MSG_ROUTING_NONE, request_id)); + + DCHECK((shm_handle && data_len > 0) || (!shm_handle && !data_len)); + SharedMemory shared_mem(shm_handle, true); // read only + + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + // this might happen for kill()ed requests on the webkit end, so perhaps + // it shouldn't be a warning... + DLOG(WARNING) << "Got data for a nonexistant or finished request"; + return; + } + + PendingRequestInfo& request_info = it->second; + + if (data_len > 0 && shared_mem.Map(data_len)) { + RESOURCE_LOG("Dispatching " << data_len << " bytes for " << + request_info.peer->GetURLForDebugging()); + const char* data = static_cast<char*>(shared_mem.memory()); + request_info.peer->OnReceivedData(data, data_len); + } +} + +void ResourceDispatcher::OnReceivedRedirect(int request_id, + const GURL& new_url) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + // this might happen for kill()ed requests on the webkit end, so perhaps + // it shouldn't be a warning... + DLOG(WARNING) << "Got data for a nonexistant or finished request"; + return; + } + + PendingRequestInfo& request_info = it->second; + + RESOURCE_LOG("Dispatching redirect for " << + request_info.peer->GetURLForDebugging()); + request_info.peer->OnReceivedRedirect(new_url); +} + +void ResourceDispatcher::OnRequestComplete(int request_id, + const URLRequestStatus& status) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + // this might happen for kill()ed requests on the webkit end, so perhaps + // it shouldn't be a warning... + DLOG(WARNING) << "Got 'complete' for a nonexistant or finished request"; + return; + } + + PendingRequestInfo& request_info = it->second; + webkit_glue::ResourceLoaderBridge::Peer* peer = request_info.peer; + + RESOURCE_LOG("Dispatching complete for " << + request_info.peer->GetURLForDebugging()); + + if (status.status() == URLRequestStatus::CANCELED && + status.os_error() != net::ERR_ABORTED) { + // Resource canceled with a specific error are filtered. + SecurityFilterPeer* new_peer = + SecurityFilterPeer::CreateSecurityFilterPeerForDeniedRequest( + request_info.resource_type, + request_info.peer, + status.os_error()); + if (new_peer) { + request_info.peer = new_peer; + peer = new_peer; + } + } + + // The request ID will be removed from our pending list in the destructor. + // Normally, dispatching this message causes the reference-counted request to + // die immediately. + peer->OnCompletedRequest(status); + + webkit_glue::NotifyCacheStats(); +} + +int ResourceDispatcher::AddPendingRequest( + webkit_glue::ResourceLoaderBridge::Peer* callback, + ResourceType::Type resource_type, + bool mixed_content) { + // Compute a unique request_id for this renderer process. + int id = MakeRequestID(); + pending_requests_[id] = PendingRequestInfo(callback, resource_type, + mixed_content); + return id; +} + +bool ResourceDispatcher::RemovePendingRequest(int request_id) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) + return false; + pending_requests_.erase(it); + return true; +} + +void ResourceDispatcher::SetDefersLoading(int request_id, bool value) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) { + NOTREACHED() << "unknown request"; + return; + } + PendingRequestInfo& request_info = it->second; + if (value) { + request_info.is_deferred = value; + } else if (request_info.is_deferred) { + request_info.is_deferred = false; + MessageLoop::current()->PostTask(FROM_HERE, + method_factory_.NewRunnableMethod( + &ResourceDispatcher::FlushDeferredMessages, request_id)); + } +} + +void ResourceDispatcher::DispatchMessage(const IPC::Message& message) { + IPC_BEGIN_MESSAGE_MAP(ResourceDispatcher, message) + IPC_MESSAGE_HANDLER(ViewMsg_Resource_UploadProgress, OnUploadProgress) + IPC_MESSAGE_HANDLER(ViewMsg_Resource_ReceivedResponse, OnReceivedResponse) + IPC_MESSAGE_HANDLER(ViewMsg_Resource_ReceivedRedirect, OnReceivedRedirect) + IPC_MESSAGE_HANDLER(ViewMsg_Resource_DataReceived, OnReceivedData) + IPC_MESSAGE_HANDLER(ViewMsg_Resource_RequestComplete, OnRequestComplete) + IPC_END_MESSAGE_MAP() +} + +void ResourceDispatcher::FlushDeferredMessages(int request_id) { + PendingRequestList::iterator it = pending_requests_.find(request_id); + if (it == pending_requests_.end()) // The request could have become invalid. + return; + PendingRequestInfo& request_info = it->second; + if (request_info.is_deferred) + return; + // Because message handlers could result in request_info being destroyed, + // we need to work with a stack reference to the deferred queue. + MessageQueue q; + q.swap(request_info.deferred_message_queue); + while (!q.empty()) { + IPC::Message* m = q.front(); + q.pop_front(); + DispatchMessage(*m); + delete m; + } +} + +webkit_glue::ResourceLoaderBridge* ResourceDispatcher::CreateBridge( + const std::string& method, + const GURL& url, + const GURL& policy_url, + const GURL& referrer, + const std::string& headers, + int flags, + int origin_pid, + ResourceType::Type resource_type, + bool mixed_content, + uint32 request_context) { + return new webkit_glue::IPCResourceLoaderBridge(this, method, url, policy_url, + referrer, headers, flags, + origin_pid, resource_type, + mixed_content, + request_context); +} diff --git a/chrome/common/resource_dispatcher.h b/chrome/common/resource_dispatcher.h new file mode 100644 index 0000000..462e717 --- /dev/null +++ b/chrome/common/resource_dispatcher.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. + +// See http://wiki.corp.google.com/twiki/bin/view/Main/ChromeMultiProcessResourceLoading + +#ifndef CHROME_COMMON_RESOURCE_DISPATCHER_H__ +#define CHROME_COMMON_RESOURCE_DISPATCHER_H__ + +#include <windows.h> +#include <hash_map> + +#include "base/task.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/render_messages.h" +#include "webkit/glue/resource_loader_bridge.h" + +// Uncomment this to disable loading resources via the parent process. This +// may be useful for debugging purposes. +//#define USING_SIMPLE_RESOURCE_LOADER_BRIDGE + +// This class serves as a communication interface between the +// ResourceDispatcherHost in the browser process and the ResourceLoaderBridge in +// the child process. It can be used from either the renderer or plugin +// processes. +class ResourceDispatcher : public base::RefCounted<ResourceDispatcher> { + public: + ResourceDispatcher(IPC::Message::Sender* sender); + ~ResourceDispatcher(); + + // Called to possibly handle the incoming IPC message. Returns true if + // handled, else false. + bool OnMessageReceived(const IPC::Message& message); + + // creates a ResourceLoaderBridge for this type of dispatcher, this is so + // this can be tested regardless of the ResourceLoaderBridge::Create + // implementation. + webkit_glue::ResourceLoaderBridge* CreateBridge(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 resource_type, + bool mixed_content, + uint32 request_context /* used for plugin->browser requests */); + + // Adds a request from the pending_requests_ list, returning the new + // requests' ID + int AddPendingRequest(webkit_glue::ResourceLoaderBridge::Peer* callback, + ResourceType::Type resource_type, + bool mixed_content); + + // Removes a request from the pending_requests_ list, returning true if the + // request was found and removed. + bool RemovePendingRequest(int request_id); + + IPC::Message::Sender* message_sender() const { + return message_sender_; + } + + // Toggles the is_deferred attribute for the specified request. + void SetDefersLoading(int request_id, bool value); + + // We can no longer use message sender + void ClearMessageSender() { + message_sender_ = NULL; + } + + private: + friend class ResourceDispatcherTest; + + typedef std::deque<IPC::Message*> MessageQueue; + struct PendingRequestInfo { + PendingRequestInfo() { } + PendingRequestInfo(webkit_glue::ResourceLoaderBridge::Peer* peer, + ResourceType::Type resource_type, + bool mixed_content) + : peer(peer), + resource_type(resource_type), + filter_policy(FilterPolicy::DONT_FILTER), + mixed_content(mixed_content), + is_deferred(false) { + } + ~PendingRequestInfo() { } + webkit_glue::ResourceLoaderBridge::Peer* peer; + ResourceType::Type resource_type; + FilterPolicy::Type filter_policy; + MessageQueue deferred_message_queue; + bool mixed_content; + bool is_deferred; + }; + typedef stdext::hash_map<int,PendingRequestInfo> PendingRequestList; + + // Message response handlers, called by the message handler for this process. + void OnUploadProgress(int request_id, int64 position, int64 size); + void OnReceivedResponse(int request_id, const ViewMsg_Resource_ResponseHead&); + void OnReceivedRedirect(int request_id, const GURL& new_url); + void OnReceivedData(int request_id, SharedMemoryHandle data, int data_len); + void OnRequestComplete(int request_id, const URLRequestStatus& status); + + // Dispatch the message to one of the message response handlers. + void DispatchMessage(const IPC::Message& message); + + // Dispatch any deferred messages for the given request, provided it is not + // again in the deferred state. + void FlushDeferredMessages(int request_id); + + IPC::Message::Sender* message_sender_; + + // All pending requests issued to the host + PendingRequestList pending_requests_; + + ScopedRunnableMethodFactory<ResourceDispatcher> method_factory_; + + DISALLOW_EVIL_CONSTRUCTORS(ResourceDispatcher); +}; + +#endif // CHROME_COMMON_RESOURCE_DISPATCHER_H__ diff --git a/chrome/common/resource_dispatcher_unittest.cc b/chrome/common/resource_dispatcher_unittest.cc new file mode 100644 index 0000000..0827b23 --- /dev/null +++ b/chrome/common/resource_dispatcher_unittest.cc @@ -0,0 +1,208 @@ +// 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 <vector> + +#include "base/ref_counted.h" +#include "chrome/common/filter_policy.h" +#include "chrome/common/resource_dispatcher.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using webkit_glue::ResourceLoaderBridge; + +static const char test_page_url[] = "http://www.google.com/"; +static const char test_page_headers[] = + "HTTP/1.1 200 OK\nContent-Type:text/html\n\n"; +static const char test_page_mime_type[] = "text/html"; +static const char test_page_charset[] = ""; +static const char test_page_contents[] = + "<html><head><title>Google</title></head><body><h1>Google</h1></body></html>"; +static const int test_page_contents_len = arraysize(test_page_contents) - 1; + +// Listens for request response data and stores it so that it can be compared +// to the reference data. +class TestRequestCallback : public ResourceLoaderBridge::Peer { + public: + TestRequestCallback() : complete_(false) { + } + + virtual void OnReceivedRedirect(const GURL& new_url) { + } + + virtual void OnReceivedResponse( + const ResourceLoaderBridge::ResponseInfo& info) { + } + + virtual void OnReceivedData(const char* data, int len) { + EXPECT_FALSE(complete_); + data_.append(data, len); + } + + virtual void OnCompletedRequest(const URLRequestStatus& status) { + EXPECT_FALSE(complete_); + complete_ = true; + } + + virtual std::string GetURLForDebugging() { + return std::string(); + } + + const std::string& data() const { + return data_; + } + const bool complete() const { + return complete_; + } + + private: + bool complete_; + std::string data_; +}; + + +// Sets up the message sender override for the unit test +class ResourceDispatcherTest : public testing::Test, + public IPC::Message::Sender { + public: + // Emulates IPC send operations (IPC::Message::Sender) by adding + // pending messages to the queue. + virtual bool Send(IPC::Message* msg) { + message_queue_.push_back(IPC::Message(*msg)); + delete msg; + return true; + } + + // Emulates the browser process and processes the pending IPC messages, + // returning the hardcoded file contents. + void ProcessMessages() { + while (!message_queue_.empty()) { + void* iter = NULL; + + int request_id; + ASSERT_TRUE(IPC::ReadParam(&message_queue_[0], &iter, &request_id)); + + ViewHostMsg_Resource_Request request; + ASSERT_TRUE(IPC::ReadParam(&message_queue_[0], &iter, &request)); + + // check values + EXPECT_EQ(test_page_url, request.url.spec()); + + // received response message + ViewMsg_Resource_ResponseHead response; + std::string raw_headers(test_page_headers); + std::replace(raw_headers.begin(), raw_headers.end(), '\n', '\0'); + response.headers = new net::HttpResponseHeaders(raw_headers); + response.mime_type = test_page_mime_type; + response.charset = test_page_charset; + response.filter_policy = FilterPolicy::DONT_FILTER; + dispatcher_->OnReceivedResponse(request_id, response); + + // received data message with the test contents + SharedMemory shared_mem; + EXPECT_TRUE(shared_mem.Create(std::wstring(), + false, false, test_page_contents_len)); + EXPECT_TRUE(shared_mem.Map(test_page_contents_len)); + char* put_data_here = static_cast<char*>(shared_mem.memory()); + memcpy(put_data_here, test_page_contents, test_page_contents_len); + SharedMemoryHandle dup_handle; + EXPECT_TRUE(shared_mem.GiveToProcess(GetCurrentProcess(), &dup_handle)); + dispatcher_->OnReceivedData(request_id, dup_handle, test_page_contents_len); + + message_queue_.erase(message_queue_.begin()); + + // read the ack message. + iter = NULL; + int request_ack = -1; + ASSERT_TRUE(IPC::ReadParam(&message_queue_[0], &iter, &request_ack)); + + ASSERT_EQ(request_ack, request_id); + + message_queue_.erase(message_queue_.begin()); + } + } + + protected: + static ResourceDispatcher* GetResourceDispatcher(WebFrame* unused) { + return dispatcher_; + } + + // testing::Test + virtual void SetUp() { + dispatcher_ = new ResourceDispatcher(this); + } + virtual void TearDown() { + dispatcher_ = NULL; + } + + std::vector<IPC::Message> message_queue_; + static scoped_refptr<ResourceDispatcher> dispatcher_; +}; + +/*static*/ +scoped_refptr<ResourceDispatcher> ResourceDispatcherTest::dispatcher_; + +// Does a simple request and tests that the correct data is received. +TEST_F(ResourceDispatcherTest, RoundTrip) { + TestRequestCallback callback; + ResourceLoaderBridge* bridge = + dispatcher_->CreateBridge("GET", GURL(test_page_url), GURL(test_page_url), + GURL(), std::string(), 0, 0, + ResourceType::SUB_RESOURCE, false, 0); + + bridge->Start(&callback); + + ProcessMessages(); + + // FIXME(brettw) when the request complete messages are actually handledo + // and dispatched, uncomment this. + //EXPECT_TRUE(callback.complete()); + //EXPECT_STREQ(test_page_contents, callback.data().c_str()); + + delete bridge; +} + +// Tests that the request IDs are straight when there are multiple requests. +TEST_F(ResourceDispatcherTest, MultipleRequests) { + // FIXME +} + +// Tests that the cancel method prevents other messages from being received +TEST_F(ResourceDispatcherTest, Cancel) { + // FIXME +} + +TEST_F(ResourceDispatcherTest, Cookies) { + // FIXME +} + +TEST_F(ResourceDispatcherTest, SerializedPostData) { + // FIXME +} diff --git a/chrome/common/scoped_vector.h b/chrome/common/scoped_vector.h new file mode 100644 index 0000000..befb629 --- /dev/null +++ b/chrome/common/scoped_vector.h @@ -0,0 +1,76 @@ +// 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 CHROME_BROWSER_COMMON_SCOPED_VECTOR_H__ +#define CHROME_BROWSER_COMMON_SCOPED_VECTOR_H__ + +#include <vector> + +#include "chrome/common/stl_util-inl.h" + +// ScopedVector wraps a vector deleting the elements from its +// destructor. +template <class T> +class ScopedVector { + public: + typedef typename std::vector<T*>::iterator iterator; + typedef typename std::vector<T*>::const_iterator const_iterator; + + ScopedVector() {} + ~ScopedVector() { reset(); } + + std::vector<T*>* operator->() { return &v; } + const std::vector<T*>* operator->() const { return &v; } + T* operator[](size_t i) { return v[i]; } + const T* operator[](size_t i) const { return v[i]; } + + bool empty() const { return v.empty(); } + size_t size() const { return v.size(); } + + iterator begin() { return v.begin(); } + const_iterator begin() const { return v.begin(); } + iterator end() { return v.end(); } + const_iterator end() const { return v.end(); } + + void push_back(T* elem) { v.push_back(elem); } + + std::vector<T*>& get() { return v; } + const std::vector<T*>& get() const { return v; } + void swap(ScopedVector<T>& other) { v.swap(other.v); } + void release(std::vector<T*>* out) { out->swap(v); v.clear(); } + + void reset() { STLDeleteElements(&v); } + + private: + std::vector<T*> v; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedVector); +}; + +#endif // CHROME_BROWSER_COMMON_SCOPED_VECTOR_H__ diff --git a/chrome/common/security_filter_peer.cc b/chrome/common/security_filter_peer.cc new file mode 100644 index 0000000..66ebbb1 --- /dev/null +++ b/chrome/common/security_filter_peer.cc @@ -0,0 +1,339 @@ +// 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 "chrome/common/security_filter_peer.h" + +#include "base/gfx/png_encoder.h" +#include "base/gfx/size.h" +#include "base/string_util.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/renderer/renderer_resources.h" +#include "generated_resources.h" +#include "skia/include/SkBitmap.h" +#include "skia/include/SkCanvas.h" +#include "webkit/glue/webkit_glue.h" +#include "SkDevice.h" + +SecurityFilterPeer::SecurityFilterPeer( + webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer) + : original_peer_(peer), + resource_loader_bridge_(resource_loader_bridge) { +} + +SecurityFilterPeer::~SecurityFilterPeer() { +} + +// static +SecurityFilterPeer* SecurityFilterPeer::CreateSecurityFilterPeer( + webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer, + ResourceType::Type resource_type, + const std::string& mime_type, + FilterPolicy::Type filter_policy, + int os_error) { + if (filter_policy == FilterPolicy::DONT_FILTER) { + NOTREACHED(); + return NULL; + } + + if (StartsWithASCII(mime_type, "image/", false)) { + // What we do with images depends on details of the |filter_policy|. + if (filter_policy == FilterPolicy::FILTER_ALL_EXCEPT_IMAGES) + return new ImageFilterPeer(resource_loader_bridge, peer); + // Otherwise, fall through to blocking images hard. + } + + // Regardless of what a frame contents replace it with our error message, + // so it is visible it's been filtered-out. + if (ResourceType::IsFrame(resource_type)) + return CreateSecurityFilterPeerForFrame(peer, os_error); + + // Any other content is entirely filtered-out. + return new ReplaceContentPeer(resource_loader_bridge, peer, + std::string(), std::string()); +} + +// static +SecurityFilterPeer* + SecurityFilterPeer::CreateSecurityFilterPeerForDeniedRequest( + ResourceType::Type resource_type, + webkit_glue::ResourceLoaderBridge::Peer* peer, + int os_error) { + // Create a filter for SSL and CERT errors. + switch (os_error) { + case net::ERR_SSL_PROTOCOL_ERROR: + case net::ERR_CERT_COMMON_NAME_INVALID: + case net::ERR_CERT_DATE_INVALID: + case net::ERR_CERT_AUTHORITY_INVALID: + case net::ERR_CERT_CONTAINS_ERRORS: + case net::ERR_CERT_NO_REVOCATION_MECHANISM: + case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION: + case net::ERR_CERT_REVOKED: + case net::ERR_CERT_INVALID: + case net::ERR_INSECURE_RESPONSE: + if (ResourceType::IsFrame(resource_type)) + return CreateSecurityFilterPeerForFrame(peer, os_error); + // Any other content is entirely filtered-out. + return new ReplaceContentPeer(NULL, peer, std::string(), std::string()); + default: + // For other errors, we use our normal error handling. + return NULL; + } +} + +// static +SecurityFilterPeer* SecurityFilterPeer::CreateSecurityFilterPeerForFrame( + webkit_glue::ResourceLoaderBridge::Peer* peer, int os_error) { + // TODO(jcampan): use a different message when getting a phishing/malware + // error. + std::wstring error_msg = l10n_util::GetString(IDS_UNSAFE_FRAME_MESSAGE); + std::string html = StringPrintf( + "<html><body style='background-color:#990000;color:white;'>" + "%s</body></html>", + WideToUTF8(error_msg).c_str()); + return new ReplaceContentPeer(NULL, peer, "text/html", html); +} + +void SecurityFilterPeer::OnReceivedRedirect(const GURL& new_url) { + NOTREACHED(); +} + +void SecurityFilterPeer::OnReceivedResponse( + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info) { + NOTREACHED(); +} + +void SecurityFilterPeer::OnReceivedData(const char* data, int len) { + NOTREACHED(); +} + +void SecurityFilterPeer::OnCompletedRequest(const URLRequestStatus& status) { + NOTREACHED(); +} + +std::string SecurityFilterPeer::GetURLForDebugging() { + return original_peer_->GetURLForDebugging(); +} + +// static +void ProcessResponseInfo( + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info_in, + webkit_glue::ResourceLoaderBridge::ResponseInfo* info_out, + const std::string& mime_type) { + DCHECK(info_out); + *info_out = info_in; + info_out->mime_type = mime_type; + // Let's create our own HTTP headers. + std::string raw_headers; + raw_headers.append("HTTP/1.1 200 OK"); + raw_headers.push_back('\0'); + // Don't cache the data we are serving, it is not the real data for that URL + // (if the filtered resource were to make it into the WebCore cache, then the + // same URL loaded in a safe scenario would still return the filtered + // resource). + raw_headers.append("cache-control: no-cache"); + raw_headers.push_back('\0'); + if (!mime_type.empty()) { + raw_headers.append("content-type: "); + raw_headers.append(mime_type); + raw_headers.push_back('\0'); + } + raw_headers.push_back('\0'); + net::HttpResponseHeaders* new_headers = + new net::HttpResponseHeaders(raw_headers); + info_out->headers = new_headers; +} + +//////////////////////////////////////////////////////////////////////////////// +// BufferedPeer + +BufferedPeer::BufferedPeer( + webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer, + const std::string& mime_type) + : SecurityFilterPeer(resource_loader_bridge, peer), + mime_type_(mime_type) { +} + +BufferedPeer::~BufferedPeer() { +} + +void BufferedPeer::OnReceivedResponse( + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info) { + ProcessResponseInfo(info, &response_info_, mime_type_); +} + +void BufferedPeer::OnReceivedData(const char* data, int len) { + data_.append(data, len); +} + +void BufferedPeer::OnCompletedRequest(const URLRequestStatus& status) { + // Make sure we delete ourselves at the end of this call. + scoped_ptr<BufferedPeer> this_deleter(this); + + // Give sub-classes a chance at altering the data. + if (status.status() != URLRequestStatus::SUCCESS || !DataReady()) { + // Pretend we failed to load the resource. + original_peer_->OnReceivedResponse(response_info_); + URLRequestStatus status(URLRequestStatus::CANCELED, 0); + original_peer_->OnCompletedRequest(status); + return; + } + + original_peer_->OnReceivedResponse(response_info_); + if (!data_.empty()) + original_peer_->OnReceivedData(data_.data(), + static_cast<int>(data_.size())); + original_peer_->OnCompletedRequest(status); +} + +//////////////////////////////////////////////////////////////////////////////// +// ReplaceContentPeer + +ReplaceContentPeer::ReplaceContentPeer( + webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer, + const std::string& mime_type, + const std::string& data) + : SecurityFilterPeer(resource_loader_bridge, peer), + mime_type_(mime_type), + data_(data) { +} + +ReplaceContentPeer::~ReplaceContentPeer() { +} + +void ReplaceContentPeer::OnReceivedResponse( + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info) { + // Ignore this, we'll serve some alternate content in OnCompletedRequest. +} + +void ReplaceContentPeer::OnReceivedData(const char* data, int len) { + // Ignore this, we'll serve some alternate content in OnCompletedRequest. +} + +void ReplaceContentPeer::OnCompletedRequest(const URLRequestStatus& status) { + webkit_glue::ResourceLoaderBridge::ResponseInfo info; + ProcessResponseInfo(info, &info, mime_type_); + info.content_length = static_cast<int>(data_.size()); + original_peer_->OnReceivedResponse(info); + if (!data_.empty()) + original_peer_->OnReceivedData(data_.data(), + static_cast<int>(data_.size())); + original_peer_->OnCompletedRequest(URLRequestStatus()); + + // The request processing is complete, we must delete ourselves. + delete this; +} + +//////////////////////////////////////////////////////////////////////////////// +// ImageFilterPeer + +ImageFilterPeer::ImageFilterPeer( + webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer) + : BufferedPeer(resource_loader_bridge, peer, "image/png") { +} + +ImageFilterPeer::~ImageFilterPeer() { +} + +bool ImageFilterPeer::DataReady() { + static SkBitmap* stamp_bitmap = NULL; + if (!stamp_bitmap) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + stamp_bitmap = rb.GetBitmapNamed(IDR_INSECURE_CONTENT_STAMP); + if (!stamp_bitmap) { + NOTREACHED(); + return false; + } + } + + // Let's decode the image we have been given. + SkBitmap image; + if (!webkit_glue::DecodeImage(data_, &image)) + return false; // We could not decode that image. + + // Let's create a new canvas to modify the image. + SkBitmap canvas_bitmap; + canvas_bitmap.setConfig(SkBitmap::kARGB_8888_Config, + image.width(), image.height()); + canvas_bitmap.allocPixels(); + SkCanvas canvas(canvas_bitmap); + + // Let's paint the actual image with an alpha. + SkRect rect; + rect.fLeft = 0; + rect.fTop = 0; + rect.fRight = SkIntToScalar(image.width()); + rect.fBottom = SkIntToScalar(image.height()); + + SkPaint paint; + paint.setAlpha(80); + canvas.drawBitmapRect(image, NULL, rect, &paint); + + // Then stamp-it. + paint.setAlpha(150); + int visible_row_count = image.height() / stamp_bitmap->height(); + if (image.height() % stamp_bitmap->height() != 0) + visible_row_count++; + + for (int row = 0; row < visible_row_count; row++) { + for (int x = (row % 2 == 0) ? 0 : stamp_bitmap->width(); x < image.width(); + x += stamp_bitmap->width() * 2) { + canvas.drawBitmap(*stamp_bitmap, + SkIntToScalar(x), + SkIntToScalar(row * stamp_bitmap->height()), + &paint); + } + } + + // Now encode it to a PNG. + std::vector<unsigned char> output; + unsigned char* input = + static_cast<unsigned char*>(canvas.getDevice()->accessBitmap(false). + getPixels()); + if (!PNGEncoder::Encode(input, + PNGEncoder::FORMAT_BGRA, + image.width(), image.height(), + image.width() * 4, false, &output)) { + return false; + } + + // Copy the vector content to data_ which is a string. + data_.clear(); + data_.resize(output.size()); + std::copy(output.begin(), output.end(), data_.begin()); + + return true; +} diff --git a/chrome/common/security_filter_peer.h b/chrome/common/security_filter_peer.h new file mode 100644 index 0000000..e9dcb0e --- /dev/null +++ b/chrome/common/security_filter_peer.h @@ -0,0 +1,157 @@ +// 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 CHROME_COMMON_SECURITY_FILTER_PEER_H__ +#define CHROME_COMMON_SECURITY_FILTER_PEER_H__ + +#include "chrome/common/render_messages.h" +#include "webkit/glue/resource_loader_bridge.h" + +// The SecurityFilterPeer is a proxy to a +// webkit_glue::ResourceLoaderBridge::Peer instance. It is used to pre-process +// unsafe resources (such as mixed-content resource). +// Call the factory method CreateSecurityFilterPeer() to obtain an instance of +// SecurityFilterPeer based on the original Peer. +// NOTE: subclasses should insure they delete themselves at the end of the +// OnReceiveComplete call. +class SecurityFilterPeer : public webkit_glue::ResourceLoaderBridge::Peer { + public: + virtual ~SecurityFilterPeer(); + + static SecurityFilterPeer* CreateSecurityFilterPeer( + webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer, + ResourceType::Type resource_type, + const std::string& mime_type, + FilterPolicy::Type filter_policy, + int os_error); + + static SecurityFilterPeer* CreateSecurityFilterPeerForDeniedRequest( + ResourceType::Type resource_type, + webkit_glue::ResourceLoaderBridge::Peer* peer, + int os_error); + + static SecurityFilterPeer* CreateSecurityFilterPeerForFrame( + webkit_glue::ResourceLoaderBridge::Peer* peer, + int os_error); + + // ResourceLoaderBridge::Peer methods. + virtual void OnReceivedRedirect(const GURL& new_url); + virtual void OnReceivedResponse( + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info); + virtual void OnReceivedData(const char* data, int len); + virtual void OnCompletedRequest(const URLRequestStatus& status); + virtual std::string GetURLForDebugging(); + + protected: + SecurityFilterPeer(webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer); + + webkit_glue::ResourceLoaderBridge* resource_loader_bridge_; + webkit_glue::ResourceLoaderBridge::Peer* original_peer_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(SecurityFilterPeer); +}; + +// The BufferedPeer reads all the data of the request into an internal buffer. +// Subclasses should implement DataReady() to process the data as necessary. +class BufferedPeer : public SecurityFilterPeer { + public: + BufferedPeer(webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer, + const std::string& mime_type); + virtual ~BufferedPeer(); + + // ResourceLoaderBridge::Peer Implementation. + virtual void OnReceivedResponse( + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info); + virtual void OnReceivedData(const char* data, int len); + virtual void OnCompletedRequest(const URLRequestStatus& status); + + protected: + // Invoked when the entire request has been processed before the data is sent + // to the original peer, giving an opportunity to subclasses to process the + // data in data_. If this method returns true, the data is fed to the + // original peer, if it returns false, an error is sent instead. + virtual bool DataReady() = 0; + + webkit_glue::ResourceLoaderBridge::ResponseInfo response_info_; + std::string data_; + + private: + std::string mime_type_; + DISALLOW_EVIL_CONSTRUCTORS(BufferedPeer); +}; + +// The ReplaceContentPeer cancels the request and serves the provided data as +// content instead. +// TODO (jcampan): we do not as of now cancel the request, as we do not have +// access to the resource_loader_bridge in the SecurityFilterPeer factory +// method. For now the resource is still being fetched, but ignored, as once +// we have provided the replacement content, the associated pending request +// in ResourceDispatcher is removed and further OnReceived* notifications are +// ignored. +class ReplaceContentPeer : public SecurityFilterPeer { + public: + ReplaceContentPeer(webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer, + const std::string& mime_type, + const std::string& data); + virtual ~ReplaceContentPeer(); + + // ResourceLoaderBridge::Peer Implementation. + virtual void OnReceivedResponse( + const webkit_glue::ResourceLoaderBridge::ResponseInfo& info); + void OnReceivedData(const char* data, int len); + void OnCompletedRequest(const URLRequestStatus& status); + private: + webkit_glue::ResourceLoaderBridge::ResponseInfo response_info_; + std::string mime_type_; + std::string data_; + DISALLOW_EVIL_CONSTRUCTORS(ReplaceContentPeer); +}; + +// This class filters insecure image by replacing them with a transparent and +// stamped image. +class ImageFilterPeer : public BufferedPeer { + public: + ImageFilterPeer(webkit_glue::ResourceLoaderBridge* resource_loader_bridge, + webkit_glue::ResourceLoaderBridge::Peer* peer); + virtual ~ImageFilterPeer(); + + protected: + virtual bool DataReady(); + + private: + DISALLOW_EVIL_CONSTRUCTORS(ImageFilterPeer); +}; + +#endif // CHROME_COMMON_SECURITY_FILTER_PEER_H__ diff --git a/chrome/common/slide_animation.cc b/chrome/common/slide_animation.cc new file mode 100644 index 0000000..f3f9c5a --- /dev/null +++ b/chrome/common/slide_animation.cc @@ -0,0 +1,148 @@ +// 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 "chrome/common/slide_animation.h" + +// How many frames per second to target. +static const int kDefaultFramerateHz = 50; + +// How long animations should take by default. +static const int kDefaultDurationMs = 120; + +SlideAnimation::SlideAnimation(AnimationDelegate* target) + : Animation(kDefaultFramerateHz, target), + target_(target), + showing_(false), + value_start_(0), + value_end_(0), + value_current_(0), + tween_type_(EASE_OUT), + slide_duration_(kDefaultDurationMs) { +} + +SlideAnimation::~SlideAnimation() { +} + +void SlideAnimation::Reset() { + Reset(0); +} + +void SlideAnimation::Reset(double value) { + Stop(); + showing_ = static_cast<bool>(value == 1); + value_current_ = value; +} + +void SlideAnimation::Show() { + // If we're already showing (or fully shown), we have nothing to do. + if (showing_) + return; + + showing_ = true; + value_start_ = value_current_; + value_end_ = 1.0; + + // Make sure we actually have something to do. + if (slide_duration_ == 0) { + AnimateToState(1.0); // Skip to the end of the animation. + return; + } else if (value_current_ == value_end_) { + return; + } + + // This will also reset the currently-occuring animation. + SetDuration(static_cast<int>(slide_duration_ * (1 - value_current_))); + Start(); +} + +void SlideAnimation::Hide() { + // If we're already hiding (or hidden), we have nothing to do. + if (!showing_) + return; + + showing_ = false; + value_start_ = value_current_; + value_end_ = 0.0; + + // Make sure we actually have something to do. + if (slide_duration_ == 0) { + AnimateToState(0.0); // Skip to the end of the animation. + return; + } else if (value_current_ == value_end_) { + return; + } + + // This will also reset the currently-occuring animation. + SetDuration(static_cast<int>(slide_duration_ * value_current_)); + Start(); +} + +void SlideAnimation::AnimateToState(double state) { + if (state > 1.0) + state = 1.0; + + // Make our animation ease-out. + switch (tween_type_) { + case EASE_IN: + state = pow(state, 2); + break; + case EASE_IN_OUT: + if (state < 0.5) + state = pow(state * 2, 2) / 2.0; + else + state = 1.0 - (pow((state - 1.0) * 2, 2) / 2.0); + break; + case FAST_IN_OUT: + state = (pow(state - 0.5, 3) + 0.125) / 0.25; + break; + case NONE: + // state remains linear. + break; + case EASE_OUT_SNAP: + state = 0.95 * (1.0 - pow(1.0 - state, 2)); + break; + case EASE_OUT: + default: + state = 1.0 - pow(1.0 - state, 2); + break; + } + + value_current_ = value_start_ + (value_end_ - value_start_) * state; + + // Implement snapping. + if (tween_type_ == EASE_OUT_SNAP && abs(value_current_ - value_end_) <= 0.06) + value_current_ = value_end_; + + // Correct for any overshoot (while state may be capped at 1.0, let's not + // take any rounding error chances. + if ((value_end_ >= value_start_ && value_current_ > value_end_) || + (value_end_ < value_start_ && value_current_ < value_end_)) { + value_current_ = value_end_; + } +} diff --git a/chrome/common/slide_animation.h b/chrome/common/slide_animation.h new file mode 100644 index 0000000..4fe28df --- /dev/null +++ b/chrome/common/slide_animation.h @@ -0,0 +1,125 @@ +// 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 CHROME_SLIDE_COMMON_ANIMATION_H__ +#define CHROME_SLIDE_COMMON_ANIMATION_H__ + +#include "chrome/common/animation.h" + +// Slide Animation +// +// Used for reversible animations and as a general helper class. Typical usage: +// +// #include "chrome/common/slide_animation.h" +// +// class MyClass : public AnimationDelegate { +// public: +// MyClass() { +// animation_.reset(new SlideAnimation(this)); +// animation_->SetSlideDuration(500); +// } +// void OnMouseOver() { +// animation_->Show(); +// } +// void OnMouseOut() { +// animation_->Hide(); +// } +// void AnimationProgressed(const Animation* animation) { +// if (animation == animation_.get()) { +// Layout(); +// SchedulePaint(); +// } else if (animation == other_animation_.get()) { +// ... +// } +// } +// void Layout() { +// if (animation_->IsAnimating()) { +// hover_image_.SetOpacity(animation_->GetCurrentValue()); +// } +// } +// private: +// scoped_ptr<SlideAnimation> animation_; +// } +class SlideAnimation : public Animation { + public: + explicit SlideAnimation(AnimationDelegate* target); + virtual ~SlideAnimation(); + + enum TweenType { + NONE, // Default linear. + EASE_OUT, // Fast in, slow out. + EASE_IN, // Slow in, fast out. + EASE_IN_OUT, // Slow in and out, fast in the middle. + FAST_IN_OUT, // Fast in and out, slow in the middle. + EASE_OUT_SNAP, // Fast in, slow out, snap to final value. + }; + + // Set the animation back to the 0 state. + virtual void Reset(); + virtual void Reset(double value); + + // Begin a showing animation or reverse a hiding animation in progress. + virtual void Show(); + + // Begin a hiding animation or reverse a showing animation in progress. + virtual void Hide(); + + // Sets the time a slide will take. Note that this isn't actually + // the amount of time an animation will take as the current value of + // the slide is considered. + virtual void SetSlideDuration(int duration) { slide_duration_ = duration; } + int GetSlideDuration() const { return slide_duration_; } + void SetTweenType(TweenType tween_type) { tween_type_ = tween_type; } + + double GetCurrentValue() const { return value_current_; } + bool IsShowing() { return showing_; } + + private: + // Overridden from Animation. + void AnimateToState(double state); + + AnimationDelegate* target_; + + TweenType tween_type_; + + // Used to determine which way the animation is going. + bool showing_; + + // Animation values. These are a layer on top of Animation::state_ to + // provide the reversability. + double value_start_; + double value_end_; + double value_current_; + + // How long a hover in/out animation will last for. This defaults to + // kHoverFadeDurationMS, but can be overridden with SetDuration. + int slide_duration_; +}; + +#endif // CHROME_COMMON_SLIDE_ANIMATION_H__ diff --git a/chrome/common/sqlite_compiled_statement.cc b/chrome/common/sqlite_compiled_statement.cc new file mode 100644 index 0000000..3916d14 --- /dev/null +++ b/chrome/common/sqlite_compiled_statement.cc @@ -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. + +#include "chrome/common/stl_util-inl.h" +#include "chrome/common/sqlite_compiled_statement.h" + +// SqliteStatementCache ------------------------------------------------------- + +SqliteStatementCache::~SqliteStatementCache() { + STLDeleteContainerPairSecondPointers(statements_.begin(), statements_.end()); + statements_.clear(); + db_ = NULL; +} + +SQLStatement* SqliteStatementCache::InternalGetStatement(const char* func_name, + int func_number, + const char* sql) { + FuncID id; + id.name = func_name; + id.number = func_number; + + StatementMap::const_iterator found = statements_.find(id); + if (found != statements_.end()) + return found->second; + + if (!sql) + return NULL; // Don't create a new statement when we were not given SQL. + + // Create a new statement. + SQLStatement* statement = new SQLStatement(); + if (statement->prepare(db_, sql) != SQLITE_OK) { + const char* err_msg = sqlite3_errmsg(db_); + NOTREACHED() << "SQL preparation error for: " << err_msg; + return NULL; + } + + statements_[id] = statement; + return statement; +} + +bool SqliteStatementCache::FuncID::operator<(const FuncID& other) const { + // Compare numbers first since they are faster than strings and they are + // almost always unique. + if (number != other.number) + return number < other.number; + return name < other.name; +} + +// SqliteCompiledStatement ---------------------------------------------------- + +SqliteCompiledStatement::SqliteCompiledStatement(const char* func_name, + int func_number, + SqliteStatementCache& cache, + const char* sql) { + statement_ = cache.GetStatement(func_name, func_number, sql); +} + +SqliteCompiledStatement::~SqliteCompiledStatement() { + // Reset the statement so that subsequent callers don't get crap in it. + if (statement_) + statement_->reset(); +} diff --git a/chrome/common/sqlite_compiled_statement.h b/chrome/common/sqlite_compiled_statement.h new file mode 100644 index 0000000..ad12b45 --- /dev/null +++ b/chrome/common/sqlite_compiled_statement.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 CHROME_COMMON_SQLITE_COMPILED_STATEMENT__ +#define CHROME_COMMON_SQLITE_COMPILED_STATEMENT__ + +#include <map> +#include <string> + +#include "base/logging.h" +#include "chrome/common/sqlite_utils.h" +#include "chrome/third_party/sqlite/sqlite3.h" + +// Stores a list of precompiled sql statements for a database. Each statement +// is given a unique name by the caller. +// +// Note: see comments on descructor. +class SqliteStatementCache { + public: + // You must call set_db before anything else if you use this constructor. + SqliteStatementCache() : db_(NULL) { + } + + SqliteStatementCache(sqlite3* db) : db_(db) { + } + + // This object must be deleted before the sqlite connection it is associated + // with. Otherwise, sqlite seems to keep the file open because there are open + // statements. + ~SqliteStatementCache(); + + void set_db(sqlite3* db) { + DCHECK(!db_) << "Setting the database twice"; + db_ = db; + } + + // Creates or retrieves a cached SQL statement identified by the given + // (name, number) pair. + // + // The name and number can be anything the caller wants, but must uniquely + // identify the SQL. The caller must ensure that every call with the same + // number and name has the same SQL. + // + // In practice the number and name is supposed to be a file and line number. + // (See the SQLITE_UNIQUE_STATEMENT macro below.) Recommended practice is to + // use 0 for the function number if you are not using this scheme, and just + // use a name you like. + // + // On error, NULL is returned. Otherwise, the statement for the given SQL is + // returned. This pointer is cached and owned by this class. + // + // The caller should not cache this value since it may be used by others. + // The caller should reset the statement when it is complete so that + // subsequent callers do not get bound stuff. + SQLStatement* GetStatement(const char* func_name, + int func_number, + const char* sql) { + return InternalGetStatement(func_name, func_number, sql); + } + + // Returns the cached statement if it has already been created, or NULL if it + // has not. + SQLStatement* GetExistingStatement(const char* func_name, + int func_number) { + return InternalGetStatement(func_name, func_number, NULL); + } + + private: + // The key used for precompiled function lookup. + struct FuncID { + int number; + std::string name; + + // Used as a key in the map below, so we need this comparator. + bool operator<(const FuncID& other) const; + }; + + // Backend for GetStatement and GetExistingStatement. If sql is NULL, we will + // only look for an existing statement and return NULL if there is not a + // matching one. If it is non-NULL, we will create it if it doesn't exist. + SQLStatement* InternalGetStatement(const char* func_name, + int func_number, + const char* sql); + + sqlite3* db_; + + // This object owns the statement pointers, which it must manually free. + typedef std::map<FuncID, SQLStatement*> StatementMap; + StatementMap statements_; + + DISALLOW_EVIL_CONSTRUCTORS(SqliteStatementCache); +}; + +// Automatically creates or retrieves a statement from the given cache, and +// automatically resets the statement when it goes out of scope. +class SqliteCompiledStatement { + public: + // See SqliteStatementCache::GetStatement for a description of these args. + SqliteCompiledStatement(const char* func_name, + int func_number, + SqliteStatementCache& cache, + const char* sql); + ~SqliteCompiledStatement(); + + // Call to see if this statement is valid or not. Using this statement will + // segfault if it is not valid. + bool is_valid() const { return !!statement_; } + + // Allow accessing this object to be like accessing a statement for + // convenience. The caller must ensure the statement is_valid() before using + // these two functions. + SQLStatement& operator*() { + DCHECK(statement_) << "Should check is_valid() before using the statement."; + return *statement_; + } + SQLStatement* operator->() { + DCHECK(statement_) << "Should check is_valid() before using the statement."; + return statement_; + } + SQLStatement* statement() { + DCHECK(statement_) << "Should check is_valid() before using the statement."; + return statement_; + } + + private: + // The sql statement if valid, NULL if not valid. This pointer is NOT owned + // by this class, it is owned by the statement cache object. + SQLStatement* statement_; + + DISALLOW_EVIL_CONSTRUCTORS(SqliteCompiledStatement); +}; + +// Creates a compiled statement that has a unique name based on the file and +// line number. Example: +// +// SQLITE_UNIQUE_STATEMENT(var_name, cache, "SELECT * FROM foo"); +// if (!var_name.is_valid()) +// return oops; +// var_name->bind_XXX( +// var_name->step(); +// ... +#define SQLITE_UNIQUE_STATEMENT(var_name, cache, sql) \ + SqliteCompiledStatement var_name(__FILE__, __LINE__, cache, sql) + +#endif // CHROME_COMMON_SQLITE_COMPILED_STATEMENT__ diff --git a/chrome/common/sqlite_utils.cc b/chrome/common/sqlite_utils.cc new file mode 100644 index 0000000..cadb609 --- /dev/null +++ b/chrome/common/sqlite_utils.cc @@ -0,0 +1,95 @@ +// 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 "chrome/common/sqlite_utils.h" + +bool DoesSqliteTableExist(sqlite3* db, + const char* db_name, + const char* table_name) { + // sqlite doesn't allow binding parameters as table names, so we have to + // manually construct the sql + std::string sql("SELECT name FROM "); + if (db_name && db_name[0]) { + sql.append(db_name); + sql.push_back('.'); + } + sql.append("sqlite_master WHERE type='table' AND name=?"); + + SQLStatement statement; + if (statement.prepare(db, sql.c_str()) != SQLITE_OK) + return false; + + if (statement.bind_text(0, table_name) != SQLITE_OK) + return false; + + // we only care about if this matched a row, not the actual data + return sqlite3_step(statement.get()) == SQLITE_ROW; +} + +bool DoesSqliteColumnExist(sqlite3* db, + const char* database_name, + const char* table_name, + const char* column_name, + const char* column_type) { + SQLStatement s; + std::string sql; + sql.append("PRAGMA "); + if (database_name && database_name[0]) { + // optional database name specified + sql.append(database_name); + sql.push_back('.'); + } + sql.append("TABLE_INFO("); + sql.append(table_name); + sql.append(")"); + + if (s.prepare(db, sql.c_str()) != SQLITE_OK) + return false; + + while (s.step() == SQLITE_ROW) { + if (!strcmp(column_name, s.column_text(1))) { + if (column_type && column_type[0]) + return !strcmp(column_type, s.column_text(2)); + return true; + } + } + return false; +} + +bool DoesSqliteTableHaveRow(sqlite3* db, const char* table_name) { + SQLStatement s; + std::string b; + b.append("SELECT * FROM "); + b.append(table_name); + + if (s.prepare(db, b.c_str()) != SQLITE_OK) + return false; + + return s.step() == SQLITE_ROW; +} diff --git a/chrome/common/sqlite_utils.h b/chrome/common/sqlite_utils.h new file mode 100644 index 0000000..8b0d529 --- /dev/null +++ b/chrome/common/sqlite_utils.h @@ -0,0 +1,641 @@ +// 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 CHROME_COMMON_SQLITEUTILS_H__ +#define CHROME_COMMON_SQLITEUTILS_H__ + +#include <vector> + +#include "base/logging.h" +#include "chrome/third_party/sqlite/sqlite3.h" + +// forward declarations of classes defined here +class SQLTransaction; +class SQLNestedTransaction; +class SQLNestedTransactionSite; +class scoped_sqlite3_stmt_ptr; +class SQLStatement; + +//------------------------------------------------------------------------------ +// A wrapper for sqlite transactions that rollsback when the wrapper +// goes out of scope if the caller has not already called Commit or Rollback. +// Note: the constructor does NOT Begin a transaction. +//------------------------------------------------------------------------------ +class SQLTransaction { + public: + SQLTransaction(sqlite3 *db) { + db_ = db; + began_ = false; + } + + virtual ~SQLTransaction() { + if (began_) { + Rollback(); + } + } + + int Begin() { + // By default, we BEGIN IMMEDIATE to establish file locks at the + // onset of a transaction. This avoids SQLITE_BUSY errors, without + // waiting for the busy timeout period, which can occur when BEGIN + // DEFERRED is used. + return BeginImmediate(); + } + + int BeginExclusive() { + return BeginCommand("BEGIN EXCLUSIVE"); + } + + int BeginImmediate() { + return BeginCommand("BEGIN IMMEDIATE"); + } + + int BeginDeferred() { + return BeginCommand("BEGIN DEFERRED"); + } + + int Commit() { + return EndCommand("COMMIT"); + } + + int Rollback() { + return EndCommand("ROLLBACK"); + } + + bool HasBegun() { + return began_; + } + + protected: + virtual int BeginCommand(const char *command) { + int rv = SQLITE_ERROR; + if (!began_ && db_) { + rv = sqlite3_exec(db_, command, NULL, NULL, NULL); + began_ = (rv == SQLITE_OK); + } + return rv; + } + + virtual int EndCommand(const char *command) { + int rv = SQLITE_ERROR; + if (began_ && db_) { + rv = sqlite3_exec(db_, command, NULL, NULL, NULL); + began_ = (rv != SQLITE_OK); + } + return rv; + } + + bool began_; + sqlite3 *db_; + DISALLOW_EVIL_CONSTRUCTORS(SQLTransaction); +}; + + +//------------------------------------------------------------------------------ +// A class for use with SQLNestedTransaction. +//------------------------------------------------------------------------------ +class SQLNestedTransactionSite { + protected: + SQLNestedTransactionSite() : db_(NULL), top_transaction_(NULL) {} + virtual ~SQLNestedTransactionSite() { DCHECK(!top_transaction_); } + + // The following virtual methods provide notification of true transaction + // boundaries as they are crossed by a top nested transaction. + // Intended to be overriden (See WebCacheDB) + // SQLNestedTransaction calls these after the underlying database + // operation has been performed. + + virtual void OnBegin() {} + virtual void OnCommit() {} + virtual void OnRollback() {} + + // Returns the sqlite3 database connection associated with this site + // Used by SQLNestedTransaction + sqlite3* GetSqlite3DB() { return db_; } + + // Returns the current top nested transaction associated with this site + // Used by SQLNestedTransaction + SQLNestedTransaction *GetTopTransaction() { + return top_transaction_; + } + + // Sets or clears the top nested transaction associated with this site + // Used by SQLNestedTransaction + void SetTopTransaction(SQLNestedTransaction *top) { + DCHECK(!top || !top_transaction_); + top_transaction_ = top; + } + + sqlite3* db_; + SQLNestedTransaction *top_transaction_; + friend class SQLNestedTransaction; +}; + +//------------------------------------------------------------------------------ +// SQLite does not support nested transactions. This class provides a gross +// approximation of nested transactions. +// +// Really there is only one transaction, the top transaction. +// +// A nested transaction commits with respect to the top transaction. +// That is, even though the nested transaction commits, the permanence of its +// effects depends on the top transaction committing. If the top +// transaction rollsback, the results of the nested transaction are backed out. +// If any nested transaction aborts, the top transaction ultimately rollsback +// as well. +// +// Note: If a nested transaction is open for a particular db connection, an +// attempt to open a non-nested transaction (class SQLTransaction) will fail. +// And vice versa. +// +// TODO(michaeln): demonstrate usage here +// TODO(michaeln): safegaurds to prevent mis-use +//------------------------------------------------------------------------------ +class SQLNestedTransaction : public SQLTransaction { + public: + SQLNestedTransaction(SQLNestedTransactionSite *site) + : SQLTransaction(site->GetSqlite3DB()), + needs_rollback_(false), + site_(site) { + DCHECK(site); + if (site->GetTopTransaction() == NULL) { + site->SetTopTransaction(this); + } + } + + virtual ~SQLNestedTransaction() { + if (began_) { + Rollback(); + } + if (site_->GetTopTransaction() == this) { + site_->SetTopTransaction(NULL); + } + } + + protected: + virtual int BeginCommand(const char *command) { + DCHECK(db_); + DCHECK(site_ && site_->GetTopTransaction()); + if (!db_ || began_) { + return SQLITE_ERROR; + } + if (site_->GetTopTransaction() == this) { + int rv = sqlite3_exec(db_, command, NULL, NULL, NULL); + began_ = (rv == SQLITE_OK); + if (began_) { + site_->OnBegin(); + } + return rv; + } else { + if (site_->GetTopTransaction()->needs_rollback_) { + return SQLITE_ERROR; + } + began_ = true; + return SQLITE_OK; + } + } + + virtual int EndCommand(const char *command) { + DCHECK(db_); + DCHECK(site_ && site_->GetTopTransaction()); + if (!db_ || !began_) { + return SQLITE_ERROR; + } + if (site_->GetTopTransaction() == this) { + if (needs_rollback_) { + sqlite3_exec(db_, "ROLLBACK", NULL, NULL, NULL); + began_ = false; // reset so we don't try to rollback or call + // OnRollback() again + site_->OnRollback(); + return SQLITE_ERROR; + } else { + int rv = sqlite3_exec(db_, command, NULL, NULL, NULL); + began_ = (rv != SQLITE_OK); + if (strcmp(command, "ROLLBACK") == 0) { + began_ = false; // reset so we don't try to rollbck or call + // OnRollback() again + site_->OnRollback(); + } else { + DCHECK(strcmp(command, "COMMIT") == 0); + if (rv == SQLITE_OK) { + site_->OnCommit(); + } + } + return rv; + } + } else { + if (strcmp(command, "ROLLBACK") == 0) { + site_->GetTopTransaction()->needs_rollback_ = true; + } + began_ = false; + return SQLITE_OK; + } + } + + private: + bool needs_rollback_; + SQLNestedTransactionSite *site_; + DISALLOW_EVIL_CONSTRUCTORS(SQLNestedTransaction); +}; + +//------------------------------------------------------------------------------ +// A scoped sqlite statement that finalizes when it goes out of scope. +//------------------------------------------------------------------------------ +class scoped_sqlite3_stmt_ptr { + public: + ~scoped_sqlite3_stmt_ptr() { + finalize(); + } + + scoped_sqlite3_stmt_ptr() : stmt_(NULL) { + } + + explicit scoped_sqlite3_stmt_ptr(sqlite3_stmt *stmt) + : stmt_(stmt) { + } + + sqlite3_stmt *get() const { + return stmt_; + } + + void set(sqlite3_stmt *stmt) { + finalize(); + stmt_ = stmt; + } + + sqlite3_stmt *release() { + sqlite3_stmt *tmp = stmt_; + stmt_ = NULL; + return tmp; + } + + // It is not safe to call sqlite3_finalize twice on the same stmt. + // Sqlite3's sqlite3_finalize() function should not be called directly + // without calling the release method. If sqlite3_finalize() must be + // called directly, the following usage is advised: + // scoped_sqlite3_stmt_ptr stmt; + // ... do something with stmt ... + // sqlite3_finalize(stmt.release()); + int finalize() { + int err = sqlite3_finalize(stmt_); + stmt_ = NULL; + return err; + } + + protected: + sqlite3_stmt *stmt_; + + private: + DISALLOW_EVIL_CONSTRUCTORS(scoped_sqlite3_stmt_ptr); +}; + + +//------------------------------------------------------------------------------ +// A scoped sqlite statement with convenient C++ wrappers for sqlite3 APIs. +//------------------------------------------------------------------------------ +class SQLStatement : public scoped_sqlite3_stmt_ptr { +public: + SQLStatement() { + } + + int prepare(sqlite3 *db, const char *sql) { + return prepare(db, sql, -1); + } + + int prepare(sqlite3 *db, const char *sql, int sql_len) { + DCHECK(!stmt_); + int rv = sqlite3_prepare_v2(db, sql, sql_len, &stmt_, NULL); + if (rv != SQLITE_OK) { + DLOG(ERROR) << "SQLStatement.prepare_v2 failed: " << sqlite3_errmsg(db); + } + return rv; + } + + int prepare16(sqlite3 *db, const wchar_t *sql) { + return prepare16(db, sql, -1); + } + + // sql_len is number of characters or may be negative + // a for null-terminated sql string + int prepare16(sqlite3 *db, const wchar_t *sql, int sql_len) { + DCHECK(!stmt_); + sql_len *= sizeof(wchar_t); + int rv = sqlite3_prepare16_v2(db, sql, sql_len, &stmt_, NULL); + if (rv != SQLITE_OK) { + DLOG(ERROR) << "SQLStatement.prepare16_v2 failed: " << sqlite3_errmsg(db); + } + return rv; + } + + int step() { + DCHECK(stmt_); + return sqlite3_step(stmt_); + } + + int reset() { + DCHECK(stmt_); + return sqlite3_reset(stmt_); + } + + sqlite_int64 last_insert_rowid() { + DCHECK(stmt_); + return sqlite3_last_insert_rowid(db_handle()); + } + + sqlite3 *db_handle() { + DCHECK(stmt_); + return sqlite3_db_handle(stmt_); + } + + // + // Parameter binding helpers (NOTE: index is 0-based) + // + + int bind_parameter_count() { + DCHECK(stmt_); + return sqlite3_bind_parameter_count(stmt_); + } + + typedef void (*Function)(void*); + + int bind_blob(int index, std::vector<unsigned char> *blob) { + if (blob) { + const void *value = &(*blob)[0]; + int len = static_cast<int>(blob->size()); + return bind_blob(index, value, len); + } else { + return bind_null(index); + } + } + + int bind_blob(int index, const void *value, int value_len) { + return bind_blob(index, value, value_len, SQLITE_TRANSIENT); + } + + int bind_blob(int index, const void *value, int value_len, Function dtor) { + DCHECK(stmt_); + return sqlite3_bind_blob(stmt_, index + 1, value, value_len, dtor); + } + + int bind_double(int index, double value) { + DCHECK(stmt_); + return sqlite3_bind_double(stmt_, index + 1, value); + } + + int bind_bool(int index, bool value) { + DCHECK(stmt_); + return sqlite3_bind_int(stmt_, index + 1, value); + } + + int bind_int(int index, int value) { + DCHECK(stmt_); + return sqlite3_bind_int(stmt_, index + 1, value); + } + + int bind_int64(int index, sqlite_int64 value) { + DCHECK(stmt_); + return sqlite3_bind_int64(stmt_, index + 1, value); + } + + int bind_null(int index) { + DCHECK(stmt_); + return sqlite3_bind_null(stmt_, index + 1); + } + + int bind_string(int index, const std::string& value) { + // don't use c_str so it doesn't have to fix up the null terminator + // (sqlite just uses the length) + return bind_text(index, value.data(), + static_cast<int>(value.length()), SQLITE_TRANSIENT); + } + + int bind_wstring(int index, const std::wstring& value) { + // don't use c_str so it doesn't have to fix up the null terminator + // (sqlite just uses the length) + return bind_text16(index, value.data(), + static_cast<int>(value.length()), SQLITE_TRANSIENT); + } + + int bind_text(int index, const char *value) { + return bind_text(index, value, -1, SQLITE_TRANSIENT); + } + + // value_len is number of characters or may be negative + // a for null-terminated value string + int bind_text(int index, const char *value, int value_len) { + return bind_text(index, value, value_len, SQLITE_TRANSIENT); + } + + // value_len is number of characters or may be negative + // a for null-terminated value string + int bind_text(int index, const char *value, int value_len, + Function dtor) { + DCHECK(stmt_); + return sqlite3_bind_text(stmt_, index + 1, value, value_len, dtor); + } + + int bind_text16(int index, const wchar_t *value) { + return bind_text16(index, value, -1, SQLITE_TRANSIENT); + } + + // value_len is number of characters or may be negative + // a for null-terminated value string + int bind_text16(int index, const wchar_t *value, int value_len) { + return bind_text16(index, value, value_len, SQLITE_TRANSIENT); + } + + // value_len is number of characters or may be negative + // a for null-terminated value string + int bind_text16(int index, const wchar_t *value, int value_len, + Function dtor) { + DCHECK(stmt_); + value_len *= sizeof(wchar_t); + return sqlite3_bind_text16(stmt_, index + 1, value, value_len, dtor); + } + + int bind_value(int index, const sqlite3_value *value) { + DCHECK(stmt_); + return sqlite3_bind_value(stmt_, index + 1, value); + } + + // + // Column helpers (NOTE: index is 0-based) + // + + int column_count() { + DCHECK(stmt_); + return sqlite3_column_count(stmt_); + } + + int column_type(int index) { + DCHECK(stmt_); + return sqlite3_column_type(stmt_, index); + } + + const wchar_t *column_name16(int index) { + DCHECK(stmt_); + return static_cast<const wchar_t*>( sqlite3_column_name16(stmt_, index) ); + } + + const void *column_blob(int index) { + DCHECK(stmt_); + return sqlite3_column_blob(stmt_, index); + } + + bool column_blob_as_vector(int index, std::vector<unsigned char> *blob) { + DCHECK(stmt_); + const void *p = column_blob(index); + size_t len = column_bytes(index); + blob->resize(len); + if (blob->size() != len) { + return false; + } + if (len > 0) + memcpy(&(blob->front()), p, len); + return true; + } + + bool column_blob_as_string(int index, std::string* blob) { + DCHECK(stmt_); + const void *p = column_blob(index); + size_t len = column_bytes(index); + blob->resize(len); + if (blob->size() != len) { + return false; + } + blob->assign(reinterpret_cast<const char*>(p), len); + return true; + } + + int column_bytes(int index) { + DCHECK(stmt_); + return sqlite3_column_bytes(stmt_, index); + } + + int column_bytes16(int index) { + DCHECK(stmt_); + return sqlite3_column_bytes16(stmt_, index); + } + + double column_double(int index) { + DCHECK(stmt_); + return sqlite3_column_double(stmt_, index); + } + + bool column_bool(int index) { + DCHECK(stmt_); + return sqlite3_column_int(stmt_, index) ? true : false; + } + + int column_int(int index) { + DCHECK(stmt_); + return sqlite3_column_int(stmt_, index); + } + + sqlite_int64 column_int64(int index) { + DCHECK(stmt_); + return sqlite3_column_int64(stmt_, index); + } + + const char* column_text(int index) { + DCHECK(stmt_); + return reinterpret_cast<const char*>(sqlite3_column_text(stmt_, index)); + } + + bool column_string(int index, std::string *str) { + DCHECK(stmt_); + DCHECK(str); + const char* s = column_text(index); + str->assign(s ? s : std::string("")); + return s != NULL; + } + + std::string column_string(int index) { + std::string str; + column_string(index, &str); + return str; + } + + const wchar_t *column_text16(int index) { + DCHECK(stmt_); + return static_cast<const wchar_t*>( sqlite3_column_text16(stmt_, index) ); + } + + bool column_string16(int index, std::wstring *str) { + DCHECK(stmt_); + DCHECK(str); + const wchar_t *s = column_text16(index); + str->assign(s ? s : std::wstring(L"")); + return (s != NULL); + } + + std::wstring column_string16(int index) { + std::wstring wstr; + column_string16(index, &wstr); + return wstr; + } + private: + DISALLOW_EVIL_CONSTRUCTORS(SQLStatement); +}; + +// Returns true if there is a table with the given name in the database. +// For the version where a database name is specified, it may be NULL or the +// empty string if no database name is necessary. +bool DoesSqliteTableExist(sqlite3* db, + const char* db_name, + const char* table_name); +inline bool DoesSqliteTableExist(sqlite3* db, const char* table_name) { + return DoesSqliteTableExist(db, NULL, table_name); +} + + +// Test whether a table has a column matching the provided name and type. +// Returns true if the column exist and false otherwise. There are two +// versions, one that takes a database name, the other that doesn't. The +// database name can be NULL or empty if no name is desired. +// +// Column type is optional, it can be NULL or empty. If specified, we the +// function will check that the column is of the correct type (case-sensetive). +bool DoesSqliteColumnExist(sqlite3* db, + const char* datbase_name, + const char* table_name, + const char* column_name, + const char* column_type); +inline bool DoesSqliteColumnExist(sqlite3* db, + const char* table_name, + const char* column_name, + const char* column_type) { + return DoesSqliteColumnExist(db, NULL, table_name, column_name, column_type); +} + +// Test whether a table has one or more rows. Returns true if the table +// has one or more rows and false if the table is empty or doesn't exist. +bool DoesSqliteTableHaveRow(sqlite3* db, const char* table_name); + +#endif // CHROME_COMMON_SQLITEUTILS_H__ diff --git a/chrome/common/stl_util-inl.h b/chrome/common/stl_util-inl.h new file mode 100644 index 0000000..0ddf0e8 --- /dev/null +++ b/chrome/common/stl_util-inl.h @@ -0,0 +1,476 @@ +// +// 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. +// +// STL utility functions. Usually, these replace built-in, but slow(!), +// STL functions with more efficient versions. +// + +#ifndef CHROME_COMMON_STL_UTIL_INL_H__ +#define CHROME_COMMON_STL_UTIL_INL_H__ + +#include <string.h> // for memcpy +#include <functional> +#include <set> +#include <string> +#include <vector> +#include <cassert> + +// Clear internal memory of an STL object. +// STL clear()/reserve(0) does not always free internal memory allocated +// This function uses swap/destructor to ensure the internal memory is freed. +template<class T> void STLClearObject(T* obj) { + T tmp; + tmp.swap(*obj); + obj->reserve(0); // this is because sometimes "T tmp" allocates objects with + // memory (arena implementation?). use reserve() + // to clear() even if it doesn't always work +} + +// Reduce memory usage on behalf of object if it is using more than +// "bytes" bytes of space. By default, we clear objects over 1MB. +template <class T> inline void STLClearIfBig(T* obj, size_t limit = 1<<20) { + if (obj->capacity() >= limit) { + STLClearObject(obj); + } else { + obj->clear(); + } +} + +// Reserve space for STL object. +// STL's reserve() will always copy. +// This function avoid the copy if we already have capacity +template<class T> void STLReserveIfNeeded(T* obj, int new_size) { + if (obj->capacity() < new_size) // increase capacity + obj->reserve(new_size); + else if (obj->size() > new_size) // reduce size + obj->resize(new_size); +} + +// STLDeleteContainerPointers() +// For a range within a container of pointers, calls delete +// (non-array version) on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template <class ForwardIterator> +void STLDeleteContainerPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// STLDeleteContainerPairPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on BOTH items in the pairs. +// NOTE: Like STLDeleteContainerPointers, it is important that this deletes +// behind the iterator because if both the key and value are deleted, the +// container may call the hash function on the iterator when it is advanced, +// which could result in the hash function trying to dereference a stale +// pointer. +template <class ForwardIterator> +void STLDeleteContainerPairPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + delete temp->second; + } +} + +// STLDeleteContainerPairFirstPointers() +// For a range within a container of pairs, calls delete (non-array version) +// on the FIRST item in the pairs. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +template <class ForwardIterator> +void STLDeleteContainerPairFirstPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + } +} + +// STLDeleteContainerPairSecondPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on the SECOND item in the pairs. +template <class ForwardIterator> +void STLDeleteContainerPairSecondPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + delete begin->second; + ++begin; + } +} + +template<typename T> +inline void STLAssignToVector(std::vector<T>* vec, + const T* ptr, + size_t n) { + vec->resize(n); + memcpy(&vec->front(), ptr, n*sizeof(T)); +} + +/***** Hack to allow faster assignment to a vector *****/ + +// This routine speeds up an assignment of 32 bytes to a vector from +// about 250 cycles per assignment to about 140 cycles. +// +// Usage: +// STLAssignToVectorChar(&vec, ptr, size); +// STLAssignToString(&str, ptr, size); + +inline void STLAssignToVectorChar(std::vector<char>* vec, + const char* ptr, + size_t n) { + STLAssignToVector(vec, ptr, n); +} + +inline void STLAssignToString(std::string* str, const char* ptr, size_t n) { + str->resize(n); + memcpy(&*str->begin(), ptr, n); +} + +// To treat a possibly-empty vector as an array, use these functions. +// If you know the array will never be empty, you can use &*v.begin() +// directly, but that is allowed to dump core if v is empty. This +// function is the most efficient code that will work, taking into +// account how our STL is actually implemented. THIS IS NON-PORTABLE +// CODE, so call us instead of repeating the nonportable code +// everywhere. If our STL implementation changes, we will need to +// change this as well. + +template<typename T> +inline T* vector_as_array(std::vector<T>* v) { +# ifdef NDEBUG + return &*v->begin(); +# else + return v->empty() ? NULL : &*v->begin(); +# endif +} + +template<typename T> +inline const T* vector_as_array(const std::vector<T>* v) { +# ifdef NDEBUG + return &*v->begin(); +# else + return v->empty() ? NULL : &*v->begin(); +# endif +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(std::string* str) { + // DO NOT USE const_cast<char*>(str->data())! See the unittest for why. + return str->empty() ? NULL : &*str->begin(); +} + +// These are methods that test two hash maps/sets for equality. These exist +// because the == operator in the STL can return false when the maps/sets +// contain identical elements. This is because it compares the internal hash +// tables which may be different if the order of insertions and deletions +// differed. + +template <class HashSet> +inline bool +HashSetEquality(const HashSet& set_a, + const HashSet& set_b) { + if (set_a.size() != set_b.size()) return false; + for (typename HashSet::const_iterator i = set_a.begin(); i != set_a.end(); ++i) + if (set_b.find(*i) == set_b.end()) return false; + return true; +} + +template <class HashMap> +inline bool +HashMapEquality(const HashMap& map_a, + const HashMap& map_b) { + if (map_a.size() != map_b.size()) return false; + for (typename HashMap::const_iterator i = map_a.begin(); + i != map_a.end(); ++i) { + typename HashMap::const_iterator j = map_b.find(i->first); + if (j == map_b.end()) return false; + if (i->second != j->second) return false; + } + return true; +} + +// The following functions are useful for cleaning up STL containers +// whose elements point to allocated memory. + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// STLElementDeleter (defined below), which ensures that your container's +// elements are deleted when the STLElementDeleter goes out of scope. +template <class T> +void STLDeleteElements(T *container) { + if (!container) return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. + +template <class T> +void STLDeleteValues(T *v) { + if (!v) return; + for (typename T::iterator i = v->begin(); i != v->end(); ++i) { + delete i->second; + } + v->clear(); +} + + +// The following classes provide a convenient way to delete all elements or +// values from STL containers when they goes out of scope. This greatly +// simplifies code that creates temporary objects and has multiple return +// statements. Example: +// +// vector<MyProto *> tmp_proto; +// STLElementDeleter<vector<MyProto *> > d(&tmp_proto); +// if (...) return false; +// ... +// return success; + +// Given a pointer to an STL container this class will delete all the element +// pointers when it goes out of scope. + +template<class STLContainer> class STLElementDeleter { + public: + STLElementDeleter<STLContainer>(STLContainer *ptr) : container_ptr_(ptr) {} + ~STLElementDeleter<STLContainer>() { STLDeleteElements(container_ptr_); } + private: + STLContainer *container_ptr_; +}; + +// Given a pointer to an STL container this class will delete all the value +// pointers when it goes out of scope. + +template<class STLContainer> class STLValueDeleter { + public: + STLValueDeleter<STLContainer>(STLContainer *ptr) : container_ptr_(ptr) {} + ~STLValueDeleter<STLContainer>() { STLDeleteValues(container_ptr_); } + private: + STLContainer *container_ptr_; +}; + + +// Forward declare some callback classes in callback.h for STLBinaryFunction +template <class R, class T1, class T2> +class ResultCallback2; + +// STLBinaryFunction is a wrapper for the ResultCallback2 class in callback.h +// It provides an operator () method instead of a Run method, so it may be +// passed to STL functions in <algorithm>. +// +// The client should create callback with NewPermanentCallback, and should +// delete callback after it is done using the STLBinaryFunction. + +template <class Result, class Arg1, class Arg2> +class STLBinaryFunction : public std::binary_function<Arg1, Arg2, Result> { + public: + typedef ResultCallback2<Result, Arg1, Arg2> Callback; + + STLBinaryFunction(Callback* callback) + : callback_(callback) { + assert(callback_); + } + + Result operator() (Arg1 arg1, Arg2 arg2) { + return callback_->Run(arg1, arg2); + } + + private: + Callback* callback_; +}; + +// STLBinaryPredicate is a specialized version of STLBinaryFunction, where the +// return type is bool and both arguments have type Arg. It can be used +// wherever STL requires a StrictWeakOrdering, such as in sort() or +// lower_bound(). +// +// templated typedefs are not supported, so instead we use inheritance. + +template <class Arg> +class STLBinaryPredicate : public STLBinaryFunction<bool, Arg, Arg> { + public: + typedef typename STLBinaryPredicate<Arg>::Callback Callback; + STLBinaryPredicate(Callback* callback) + : STLBinaryFunction<bool, Arg, Arg>(callback) { + } +}; + +// Functors that compose arbitrary unary and binary functions with a +// function that "projects" one of the members of a pair. +// Specifically, if p1 and p2, respectively, are the functions that +// map a pair to its first and second, respectively, members, the +// table below summarizes the functions that can be constructed: +// +// * UnaryOperate1st<pair>(f) returns the function x -> f(p1(x)) +// * UnaryOperate2nd<pair>(f) returns the function x -> f(p2(x)) +// * BinaryOperate1st<pair>(f) returns the function (x,y) -> f(p1(x),p1(y)) +// * BinaryOperate2nd<pair>(f) returns the function (x,y) -> f(p2(x),p2(y)) +// +// A typical usage for these functions would be when iterating over +// the contents of an STL map. For other sample usage, see the unittest. + +template<typename Pair, typename UnaryOp> +class UnaryOperateOnFirst + : public std::unary_function<Pair, typename UnaryOp::result_type> { + public: + UnaryOperateOnFirst() { + } + + UnaryOperateOnFirst(const UnaryOp& f) : f_(f) { + } + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.first); + } + + private: + UnaryOp f_; +}; + +template<typename Pair, typename UnaryOp> +UnaryOperateOnFirst<Pair, UnaryOp> UnaryOperate1st(const UnaryOp& f) { + return UnaryOperateOnFirst<Pair, UnaryOp>(f); +} + +template<typename Pair, typename UnaryOp> +class UnaryOperateOnSecond + : public std::unary_function<Pair, typename UnaryOp::result_type> { + public: + UnaryOperateOnSecond() { + } + + UnaryOperateOnSecond(const UnaryOp& f) : f_(f) { + } + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.second); + } + + private: + UnaryOp f_; +}; + +template<typename Pair, typename UnaryOp> +UnaryOperateOnSecond<Pair, UnaryOp> UnaryOperate2nd(const UnaryOp& f) { + return UnaryOperateOnSecond<Pair, UnaryOp>(f); +} + +template<typename Pair, typename BinaryOp> +class BinaryOperateOnFirst + : public std::binary_function<Pair, Pair, typename BinaryOp::result_type> { + public: + BinaryOperateOnFirst() { + } + + BinaryOperateOnFirst(const BinaryOp& f) : f_(f) { + } + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.first, p2.first); + } + + private: + BinaryOp f_; +}; + +template<typename Pair, typename BinaryOp> +BinaryOperateOnFirst<Pair, BinaryOp> BinaryOperate1st(const BinaryOp& f) { + return BinaryOperateOnFirst<Pair, BinaryOp>(f); +} + +template<typename Pair, typename BinaryOp> +class BinaryOperateOnSecond + : public std::binary_function<Pair, Pair, typename BinaryOp::result_type> { + public: + BinaryOperateOnSecond() { + } + + BinaryOperateOnSecond(const BinaryOp& f) : f_(f) { + } + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.second, p2.second); + } + + private: + BinaryOp f_; +}; + +template<typename Pair, typename BinaryOp> +BinaryOperateOnSecond<Pair, BinaryOp> BinaryOperate2nd(const BinaryOp& f) { + return BinaryOperateOnSecond<Pair, BinaryOp>(f); +} + +// Translates a set into a vector. +template<typename T> +std::vector<T> SetToVector(const std::set<T>& values) { + std::vector<T> result; + result.reserve(values.size()); + std::set<T>::const_iterator end = values.end(); + for (std::set<T>::const_iterator itr = values.begin(); itr != end; ++itr) { + result.push_back(*itr); + } + return result; +} + +#endif // CHROME_COMMON_STL_UTIL_INL_H__ diff --git a/chrome/common/task_queue.cc b/chrome/common/task_queue.cc new file mode 100644 index 0000000..133e66a --- /dev/null +++ b/chrome/common/task_queue.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 "chrome/common/stl_util-inl.h" + +#include "chrome/common/task_queue.h" + +TaskQueue::TaskQueue() { +} + +TaskQueue::~TaskQueue() { + // We own all the pointes in |queue_|. It is our job to delete them. + STLDeleteElements(&queue_); +} + +void TaskQueue::Run() { + // Nothing to run if our queue is empty. + if (queue_.empty()) + return; + + std::deque<Task*> ready; + queue_.swap(ready); + + // Run the tasks that are ready. + std::deque<Task*>::const_iterator task; + for (task = ready.begin(); task != ready.end(); ++task) { + // Run the task and then delete it. + (*task)->Run(); + delete (*task); + } +} + +void TaskQueue::Push(Task* task) { + // Add the task to the back of the queue. + queue_.push_back(task); +} + +void TaskQueue::Clear() { + // Delete all the elements in the queue and clear the dead pointers. + STLDeleteElements(&queue_); +} + +bool TaskQueue::Empty() const { + return queue_.empty(); +} diff --git a/chrome/common/task_queue.h b/chrome/common/task_queue.h new file mode 100644 index 0000000..7646c34 --- /dev/null +++ b/chrome/common/task_queue.h @@ -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. + +#ifndef CHROME_COMMON_TASK_QUEUE_H__ +#define CHROME_COMMON_TASK_QUEUE_H__ + +#include <deque> + +#include "base/task.h" + +// A TaskQueue is a queue of tasks waiting to be run. To run the tasks, call +// the Run method. A task queue is itself a Task so that it can be placed in a +// message loop or another task queue. +class TaskQueue : public Task { + public: + TaskQueue(); + ~TaskQueue(); + + // Run all the tasks in the queue. New tasks pushed onto the queue during + // a run will be run next time |Run| is called. + virtual void Run(); + + // Push the specified task onto the queue. When the queue is run, the tasks + // will be run in the order they are pushed. + // + // This method takes ownership of |task| and will delete task after it is run + // (or when the TaskQueue is destroyed, if we never got a chance to run it). + void Push(Task* task); + + // Remove all tasks from the queue. The tasks are deleted. + void Clear(); + + // Returns true if this queue contains no tasks. + bool Empty() const; + + private: + // The list of tasks we are waiting to run. + std::deque<Task*> queue_; +}; + +#endif // CHROME_COMMON_TASK_QUEUE_H__ diff --git a/chrome/common/text_zoom.h b/chrome/common/text_zoom.h new file mode 100644 index 0000000..04f07b2 --- /dev/null +++ b/chrome/common/text_zoom.h @@ -0,0 +1,42 @@ +// 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 CHROME_COMMON_TEXT_ZOOM_H__ +#define CHROME_COMMON_TEXT_ZOOM_H__ + +// Used in AlterTextSize IPC call. +namespace text_zoom { + enum TextSize { + TEXT_SMALLER = -1, + TEXT_STANDARD = 0, + TEXT_LARGER = 1, + }; +} + +#endif diff --git a/chrome/common/throb_animation.cc b/chrome/common/throb_animation.cc new file mode 100644 index 0000000..acfa485 --- /dev/null +++ b/chrome/common/throb_animation.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 "chrome/common/throb_animation.h" + +static const int kDefaultThrobDurationMS = 400; + +ThrobAnimation::ThrobAnimation(AnimationDelegate* target) + : SlideAnimation(target), + slide_duration_(GetSlideDuration()), + throb_duration_(kDefaultThrobDurationMS), + cycles_remaining_(0), + throbbing_(false) { +} + +void ThrobAnimation::StartThrobbing(int cycles_til_stop) { + cycles_remaining_ = cycles_til_stop; + throbbing_ = true; + SlideAnimation::SetSlideDuration(throb_duration_); + if (IsAnimating()) + return; // We're already running, we'll cycle when current loop finishes. + + if (IsShowing()) + SlideAnimation::Hide(); + else + SlideAnimation::Show(); + cycles_remaining_ = cycles_til_stop; +} + +void ThrobAnimation::Reset() { + ResetForSlide(); + SlideAnimation::Reset(); +} + +void ThrobAnimation::Show() { + ResetForSlide(); + SlideAnimation::Show(); +} + +void ThrobAnimation::Hide() { + ResetForSlide(); + SlideAnimation::Hide(); +} + +void ThrobAnimation::Run() { + SlideAnimation::Run(); + if (!IsAnimating() && throbbing_) { + // Were throbbing a finished a cycle. Start the next cycle unless we're at + // the end of the cycles, in which case we stop. + cycles_remaining_--; + if (IsShowing()) { + // We want to stop hidden, hence this doesn't check cycles_remaining_. + SlideAnimation::Hide(); + } else if (cycles_remaining_ > 0) { + SlideAnimation::Show(); + } else { + // We're done throbbing. + throbbing_ = false; + } + } +} + +void ThrobAnimation::ResetForSlide() { + SlideAnimation::SetSlideDuration(slide_duration_); + cycles_remaining_ = 0; + throbbing_ = false; +} diff --git a/chrome/common/throb_animation.h b/chrome/common/throb_animation.h new file mode 100644 index 0000000..6780088 --- /dev/null +++ b/chrome/common/throb_animation.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 CHROME_COMMON_THROB_ANIMATION_H_ +#define CHROME_COMMON_THROB_ANIMATION_H_ + +#include "chrome/common/slide_animation.h" + +// A subclass of SlideAnimation that can continually slide. All of the Animation +// methods behave like that of SlideAnimation: transition to the next state. +// The StartThrobbing method causes the ThrobAnimation to cycle between hidden +// and shown for a set number of cycles. +// +// A ThrobAnimation has two durations: the duration used when behavior like +// a SlideAnimation, and the duration used when throbbing. +class ThrobAnimation : public SlideAnimation { + public: + explicit ThrobAnimation(AnimationDelegate* target); + virtual ~ThrobAnimation() {} + + // Starts throbbing. cycles_til_stop gives the number of cycles to do before + // stopping. + void StartThrobbing(int cycles_til_stop); + + // Sets the duration of the slide animation when throbbing. + void SetThrobDuration(int duration) { throb_duration_ = duration; } + + // Overridden to reset to the slide duration. + virtual void Reset(); + virtual void Show(); + virtual void Hide(); + + // Overriden to continually throb (assuming we're throbbing). + virtual void Run(); + + // Overridden to maintain the slide duration. + virtual void SetSlideDuration(int duration) { slide_duration_ = duration; } + + private: + // Resets state such that we behave like SlideAnimation. + void ResetForSlide(); + + // Duration of the slide animation. + int slide_duration_; + + // Duration of the slide animation when throbbing. + int throb_duration_; + + // If throbbing, this is the number of cycles left. + int cycles_remaining_; + + // Are we throbbing? + bool throbbing_; + + DISALLOW_EVIL_CONSTRUCTORS(ThrobAnimation); +}; + +#endif // CHROME_COMMON_THROB_ANIMATION_H_ diff --git a/chrome/common/thumbnail_score.cc b/chrome/common/thumbnail_score.cc new file mode 100644 index 0000000..ef63899 --- /dev/null +++ b/chrome/common/thumbnail_score.cc @@ -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. + +#include "chrome/common/thumbnail_score.h" + +#include "base/logging.h" + +const TimeDelta ThumbnailScore::kUpdateThumbnailTime = TimeDelta::FromDays(1); +const double ThumbnailScore::kThumbnailMaximumBoringness = 0.94; +const double ThumbnailScore::kThumbnailDegradePerHour = 0.01; + +// Calculates a numeric score from traits about where a snapshot was +// taken. We store the raw components in the database because I'm sure +// this will evolve and I don't want to break databases. +static int GetThumbnailType(bool good_clipping, bool at_top) { + if (good_clipping && at_top) { + return 0; + } else if (good_clipping && !at_top) { + return 1; + } else if (!good_clipping && at_top) { + return 2; + } else if (!good_clipping && !at_top) { + return 3; + } else { + NOTREACHED(); + return -1; + } +} + +ThumbnailScore::ThumbnailScore() + : boring_score(1.0), + good_clipping(false), + at_top(false), + time_at_snapshot(Time::Now()) { +} + +ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top) + : boring_score(score), + good_clipping(clipping), + at_top(top), + time_at_snapshot(Time::Now()) { +} + +ThumbnailScore::ThumbnailScore(double score, bool clipping, bool top, + const Time& time) + : boring_score(score), + good_clipping(clipping), + at_top(top), + time_at_snapshot(time) { +} + +ThumbnailScore::~ThumbnailScore() { +} + +bool ThumbnailScore::Equals(const ThumbnailScore& rhs) const { + // When testing equality we use ToTimeT() because that's the value + // stuck in the SQL database, so we need to test equivalence with + // that lower resolution. + return boring_score == rhs.boring_score && + good_clipping == rhs.good_clipping && + at_top == rhs.at_top && + time_at_snapshot.ToTimeT() == rhs.time_at_snapshot.ToTimeT(); +} + +bool ShouldReplaceThumbnailWith(const ThumbnailScore& current, + const ThumbnailScore& replacement) { + int current_type = GetThumbnailType(current.good_clipping, current.at_top); + int replacement_type = GetThumbnailType(replacement.good_clipping, + replacement.at_top); + if (replacement_type < current_type) { + // If we have a better class of thumbnail, add it if it meets + // certain minimum boringness. + return replacement.boring_score < + ThumbnailScore::kThumbnailMaximumBoringness; + } else if (replacement_type == current_type) { + if (replacement.boring_score < current.boring_score) { + // If we have a thumbnail that's straight up less boring, use it. + return true; + } + + // Slowly degrade the boring score of the current thumbnail + // so we take thumbnails which are slightly less good: + TimeDelta since_last_thumbnail = + replacement.time_at_snapshot - current.time_at_snapshot; + double degraded_boring_score = current.boring_score + + (since_last_thumbnail.InHours() * + ThumbnailScore::kThumbnailDegradePerHour); + + if (degraded_boring_score > ThumbnailScore::kThumbnailMaximumBoringness) + degraded_boring_score = ThumbnailScore::kThumbnailMaximumBoringness; + if (replacement.boring_score < degraded_boring_score) + return true; + } + + // If the current thumbnail doesn't meet basic boringness + // requirements, but the replacement does, always replace the + // current one even if we're using a worse thumbnail type. + return current.boring_score >= ThumbnailScore::kThumbnailMaximumBoringness && + replacement.boring_score < ThumbnailScore::kThumbnailMaximumBoringness; +} diff --git a/chrome/common/thumbnail_score.h b/chrome/common/thumbnail_score.h new file mode 100644 index 0000000..db2d854 --- /dev/null +++ b/chrome/common/thumbnail_score.h @@ -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. + +#ifndef CHROME_BROWSER_COMMON_THUMBNAIL_SCORE_H__ +#define CHROME_BROWSER_COMMON_THUMBNAIL_SCORE_H__ + +#include "base/time.h" + +// A set of metadata about a Thumbnail. +struct ThumbnailScore { + // Initializes the ThumbnailScore to the absolute worst possible + // values except for time, which is set to Now(). + ThumbnailScore(); + + // Builds a ThumbnailScore with the passed in values, and sets the + // thumbnail generation time to Now(). + ThumbnailScore(double score, bool clipping, bool top); + + // Builds a ThumbnailScore with the passed in values. + ThumbnailScore(double score, bool clipping, bool top, + const Time& time); + ~ThumbnailScore(); + + // Tests for equivalence between two ThumbnailScore objects. + bool Equals(const ThumbnailScore& rhs) const; + + // How "boring" a thumbnail is. The boring score is the 0,1 ranged + // percentage of pixels that are the most common luma. Higher boring + // scores indicate that a higher percentage of a bitmap are all the + // same brightness (most likely the same color). + double boring_score; + + // Whether the thumbnail was taken with height greater then + // width. In cases where we don't have |good_clipping|, the + // thumbnails are either clipped from the horizontal center of the + // window, or are otherwise weirdly stretched. + bool good_clipping; + + // Whether this thumbnail was taken while the renderer was + // displaying the top of the page. Most pages are more recognizable + // by their headers then by a set of random text half way down the + // page; i.e. most MediaWiki sites would be indistinguishable by + // thumbnails with |at_top| set to false. + bool at_top; + + // Record the time when a thumbnail was taken. This is used to make + // sure thumbnails are kept fresh. + Time time_at_snapshot; + + // How bad a thumbnail needs to be before we completely ignore it. + static const double kThumbnailMaximumBoringness; + + // Time before we take a worse thumbnail (subject to + // kThumbnailMaximumBoringness) over what's currently in the database + // for freshness. + static const TimeDelta kUpdateThumbnailTime; + + // Penalty of how much more boring a thumbnail should be per hour. + static const double kThumbnailDegradePerHour; +}; + +// Checks whether we should replace one thumbnail with another. +bool ShouldReplaceThumbnailWith(const ThumbnailScore& current, + const ThumbnailScore& replacement); + +#endif // CHROME_BROWSER_COMMON_THUMBNAIL_SCORE_H__ diff --git a/chrome/common/time_format.cc b/chrome/common/time_format.cc new file mode 100644 index 0000000..1c4b576 --- /dev/null +++ b/chrome/common/time_format.cc @@ -0,0 +1,334 @@ +// 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 "chrome/common/time_format.h" + +#include <vector> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/singleton.h" +#include "base/string_util.h" +#include "base/time.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/stl_util-inl.h" +#include "generated_resources.h" +#include "unicode/datefmt.h" +#include "unicode/locid.h" +#include "unicode/plurfmt.h" +#include "unicode/plurrule.h" +#include "unicode/smpdtfmt.h" + +namespace { + +UDate TimeToUDate(const Time& time) { + return static_cast<UDate>(time.ToDoubleT() * 1000); +} + +std::wstring FormatTime(const DateFormat* formatter, const Time& time) { + DCHECK(formatter); + UnicodeString date_string; + formatter->format(TimeToUDate(time), date_string); + std::wstring formatted; + int capacity = date_string.length() + 1; + + UErrorCode error = U_ZERO_ERROR; + date_string.extract(static_cast<UChar*>(WriteInto(&formatted, capacity)), + capacity, error); + DCHECK(U_SUCCESS(error)); + return formatted; +} + +} // namespace + +class TimeRemainingFormat { + public: + const std::vector<PluralFormat*>& formatter(bool short_version) { + return short_version ? short_formatter_ : long_formatter_; + } + private: + TimeRemainingFormat() { + BuildFormats(true, &short_formatter_); + BuildFormats(false, &long_formatter_); + } + ~TimeRemainingFormat() { + STLDeleteContainerPointers(short_formatter_.begin(), + short_formatter_.end()); + STLDeleteContainerPointers(long_formatter_.begin(), + long_formatter_.end()); + } + friend Singleton<TimeRemainingFormat>; + friend DefaultSingletonTraits<TimeRemainingFormat>; + + std::vector<PluralFormat*> long_formatter_; + std::vector<PluralFormat*> short_formatter_; + static void BuildFormats(bool short_version, + std::vector<PluralFormat*>* time_formats); + static PluralFormat* createFallbackFormat(const PluralRules& rules, + int index, + bool short_version); + + DISALLOW_EVIL_CONSTRUCTORS(TimeRemainingFormat); +}; + +void TimeRemainingFormat::BuildFormats( + bool short_version, + std::vector<PluralFormat*>* time_formats) { + const static int kInvalidMsgId = -1; + const static int kTimeMsgIds[][6] = { + { + IDS_TIME_SECS_DEFAULT, IDS_TIME_SEC_SINGULAR, IDS_TIME_SECS_ZERO, + IDS_TIME_SECS_TWO, IDS_TIME_SECS_FEW, IDS_TIME_SECS_MANY + }, + { + IDS_TIME_MINS_DEFAULT, IDS_TIME_MIN_SINGULAR, kInvalidMsgId, + IDS_TIME_MINS_TWO, IDS_TIME_MINS_FEW, IDS_TIME_MINS_MANY + }, + { + IDS_TIME_HOURS_DEFAULT, IDS_TIME_HOUR_SINGULAR, kInvalidMsgId, + IDS_TIME_HOURS_TWO, IDS_TIME_HOURS_FEW, IDS_TIME_HOURS_MANY + }, + { + IDS_TIME_DAYS_DEFAULT, IDS_TIME_DAY_SINGULAR, kInvalidMsgId, + IDS_TIME_DAYS_TWO, IDS_TIME_DAYS_FEW, IDS_TIME_DAYS_MANY + } + }; + + const static int kTimeLeftMsgIds[][6] = { + { + IDS_TIME_REMAINING_SECS_DEFAULT, IDS_TIME_REMAINING_SEC_SINGULAR, + IDS_TIME_REMAINING_SECS_ZERO, IDS_TIME_REMAINING_SECS_TWO, + IDS_TIME_REMAINING_SECS_FEW, IDS_TIME_REMAINING_SECS_MANY + }, + { + IDS_TIME_REMAINING_MINS_DEFAULT, IDS_TIME_REMAINING_MIN_SINGULAR, + kInvalidMsgId, IDS_TIME_REMAINING_MINS_TWO, + IDS_TIME_REMAINING_MINS_FEW, IDS_TIME_REMAINING_MINS_MANY + }, + { + IDS_TIME_REMAINING_HOURS_DEFAULT, IDS_TIME_REMAINING_HOUR_SINGULAR, + kInvalidMsgId, IDS_TIME_REMAINING_HOURS_TWO, + IDS_TIME_REMAINING_HOURS_FEW, IDS_TIME_REMAINING_HOURS_MANY + }, + { + IDS_TIME_REMAINING_DAYS_DEFAULT, IDS_TIME_REMAINING_DAY_SINGULAR, + kInvalidMsgId, IDS_TIME_REMAINING_DAYS_TWO, + IDS_TIME_REMAINING_DAYS_FEW, IDS_TIME_REMAINING_DAYS_MANY + } + }; + + const static UnicodeString kKeywords[] = { + UNICODE_STRING_SIMPLE("other"), UNICODE_STRING_SIMPLE("one"), + UNICODE_STRING_SIMPLE("zero"), UNICODE_STRING_SIMPLE("two"), + UNICODE_STRING_SIMPLE("few"), UNICODE_STRING_SIMPLE("many") + }; + UErrorCode err = U_ZERO_ERROR; + scoped_ptr<PluralRules> rules( + PluralRules::forLocale(Locale::getDefault(), err)); + if (U_FAILURE(err)) { + err = U_ZERO_ERROR; + UnicodeString fallback_rules("one: n is 1", -1, US_INV); + rules.reset(PluralRules::createRules(fallback_rules, err)); + DCHECK(U_SUCCESS(err)); + } + for (int i = 0; i < 4; ++i) { + UnicodeString pattern; + for (int j = 0; j < arraysize(kKeywords); ++j) { + int msg_id = short_version ? kTimeMsgIds[i][j] : kTimeLeftMsgIds[i][j]; + if (msg_id == kInvalidMsgId) continue; + std::string sub_pattern = WideToUTF8(l10n_util::GetString(msg_id)); + // NA means this keyword is not used in the current locale. + // Even if a translator translated for this keyword, we do not + // use it unless it's 'other' (j=0) or it's defined in the rules + // for the current locale. Special-casing of 'other' will be removed + // once ICU's isKeyword is fixed to return true for isKeyword('other'). + if (sub_pattern.compare("NA") != 0 && + (j == 0 || rules->isKeyword(kKeywords[j]))) { + pattern += kKeywords[j]; + pattern += UNICODE_STRING_SIMPLE("{"); + pattern += UnicodeString(sub_pattern.c_str(), "UTF-8"); + pattern += UNICODE_STRING_SIMPLE("}"); + } + } + PluralFormat* format = new PluralFormat(*rules, pattern, err); + if (U_SUCCESS(err)) { + time_formats->push_back(format); + } else { + delete format; + time_formats->push_back(createFallbackFormat(*rules, i, short_version)); + // Reset it so that next ICU call can proceed. + err = U_ZERO_ERROR; + } + } +} + +// Create a hard-coded fallback plural format. This will never be called +// unless translators make a mistake. +PluralFormat* TimeRemainingFormat::createFallbackFormat( + const PluralRules& rules, + int index, + bool short_version) { + const static UnicodeString kUnits[4][2] = { + { UNICODE_STRING_SIMPLE("sec"), UNICODE_STRING_SIMPLE("secs") }, + { UNICODE_STRING_SIMPLE("min"), UNICODE_STRING_SIMPLE("mins") }, + { UNICODE_STRING_SIMPLE("hour"), UNICODE_STRING_SIMPLE("hours") }, + { UNICODE_STRING_SIMPLE("day"), UNICODE_STRING_SIMPLE("days") } + }; + UnicodeString suffix(short_version ? "}" : " left}", -1, US_INV); + UnicodeString pattern; + if (rules.isKeyword(UNICODE_STRING_SIMPLE("one"))) { + pattern += UNICODE_STRING_SIMPLE("one{# ") + kUnits[index][0] + suffix; + } + pattern += UNICODE_STRING_SIMPLE(" other{# ") + kUnits[index][1] + suffix; + UErrorCode err = U_ZERO_ERROR; + PluralFormat* format = new PluralFormat(rules, pattern, err); + DCHECK(U_SUCCESS(err)); + return format; +} + +Singleton<TimeRemainingFormat> time_remaining_format; + +static std::wstring TimeRemainingImpl(const TimeDelta& delta, + bool short_version) { + if (delta.ToInternalValue() < 0) { + NOTREACHED() << "Negative duration"; + return std::wstring(); + } + + int number; + + const std::vector<PluralFormat*>& formatters = + time_remaining_format->formatter(short_version); + + UErrorCode error = U_ZERO_ERROR; + UnicodeString time_string; + // Less than a minute gets "X seconds left" + if (delta.ToInternalValue() < Time::kMicrosecondsPerMinute) { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerSecond); + time_string = formatters[0]->format(number, error); + + // Less than 1 hour gets "X minutes left". + } else if (delta.ToInternalValue() < Time::kMicrosecondsPerHour) { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerMinute); + time_string = formatters[1]->format(number, error); + + // Less than 1 day remaining gets "X hours left" + } else if (delta.ToInternalValue() < Time::kMicrosecondsPerDay) { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerHour); + time_string = formatters[2]->format(number, error); + + // Anything bigger gets "X days left" + } else { + number = static_cast<int>(delta.ToInternalValue() / + Time::kMicrosecondsPerDay); + time_string = formatters[3]->format(number, error); + } + + // With the fallback added, this should never fail. + DCHECK(U_SUCCESS(error)); + int capacity = time_string.length() + 1; + std::wstring formatted; + time_string.extract(static_cast<UChar*>(WriteInto(&formatted, capacity)), + capacity, error); + DCHECK(U_SUCCESS(error)); + return formatted; +} + +// static +std::wstring TimeFormat::TimeRemaining(const TimeDelta& delta) { + return TimeRemainingImpl(delta, false); +} + +// static +std::wstring TimeFormat::TimeRemainingShort(const TimeDelta& delta) { + return TimeRemainingImpl(delta, true); +} + +// static +std::wstring TimeFormat::FriendlyDay( + const Time& time, + const Time* optional_midnight_today) { + Time midnight_today = optional_midnight_today ? *optional_midnight_today : + Time::Now().LocalMidnight(); + + // Filter out "today" and "yesterday" + if (time >= midnight_today) + return l10n_util::GetString(IDS_PAST_TIME_TODAY); + else if (time >= midnight_today - + TimeDelta::FromMicroseconds(Time::kMicrosecondsPerDay)) + return l10n_util::GetString(IDS_PAST_TIME_YESTERDAY); + + return std::wstring(); +} + +std::wstring TimeFormat::TimeOfDay(const Time& time) { + // We can omit the locale parameter because the default should match + // Chrome's application locale. + scoped_ptr<DateFormat> formatter(DateFormat::createTimeInstance( + DateFormat::kShort)); + return FormatTime(formatter.get(), time); +} + +std::wstring TimeFormat::ShortDate(const Time& time) { + scoped_ptr<DateFormat> formatter(DateFormat::createDateInstance( + DateFormat::kMedium)); + return FormatTime(formatter.get(), time); +} + +std::wstring TimeFormat::ShortDateNumeric(const Time& time) { + scoped_ptr<DateFormat> formatter(DateFormat::createDateInstance( + DateFormat::kShort)); + return FormatTime(formatter.get(), time); +} + +std::wstring TimeFormat::FriendlyDateAndTime(const Time& time) { + scoped_ptr<DateFormat> formatter(DateFormat::createDateTimeInstance( + DateFormat::kFull)); + return FormatTime(formatter.get(), time); +} + +std::wstring TimeFormat::FriendlyDate(const Time& time) { + scoped_ptr<DateFormat> formatter(DateFormat::createDateInstance( + DateFormat::kFull)); + return FormatTime(formatter.get(), time); +} + +std::wstring TimeFormat::CookieExpires(const Time& time) { + UErrorCode error = U_ZERO_ERROR; + SimpleDateFormat simple_date_formatter("EEE, dd-MMM-yyyy HH:mm:ss 'GMT'", + Locale::getEnglish(), error); + if (U_FAILURE(error)) + return std::wstring(); + + simple_date_formatter.adoptTimeZone(TimeZone::getGMT()->clone()); + return FormatTime(&simple_date_formatter, time); +} diff --git a/chrome/common/time_format.h b/chrome/common/time_format.h new file mode 100644 index 0000000..d51f844 --- /dev/null +++ b/chrome/common/time_format.h @@ -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. + +#ifndef CHROME_COMMON_TIME_FORMAT_H__ +#define CHROME_COMMON_TIME_FORMAT_H__ + +// This file defines methods to format time values as strings. + +#include <string> + +#include "unicode/smpdtfmt.h" + +class Time; +class TimeDelta; + +class TimeFormat { + public: + // Returns a localized string of approximate time remaining. The conditions + // are simpler than PastTime since this is used for in-progress operations + // and users have different expectations of units. + // Ex: "3 mins left", "2 days left". + static std::wstring TimeRemaining(const TimeDelta& delta); + + // Same as TimeRemaining without the "left". + static std::wstring TimeRemainingShort(const TimeDelta& delta); + + // For displaying a relative time in the past. This method returns either + // "Today", "Yesterday", or an empty string if it's older than that. + // + // TODO(brettw): This should be able to handle days in the future like + // "Tomorrow". + // TODO(tc): This should be able to do things like "Last week". This + // requires handling singluar/plural for all languages. + // + // The second parameter is optional, it is midnight of "Now" for relative day + // computations: Time::Now().LocalMidnight() + // If NULL, the current day's midnight will be retrieved, which can be + // slow. If many items are being processed, it is best to get the current + // time once at the beginning and pass it for each computation. + static std::wstring FriendlyDay(const Time& time, + const Time* optional_midnight_today); + + // Returns the time of day, e.g., "3:07 PM". + static std::wstring TimeOfDay(const Time& time); + + // Returns a shortened date, e.g. "Nov 7, 2007" + static std::wstring ShortDate(const Time& time); + + // Returns a numeric date such as 12/13/52. + static std::wstring ShortDateNumeric(const Time& time); + + // Formats a time in a friendly sentence format, e.g. + // "Monday, March 6, 2008 2:44:30 PM". + static std::wstring FriendlyDateAndTime(const Time& time); + + // Formats a time in a friendly sentence format, e.g. + // "Monday, March 6, 2008". + static std::wstring FriendlyDate(const Time& time); + + // Returns a time format used in a cookie expires attribute, e.g. + // "Wed, 25-Apr-2007 21:02:13 GMT" + // Its only legal time zone is GMT, and it can be parsed by + // CookieMonster::ParseCookieTime(). + static std::wstring CookieExpires(const Time& time); +}; + +#endif // CHROME_COMMON_TIME_FORMAT_H__ diff --git a/chrome/common/time_format_unittest.cc b/chrome/common/time_format_unittest.cc new file mode 100644 index 0000000..e79ce04 --- /dev/null +++ b/chrome/common/time_format_unittest.cc @@ -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. + +#include <time.h> + +#include "base/basictypes.h" +#include "base/time.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/time_format.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(TimeFormat, FriendlyDay) { + Time now = Time::Now(); + std::wstring today_str = TimeFormat::FriendlyDay(now, NULL); + EXPECT_EQ(L"Today", today_str); + + Time yesterday = now - TimeDelta::FromDays(1); + std::wstring yesterday_str = TimeFormat::FriendlyDay(yesterday, NULL); + EXPECT_EQ(L"Yesterday", yesterday_str); + + Time two_days_ago = now - TimeDelta::FromDays(2); + std::wstring two_days_ago_str = TimeFormat::FriendlyDay(two_days_ago, NULL); + EXPECT_TRUE(two_days_ago_str.empty()); + + Time a_week_ago = now - TimeDelta::FromDays(7); + std::wstring a_week_ago_str = TimeFormat::FriendlyDay(a_week_ago, NULL); + EXPECT_TRUE(a_week_ago_str.empty()); +} + +namespace { +void TestRemainingTime(const TimeDelta delta, + const std::wstring& expected_short, + const std::wstring& expected_long) { + EXPECT_EQ(expected_short, TimeFormat::TimeRemainingShort(delta)); + EXPECT_EQ(expected_long, TimeFormat::TimeRemaining(delta)); +} + +} // namespace + +TEST(TimeFormat, RemainingTime) { + const TimeDelta one_day = TimeDelta::FromDays(1); + const TimeDelta three_days = TimeDelta::FromDays(3); + const TimeDelta one_hour = TimeDelta::FromHours(1); + const TimeDelta four_hours = TimeDelta::FromHours(4); + const TimeDelta one_min = TimeDelta::FromMinutes(1); + const TimeDelta three_mins = TimeDelta::FromMinutes(3); + const TimeDelta one_sec = TimeDelta::FromSeconds(1); + const TimeDelta five_secs = TimeDelta::FromSeconds(5); + const TimeDelta twohundred_millisecs = TimeDelta::FromMilliseconds(200); + + // TODO(jungshik) : These test only pass when the OS locale is 'en'. + // We need to add SetUp() and TearDown() to set the locale to 'en'. + TestRemainingTime(twohundred_millisecs, L"0 secs", L"0 secs left"); + TestRemainingTime(one_sec - twohundred_millisecs, L"0 secs", L"0 secs left"); + TestRemainingTime(one_sec + twohundred_millisecs, L"1 sec", L"1 sec left"); + TestRemainingTime(five_secs + twohundred_millisecs, L"5 secs", L"5 secs left"); + TestRemainingTime(one_min + five_secs, L"1 min", L"1 min left"); + TestRemainingTime(three_mins + twohundred_millisecs, + L"3 mins", L"3 mins left"); + TestRemainingTime(one_hour + five_secs, L"1 hour", L"1 hour left"); + TestRemainingTime(four_hours + five_secs, L"4 hours", L"4 hours left"); + TestRemainingTime(one_day + five_secs, L"1 day", L"1 day left"); + TestRemainingTime(three_days, L"3 days", L"3 days left"); + TestRemainingTime(three_days + four_hours, L"3 days", L"3 days left"); +} + +#if 0 +TEST(TimeFormat, FriendlyDateAndTime) { + Time::Exploded exploded; + exploded.year = 2008; + exploded.month = 3; + exploded.day_of_week = 1; + exploded.day_of_month = 31; + exploded.hour = 14; + exploded.minute = 44; + exploded.second = 30; + exploded.millisecond = 549; + Time t = Time::FromLocalExploded(exploded); + + std::wstring normal_time = L"Monday, March 31, 2008 2:44:30 PM"; + EXPECT_EQ(normal_time, TimeFormat::FriendlyDateAndTime(t)); +} +#endif diff --git a/chrome/common/visitedlink_common.cc b/chrome/common/visitedlink_common.cc new file mode 100644 index 0000000..7395c38 --- /dev/null +++ b/chrome/common/visitedlink_common.cc @@ -0,0 +1,117 @@ +// 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 "chrome/common/visitedlink_common.h" + +#include "base/logging.h" +#include "base/md5.h" + +const VisitedLinkCommon::Fingerprint VisitedLinkCommon::null_fingerprint_ = 0; +const VisitedLinkCommon::Hash VisitedLinkCommon::null_hash_ = -1; + +VisitedLinkCommon::VisitedLinkCommon() : + hash_table_(NULL), + table_length_(0) { +} + +VisitedLinkCommon::~VisitedLinkCommon() { +} + +// FIXME: this uses linear probing, it should be replaced with quadratic +// probing or something better. See VisitedLinkMaster::AddFingerprint +bool VisitedLinkCommon::IsVisited(const char* canonical_url, + size_t url_len) const { + if (url_len == 0) + return false; + if (!hash_table_ || table_length_ == 0) { + // Init() will always create a table, this means somebody forgot + NOTREACHED(); + return false; + } + return IsVisited(ComputeURLFingerprint(canonical_url, url_len, salt_)); +} + +bool VisitedLinkCommon::IsVisited(Fingerprint fingerprint) const { + // Go through the table until we find the item or an empty spot (meaning it + // wasn't found). This loop will terminate as long as the table isn't full, + // which should be enforced by AddFingerprint. + Hash first_hash = HashFingerprint(fingerprint); + Hash cur_hash = first_hash; + while (true) { + Fingerprint cur_fingerprint = FingerprintAt(cur_hash); + if (cur_fingerprint == null_fingerprint_) + return false; // End of probe sequence found. + if (cur_fingerprint == fingerprint) + return true; // Found a match. + + // This spot was taken, but not by the item we're looking for, search in + // the next position. + cur_hash++; + if (cur_hash == table_length_) + cur_hash = 0; + if (cur_hash == first_hash) { + // Wrapped around and didn't find an empty space, this means we're in an + // infinite loop because AddFingerprint didn't do its job resizing. + NOTREACHED(); + return false; + } + } +} + +// Uses the top 64 bits of the MD5 sum of the canonical URL as the fingerprint, +// this is as random as any other subset of the MD5SUM. +// +// FIXME: this uses the MD5SUM of the 16-bit character version. For systems where +// wchar_t is not 16 bits (Linux uses 32 bits, I think), this will not be +// compatable. We should define explicitly what should happen here across +// platforms, and convert if necessary (probably to UTF-16). + +// static +VisitedLinkCommon::Fingerprint VisitedLinkCommon::ComputeURLFingerprint( + const char* canonical_url, + size_t url_len, + const uint8 salt[LINK_SALT_LENGTH]) { + DCHECK(url_len > 0) << "Canonical URLs should not be empty"; + + MD5Context ctx; + MD5Init(&ctx); + MD5Update(&ctx, salt, sizeof(salt)); + MD5Update(&ctx, canonical_url, url_len * sizeof(char)); + + MD5Digest digest; + MD5Final(&digest, &ctx); + + // This is the same as "return *(Fingerprint*)&digest.a;" but if we do that + // direct cast the alignment could be wrong, and we can't access a 64-bit int + // on arbitrary alignment on some processors. This reinterpret_casts it + // down to a char array of the same size as fingerprint, and then does the + // bit cast, which amounts to a memcpy. This does not handle endian issues. + return bit_cast<Fingerprint, uint8[8]>( + *reinterpret_cast<uint8(*)[8]>(&digest.a)); +}
\ No newline at end of file diff --git a/chrome/common/visitedlink_common.h b/chrome/common/visitedlink_common.h new file mode 100644 index 0000000..d7e4c63 --- /dev/null +++ b/chrome/common/visitedlink_common.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 CHROME_COMMON_VISITEDLINK_COMMON_H__ +#define CHROME_COMMON_VISITEDLINK_COMMON_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "googleurl/src/gurl.h" + +// number of bytes in the salt +#define LINK_SALT_LENGTH 8 + +// A multiprocess-safe database of the visited links for the browser. There +// should be exactly one process that has write access (implemented by +// VisitedLinkMaster), while all other processes should be read-only +// (implemented by VisitedLinkSlave). These other processes add links by calling +// the writer process to add them for it. The writer may also notify the readers +// to replace their table when the table is resized. +// +// IPC is not implemented in these classes. This is done through callback +// functions supplied by the creator of these objects to allow more flexibility, +// especially for testing. +// +// This class defines the common base for these others. We implement accessors +// for looking things up in the hash table, and for computing hash values and +// fingerprints. Both the master and the slave inherit from this, and add their +// own code to set up and change these values as their design requires. The +// slave pretty much just sets up the shared memory and saves the pointer. The +// master does a lot of work to manage the table, reading and writing it to and +// from disk, and resizing it when it gets too full. +// +// To ask whether a page is in history, we compute a 64-bit fingerprint of the +// URL. This URL is hashed and we see if it is in the URL hashtable. If it is, +// we consider it visited. Otherwise, it is unvisited. Note that it is possible +// to get collisions, which is the penalty for not storing all URL strings in +// memory (which could get to be more than we want to have in memory). We use +// a salt value for the links on one computer so that an attacker can not +// manually create a link that causes a collision. +class VisitedLinkCommon { + public: + // A number that identifies the URL. + typedef uint64 Fingerprint; + + // A hash value of a fingerprint + typedef int32 Hash; + + // A fingerprint or hash value that does not exist + static const Fingerprint null_fingerprint_; + static const Hash null_hash_; + + VisitedLinkCommon(); + virtual ~VisitedLinkCommon(); + + // Computes the fingerprint of the key and looks it up in the table. We + // return true if found. Does not modify the hastable. The input should be + // the canonical 16-bit URL. + bool IsVisited(const char* canonical_url, size_t url_len) const; + bool IsVisited(const GURL& url) const { + return IsVisited(url.spec().data(), url.spec().size()); + } + +#ifdef UNIT_TEST + // Returns statistics about DB usage + void GetUsageStatistics(int32* table_size, + VisitedLinkCommon::Fingerprint** fingerprints) { + *table_size = table_length_; + *fingerprints = hash_table_; + } +#endif + + protected: + // This structure is at the beginning of the shared memory so that the slaves + // can get stats on the table + struct SharedHeader { + // see goes into table_length_ + uint32 length; + + // goes into salt_ + uint8 salt[LINK_SALT_LENGTH]; + }; + + // Returns the fingerprint at the given index into the URL table. This + // function should be called instead of accessing the table directly to contain + // endian issues. + Fingerprint FingerprintAt(int32 table_offset) const { + DCHECK(hash_table_); + if (!hash_table_) + return 0; + return hash_table_[table_offset]; + } + + // Returns true if the given fingerprint is in the table. + bool IsVisited(Fingerprint fingerprint) const; + + // Computes the fingerprint of the given canonical URL. It is static so the + // same algorithm can be re-used by the table rebuilder, so you will have to + // pass the salt as a parameter. + static Fingerprint ComputeURLFingerprint(const char* canonical_url, + size_t url_len, + const uint8 salt[LINK_SALT_LENGTH]); + + // Computes the hash value of the given fingerprint, this is used as a lookup + // into the hashtable. + static Hash HashFingerprint(Fingerprint fingerprint, int32 table_length) { + return static_cast<Hash>(fingerprint % table_length); + } + Hash HashFingerprint(Fingerprint fingerprint) const { // uses the current hashtable + return HashFingerprint(fingerprint, table_length_); + } + + // pointer to the first item + VisitedLinkCommon::Fingerprint* hash_table_; + + // the number of items in the hash table + int32 table_length_; + + // salt used for each URL when computing the fingerprint + uint8 salt_[LINK_SALT_LENGTH]; + + private: + DISALLOW_EVIL_CONSTRUCTORS(VisitedLinkCommon); +}; + +#endif // WIN_COMMON_VISITEDLINK_COMMON_H__ diff --git a/chrome/common/win_safe_util.cc b/chrome/common/win_safe_util.cc new file mode 100644 index 0000000..0b80301 --- /dev/null +++ b/chrome/common/win_safe_util.cc @@ -0,0 +1,211 @@ +// 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 <shlobj.h> +#include <shobjidl.h> +#include <atlcomcli.h> + +#include "chrome/common/win_safe_util.h" + +#include "base/logging.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/common/win_util.h" + +namespace win_util { + +// This is the COM IAttachmentExecute interface definition. +// In the current Chrome headers it is not present because the _WIN32_IE macro +// is not set at the XPSP2 or IE60 level. We have placed guards to avoid double +// declaration in case we change the _WIN32_IE macro. +#ifndef __IAttachmentExecute_INTERFACE_DEFINED__ +#define __IAttachmentExecute_INTERFACE_DEFINED__ + +typedef +enum tagATTACHMENT_PROMPT +{ ATTACHMENT_PROMPT_NONE = 0, +ATTACHMENT_PROMPT_SAVE = 0x1, +ATTACHMENT_PROMPT_EXEC = 0x2, +ATTACHMENT_PROMPT_EXEC_OR_SAVE = 0x3 +} ATTACHMENT_PROMPT; + +typedef +enum tagATTACHMENT_ACTION +{ ATTACHMENT_ACTION_CANCEL = 0, +ATTACHMENT_ACTION_SAVE = 0x1, +ATTACHMENT_ACTION_EXEC = 0x2 +} ATTACHMENT_ACTION; + +MIDL_INTERFACE("73db1241-1e85-4581-8e4f-a81e1d0f8c57") +IAttachmentExecute : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetClientTitle( + /* [string][in] */ LPCWSTR pszTitle) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetClientGuid( + /* [in] */ REFGUID guid) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetLocalPath( + /* [string][in] */ LPCWSTR pszLocalPath) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetFileName( + /* [string][in] */ LPCWSTR pszFileName) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetSource( + /* [string][in] */ LPCWSTR pszSource) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetReferrer( + /* [string][in] */ LPCWSTR pszReferrer) = 0; + + virtual HRESULT STDMETHODCALLTYPE CheckPolicy( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Prompt( + /* [in] */ HWND hwnd, + /* [in] */ ATTACHMENT_PROMPT prompt, + /* [out] */ ATTACHMENT_ACTION *paction) = 0; + + virtual HRESULT STDMETHODCALLTYPE Save( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Execute( + /* [in] */ HWND hwnd, + /* [string][in] */ LPCWSTR pszVerb, + HANDLE *phProcess) = 0; + + virtual HRESULT STDMETHODCALLTYPE SaveWithUI( + HWND hwnd) = 0; + + virtual HRESULT STDMETHODCALLTYPE ClearClientState( void) = 0; + +}; + +#endif // __IAttachmentExecute_INTERFACE_DEFINED__ + +// This function implementation is based on the attachment execution +// services functionally deployed with IE6 or Service pack 2. This +// functionality is exposed in the IAttachmentExecute COM interface. +// more information at: +// http://msdn2.microsoft.com/en-us/library/ms647048.aspx +bool SaferOpenItemViaShell(HWND hwnd, const std::wstring& window_title, + const std::wstring& full_path, + const std::wstring& source_url, + bool ask_for_app) { + ATL::CComPtr<IAttachmentExecute> attachment_services; + HRESULT hr = attachment_services.CoCreateInstance(CLSID_AttachmentServices); + if (FAILED(hr)) { + // We don't have Attachment Execution Services, it must be a pre-XP.SP2 + // Windows installation, or the thread does not have COM initialized. + if (hr == CO_E_NOTINITIALIZED) { + NOTREACHED(); + return false; + } + return OpenItemViaShell(full_path, ask_for_app); + } + + // This GUID is associated with any 'don't ask me again' settings that the + // user can select for different file types. + // {2676A9A2-D919-4fee-9187-152100393AB2} + static const GUID kClientID = { 0x2676a9a2, 0xd919, 0x4fee, + { 0x91, 0x87, 0x15, 0x21, 0x0, 0x39, 0x3a, 0xb2 } }; + + attachment_services->SetClientGuid(kClientID); + + if (!window_title.empty()) + attachment_services->SetClientTitle(window_title.c_str()); + + // To help windows decide if the downloaded file is dangerous we can provide + // what the documentation calls evidence. Which we provide now: + // + // Set the file itself as evidence. + hr = attachment_services->SetLocalPath(full_path.c_str()); + if (FAILED(hr)) + return false; + // Set the origin URL as evidence. + hr = attachment_services->SetSource(source_url.c_str()); + if (FAILED(hr)) + return false; + + // Now check the windows policy. + bool do_prompt; + hr = attachment_services->CheckPolicy(); + if (S_FALSE == hr) { + // The user prompt is required. + do_prompt = true; + } else if (S_OK == hr) { + // An S_OK means that the file is safe to open without user consent. + do_prompt = false; + } else { + // It is possible that the last call returns an undocumented result + // equal to 0x800c000e which seems to indicate that the URL failed the + // the security check. If you proceed with the Prompt() call the + // Shell might show a dialog that says: + // "windows found that this file is potentially harmful. To help protect + // your computer, Windows has blocked access to this file." + // Upon dismissal of the dialog windows will delete the file (!!). + // So, we can 'return' here but maybe is best to let it happen to fail on + // the safe side. + } + if (do_prompt) { + ATTACHMENT_ACTION action; + // We cannot control what the prompt says or does directly but it + // is a pretty decent dialog; for example, if an excutable is signed it can + // decode and show the publisher and the certificate. + hr = attachment_services->Prompt(hwnd, ATTACHMENT_PROMPT_EXEC, &action); + if (FAILED(hr) || (ATTACHMENT_ACTION_CANCEL == action)) + { + // The user has declined opening the item. + return false; + } + } + return OpenItemViaShellNoZoneCheck(full_path, ask_for_app); +} + +bool SetInternetZoneIdentifier(const std::wstring& full_path) { + const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + std::wstring path = full_path + L":Zone.Identifier"; + HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, kShare, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == file) + return false; + + const char kIdentifier[] = "[ZoneTransfer]\nZoneId=3"; + DWORD written = 0; + BOOL result = WriteFile(file, kIdentifier, arraysize(kIdentifier), &written, + NULL); + CloseHandle(file); + + if (!result || written != arraysize(kIdentifier)) { + DCHECK(FALSE); + return false; + } + + return true; +} + +} // namespace win_util diff --git a/chrome/common/win_safe_util.h b/chrome/common/win_safe_util.h new file mode 100644 index 0000000..2df030c --- /dev/null +++ b/chrome/common/win_safe_util.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 CHROME_COMMON_WIN_SAFE_UTIL_H__ +#define CHROME_COMMON_WIN_SAFE_UTIL_H__ + +#include <string> +#include <windows.h> + +namespace win_util { + +// Open or run a downloaded file via the Windows shell, possibly showing first +// a consent dialog if the the file is deemed dangerous. This function is an +// enhancement over the OpenItemViaShell() function of win_util.h. +// +// The user consent dialog will be shown or not according to the windows +// execution policy defined in the registry which can be overridden per user. +// The mechanics of the policy are explained in the Microsoft Knowledge base +// number 883260: http://support.microsoft.com/kb/883260 +// +// The 'hwnd' is the handle to the parent window. In case a dialog is displayed +// the parent window will be disabled since the dialog is meant to be modal. +// The 'window_title' is the text displayed on the title bar of the dialog. If +// you pass an empty string the dialog will have a generic 'windows security' +// name on the title bar. +// +// You must provide a valid 'full_path' to the file to be opened and a well +// formed url in 'source_url'. The url should identify the source of the file +// but does not have to be network-reachable. If the url is malformed a +// dialog will be shown telling the user that the file will be blocked. +// +// In the event that there is no default application registered for the file +// specified by 'full_path' it ask the user, via the Windows "Open With" +// dialog, for an application to use if 'ask_for_app' is true. +// Returns 'true' on successful open, 'false' otherwise. +bool SaferOpenItemViaShell(HWND hwnd, const std::wstring& window_title, + const std::wstring& full_path, + const std::wstring& source_url, bool ask_for_app); + +// Sets the Zone Identifier on the file to "Internet" (3). Returns true if the +// function succeeds, false otherwise. A failure is expected on system where +// the Zone Identifier is not supported, like a machine with a FAT32 filesystem. +// It should not be considered fatal. +bool SetInternetZoneIdentifier(const std::wstring& full_path); + +} // namespace win_util + +#endif // CHROME_COMMON_WIN_SAFE_UTIL_H__ diff --git a/chrome/common/win_util.cc b/chrome/common/win_util.cc new file mode 100644 index 0000000..8629b3ca --- /dev/null +++ b/chrome/common/win_util.cc @@ -0,0 +1,844 @@ +// 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 "chrome/common/win_util.h" + +#include <atlbase.h> +#include <atlapp.h> +#include <commdlg.h> +#include <dwmapi.h> +#include <shellapi.h> +#include <shlobj.h> + +#include "base/file_util.h" +#include "base/gfx/bitmap_header.h" +#include "base/gfx/png_encoder.h" +#include "base/logging.h" +#include "base/registry.h" +#include "base/scoped_handle.h" +#include "base/string_util.h" +#include "base/win_util.h" +#include "chrome/common/l10n_util.h" +#include "generated_resources.h" + +// Ensure that we pick up this link library. +#pragma comment(lib, "dwmapi.lib") + +namespace win_util { + +namespace { + +// Enforce visible dialog box. +UINT_PTR CALLBACK SaveAsDialogHook(HWND dialog, UINT message, + WPARAM wparam, LPARAM lparam) { + static const UINT kPrivateMessage = 0x2F3F; + switch (message) { + case WM_INITDIALOG: { + // Do nothing here. Just post a message to defer actual processing. + PostMessage(dialog, kPrivateMessage, 0, 0); + return TRUE; + } + case kPrivateMessage: { + // The dialog box is the parent of the current handle. + HWND real_dialog = GetParent(dialog); + + // Retrieve the final size. + RECT dialog_rect; + GetWindowRect(real_dialog, &dialog_rect); + + // Verify that the upper left corner is visible. + POINT point = { dialog_rect.left, dialog_rect.top }; + HMONITOR monitor1 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); + point.x = dialog_rect.right; + point.y = dialog_rect.bottom; + + // Verify that the lower right corner is visible. + HMONITOR monitor2 = MonitorFromPoint(point, MONITOR_DEFAULTTONULL); + if (monitor1 && monitor2) + return 0; + + // Some part of the dialog box is not visible, fix it by moving is to the + // client rect position of the browser window. + HWND parent_window = GetParent(real_dialog); + if (!parent_window) + return 0; + WINDOWINFO parent_info; + parent_info.cbSize = sizeof(WINDOWINFO); + GetWindowInfo(parent_window, &parent_info); + SetWindowPos(real_dialog, NULL, + parent_info.rcClient.left, + parent_info.rcClient.top, + 0, 0, // Size. + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | + SWP_NOZORDER); + + return 0; + } + } + return 0; +} + +} // namespace + +std::wstring FormatSystemTime(const SYSTEMTIME& time, + const std::wstring& format) { + // If the format string is empty, just use the default format. + LPCTSTR format_ptr = NULL; + if (!format.empty()) + format_ptr = format.c_str(); + + int buffer_size = GetTimeFormat(LOCALE_USER_DEFAULT, NULL, &time, format_ptr, + NULL, 0); + + std::wstring output; + GetTimeFormat(LOCALE_USER_DEFAULT, NULL, &time, format_ptr, + WriteInto(&output, buffer_size), buffer_size); + + return output; +} + +std::wstring FormatSystemDate(const SYSTEMTIME& date, + const std::wstring& format) { + // If the format string is empty, just use the default format. + LPCTSTR format_ptr = NULL; + if (!format.empty()) + format_ptr = format.c_str(); + + int buffer_size = GetDateFormat(LOCALE_USER_DEFAULT, NULL, &date, format_ptr, + NULL, 0); + + std::wstring output; + GetDateFormat(LOCALE_USER_DEFAULT, NULL, &date, format_ptr, + WriteInto(&output, buffer_size), buffer_size); + + return output; +} + +bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path) { + wchar_t long_path_buf[MAX_PATH]; + DWORD return_value = GetLongPathName(short_path.c_str(), long_path_buf, + MAX_PATH); + if (return_value != 0 && return_value < MAX_PATH) { + *long_path = long_path_buf; + return true; + } + + return false; +} + +bool IsDoubleClick(const POINT& origin, + const POINT& current, + DWORD elapsed_time) { + // The CXDOUBLECLK and CYDOUBLECLK system metrics describe the width and + // height of a rectangle around the origin position, inside of which clicks + // within the double click time are considered double clicks. + return (elapsed_time <= GetDoubleClickTime()) && + (abs(current.x - origin.x) <= (GetSystemMetrics(SM_CXDOUBLECLK) / 2)) && + (abs(current.y - origin.y) <= (GetSystemMetrics(SM_CYDOUBLECLK) / 2)); +} + +bool IsDrag(const POINT& origin, const POINT& current) { + // The CXDRAG and CYDRAG system metrics describe the width and height of a + // rectangle around the origin position, inside of which motion is not + // considered a drag. + return (abs(current.x - origin.x) > (GetSystemMetrics(SM_CXDRAG) / 2)) || + (abs(current.y - origin.y) > (GetSystemMetrics(SM_CYDRAG) / 2)); +} + +bool ShouldUseVistaFrame() { + if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) + return false; + // If composition is not enabled, we behave like on XP. + BOOL f; + DwmIsCompositionEnabled(&f); + return !!f; +} + +std::wstring FormatMessage(unsigned messageid) { + wchar_t * string_buffer = NULL; + unsigned string_length = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + messageid, 0, + reinterpret_cast<wchar_t *>(&string_buffer), + 0, NULL); + + std::wstring formatted_string; + if (string_buffer) { + formatted_string = string_buffer; + LocalFree(reinterpret_cast<HLOCAL>(string_buffer)); + } else { + // The formating failed. simply convert the message value into a string. + SStringPrintf(&formatted_string, L"message number %d", messageid); + } + return formatted_string; +} + +std::wstring FormatLastWin32Error() { + return FormatMessage(GetLastError()); +} + +void ShowItemInFolder(const std::wstring& full_path) { + std::wstring dir = file_util::GetDirectoryFromPath(full_path); + if (dir == L"" || !file_util::PathExists(full_path)) + return; + + typedef HRESULT (WINAPI *SHOpenFolderAndSelectItemsFuncPtr)( + PCIDLIST_ABSOLUTE pidl_Folder, + UINT cidl, + PCUITEMID_CHILD_ARRAY pidls, + DWORD flags); + + static SHOpenFolderAndSelectItemsFuncPtr open_folder_and_select_itemsPtr = NULL; + static bool initialize_open_folder_proc = true; + if (initialize_open_folder_proc) { + initialize_open_folder_proc = false; + // The SHOpenFolderAndSelectItems API is exposed by shell32 version 6 + // and does not exist in Win2K. We attempt to retrieve this function export + // from shell32 and if it does not exist, we just invoke ShellExecute to + // open the folder thus losing the functionality to select the item in + // the process. + HMODULE shell32_base = GetModuleHandle(L"shell32.dll"); + if (!shell32_base) { + NOTREACHED(); + return; + } + open_folder_and_select_itemsPtr = + reinterpret_cast<SHOpenFolderAndSelectItemsFuncPtr> + (GetProcAddress(shell32_base, "SHOpenFolderAndSelectItems")); + } + if (!open_folder_and_select_itemsPtr) { + ShellExecute(NULL, _T("open"), dir.c_str(), NULL, NULL, SW_SHOW); + return; + } + + CComPtr<IShellFolder> desktop; + HRESULT hr = SHGetDesktopFolder(&desktop); + if (FAILED(hr)) + return; + + CoMemReleaser<ITEMIDLIST> dir_item; + hr = desktop->ParseDisplayName(NULL, NULL, + const_cast<wchar_t *>(dir.c_str()), + NULL, &dir_item, NULL); + if (FAILED(hr)) + return; + + CoMemReleaser<ITEMIDLIST> file_item; + hr = desktop->ParseDisplayName(NULL, NULL, + const_cast<wchar_t *>(full_path.c_str()), + NULL, &file_item, NULL); + if (FAILED(hr)) + return; + + const ITEMIDLIST* highlight[] = { + {file_item}, + }; + (*open_folder_and_select_itemsPtr)(dir_item, arraysize(highlight), + highlight, NULL); +} + +// Open an item via a shell execute command. Error code checking and casting +// explanation: http://msdn2.microsoft.com/en-us/library/ms647732.aspx +bool OpenItemViaShell(const std::wstring& full_path, bool ask_for_app) { + HINSTANCE h = ::ShellExecuteW(NULL, NULL, full_path.c_str(), NULL, + NULL, SW_SHOWNORMAL); + LONG_PTR error = reinterpret_cast<LONG_PTR>(h); + if (error > 32) + return true; + + if ((error == SE_ERR_NOASSOC) && ask_for_app) + return OpenItemWithExternalApp(full_path); + + return false; +} + +bool OpenItemViaShellNoZoneCheck(const std::wstring& full_path, + bool ask_for_app) { + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_NOZONECHECKS | SEE_MASK_FLAG_DDEWAIT; + sei.nShow = SW_SHOWNORMAL; + sei.lpVerb = NULL; + sei.lpFile = full_path.c_str(); + if (::ShellExecuteExW(&sei)) + return true; + LONG_PTR error = reinterpret_cast<LONG_PTR>(sei.hInstApp); + if ((error == SE_ERR_NOASSOC) && ask_for_app) + return OpenItemWithExternalApp(full_path); + return false; +} + +// Show the Windows "Open With" dialog box to ask the user to pick an app to +// open the file with. +bool OpenItemWithExternalApp(const std::wstring& full_path) { + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_FLAG_DDEWAIT; + sei.nShow = SW_SHOWNORMAL; + sei.lpVerb = L"openas"; + sei.lpFile = full_path.c_str(); + return (TRUE == ::ShellExecuteExW(&sei)); +} + +// Get the file type description from the registry. This will be "Text Document" +// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't +// have an entry for the file type, we return false, true if the description was +// found. 'file_ext' must be in form ".txt". +static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext, + std::wstring* reg_description) { + DCHECK(reg_description); + RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ); + std::wstring reg_app; + if (reg_ext.ReadValue(NULL, ®_app) && !reg_app.empty()) { + RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ); + if (reg_link.ReadValue(NULL, reg_description)) + return true; + } + return false; +} + +// Set up a filter for a "Save As" dialog, which will consist of 'file_ext' file +// extension, 'ext_desc' as the text description of the 'file_ext' type, and +// (optionally) the default 'All Files' view. The purpose of the filter is to +// show only files of a particular type in a Windows "Save As" dialog box. The +// resulting filter is stored in 'buffer', which is a vector since multiple +// NULLs are embedded. The filters created here are: +// 1. only files that have 'file_ext' as their extension +// 2. all files (only added if 'include_all_files' is true) +// Example: +// file_ext: ".txt" +// ext_desc: "Text Document" +// returned (in buffer): "Text Document\0*.txt\0All Files\0*.*\0\0" +// This is painful to build, as you will soon see. +static void FormatSaveAsFilterForExtension(const std::wstring& file_ext, + const std::wstring& ext_desc, + bool include_all_files, + std::vector<wchar_t>* buffer) { + DCHECK(buffer); + + // Force something reasonable to appear in the dialog box if there is no + // description provided. + if (file_ext.empty() || ext_desc.empty()) + include_all_files = true; + + size_t size; + size_t offset = 0; + const std::wstring all_ext = L"*.*"; + const std::wstring all_desc = l10n_util::GetString(IDS_SAVEAS_ALL_FILES); + + // Includes 2 internal NULLs + "*". + const size_t ext_size = ext_desc.length() + file_ext.length() + 3; + // Includes 2 internal NULLs. + const size_t all_size = all_desc.length() + all_ext.length() + 2; + // Includes double terminating NULL. + const size_t buf_size = (!ext_desc.empty() ? ext_size : 0) + + (include_all_files ? all_size : 0) + 1; + buffer->resize(buf_size); + + if (!file_ext.empty() && !ext_desc.empty()) { + // Copy in the text description ("JPEG Image") + NULL. + size = ext_desc.length() + 1; + memcpy(&(*buffer)[offset], ext_desc.c_str(), size * sizeof(wchar_t)); + offset += size; + + // Copy in the file type ("*.jpg") + NULL. + const std::wstring wildcard_ext = L"*" + file_ext; + size = wildcard_ext.length() + 1; + memcpy(&(*buffer)[offset], wildcard_ext.c_str(), size * sizeof(wchar_t)); + offset += size; + } + + if (include_all_files) { + // Copy in the default description ("All Files") + NULL. + size = all_desc.length() + 1; + memcpy(&(*buffer)[offset], all_desc.c_str(), size * sizeof(wchar_t)); + offset += size; + + // Copy in the default file extension ("*.*") + NULL. + size = all_ext.length() + 1; + memcpy(&(*buffer)[offset], all_ext.c_str(), size * sizeof(wchar_t)); + offset += size; + } + + (*buffer)[offset] = L'\0'; // Double NULL required. +} + +bool SaveFileAs(HWND owner, + const std::wstring& suggested_name, + std::wstring* final_name) { + std::wstring reg_description; + std::wstring file_ext = file_util::GetFileExtensionFromPath(suggested_name); + if (!file_ext.empty()) { + file_ext = L"." + file_ext; + GetRegistryDescriptionFromExtension(file_ext, ®_description); + } + + std::vector<wchar_t> filter; + FormatSaveAsFilterForExtension(file_ext, reg_description, true, &filter); + + unsigned index = 1; + return SaveFileAsWithFilter(owner, + suggested_name, + &filter[0], + L"", + &index, + final_name); +} + +bool SaveFileAsWithFilter(HWND owner, + const std::wstring& suggested_name, + const wchar_t* filter, + const std::wstring& def_ext, + unsigned* index, + std::wstring* final_name) { + DCHECK(final_name); + + // Initially populated by the file component of 'suggested_name', this buffer + // will be written into by Windows when the user is done with the dialog box. + wchar_t file_name[MAX_PATH+1]; + std::wstring file_part = file_util::GetFilenameFromPath(suggested_name); + memcpy(file_name, + file_part.c_str(), + (file_part.length()+1) * sizeof(wchar_t)); + + OPENFILENAME save_as; + // We must do this otherwise the ofn's FlagsEx may be initialized to random + // junk in release builds which can cause the Places Bar not to show up! + ZeroMemory(&save_as, sizeof(save_as)); + save_as.lStructSize = sizeof(OPENFILENAME); + save_as.hwndOwner = owner; + save_as.hInstance = NULL; + + save_as.lpstrFilter = &filter[0]; + + save_as.lpstrCustomFilter = NULL; + save_as.nMaxCustFilter = 0; + save_as.nFilterIndex = *index; + save_as.lpstrFile = file_name; + save_as.nMaxFile = arraysize(file_name); + save_as.lpstrFileTitle = NULL; + save_as.nMaxFileTitle = 0; + + // Set up the initial directory for the dialog. + std::wstring directory = file_util::GetDirectoryFromPath(suggested_name); + save_as.lpstrInitialDir = directory.c_str(); + save_as.lpstrTitle = NULL; + save_as.Flags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING | + OFN_ENABLEHOOK | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST; + save_as.lpstrDefExt = &def_ext[0]; + save_as.lCustData = NULL; + save_as.lpfnHook = &SaveAsDialogHook; + + // Must be NULL or 0. + save_as.pvReserved = NULL; + save_as.dwReserved = 0; + + if (!GetSaveFileName(&save_as)) + return false; + + // Return the user's choice. + final_name->assign(save_as.lpstrFile); + *index = save_as.nFilterIndex; + + std::wstring file_ext = file_util::GetFileExtensionFromPath(suggested_name); + if (save_as.nFileExtension == 0) { + // No extension is specified. Append the default extension. + final_name->append(file_ext); + } else if (save_as.nFileExtension == wcslen(save_as.lpstrFile)) { + // The path ends with a ".". This is not supported on windows and since + // we don't use a windows API to create the file, it will make the file + // impossible to open. + final_name->resize(final_name->size() - 1); + } + + return true; +} + +// Adjust the window to fit, returning true if the window was resized or moved. +static bool AdjustWindowToFit(HWND hwnd, const RECT& bounds) { + // Get the monitor. + HMONITOR hmon = MonitorFromRect(&bounds, MONITOR_DEFAULTTONEAREST); + if (!hmon) { + NOTREACHED() << "Unable to find default monitor"; + // No monitor available. + return false; + } + + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hmon, &mi); + gfx::Rect window_rect(bounds); + gfx::Rect monitor_rect(mi.rcWork); + gfx::Rect new_window_rect = window_rect.AdjustToFit(monitor_rect); + if (!new_window_rect.Equals(window_rect)) { + // Window doesn't fit on monitor, move and possibly resize. + SetWindowPos(hwnd, 0, new_window_rect.x(), new_window_rect.y(), + new_window_rect.width(), new_window_rect.height(), + SWP_NOACTIVATE | SWP_NOZORDER); + return true; + } else { + return false; + } +} + +void AdjustWindowToFit(HWND hwnd) { + // Get the window bounds. + RECT r; + GetWindowRect(hwnd, &r); + AdjustWindowToFit(hwnd, r); +} + +void CenterAndSizeWindow(HWND parent, HWND window, const SIZE& pref, + bool pref_is_client) { + DCHECK(window && pref.cx > 0 && pref.cy > 0); + // Calculate the ideal bounds. + RECT window_bounds; + RECT center_bounds = {0}; + if (parent) { + // If there is a parent, center over the parents bounds. + ::GetWindowRect(parent, ¢er_bounds); + } else { + // No parent. Center over the monitor the window is on. + HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST); + if (monitor) { + MONITORINFO mi = {0}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(monitor, &mi); + center_bounds = mi.rcWork; + } else { + NOTREACHED() << "Unable to get default monitor"; + } + } + window_bounds.left = center_bounds.left + + (center_bounds.right - center_bounds.left - pref.cx) / 2; + window_bounds.right = window_bounds.left + pref.cx; + window_bounds.top = center_bounds.top + + (center_bounds.bottom - center_bounds.top - pref.cy) / 2; + window_bounds.bottom = window_bounds.top + pref.cy; + + // If we're centering a child window, we are positioning in client + // coordinates, and as such we need to offset the target rectangle by the + // position of the parent window. + if (::GetWindowLong(window, GWL_STYLE) & WS_CHILD) { + DCHECK(parent && ::GetParent(window) == parent); + POINT topleft = { window_bounds.left, window_bounds.top }; + ::MapWindowPoints(HWND_DESKTOP, parent, &topleft, 1); + window_bounds.left = topleft.x; + window_bounds.top = topleft.y; + window_bounds.right = window_bounds.left + pref.cx; + window_bounds.bottom = window_bounds.top + pref.cy; + } + + // Get the WINDOWINFO for window. We need the style to calculate the size + // for the window. + WINDOWINFO win_info = {0}; + win_info.cbSize = sizeof(WINDOWINFO); + GetWindowInfo(window, &win_info); + + // Calculate the window size needed for the content size. + + if (!pref_is_client || + AdjustWindowRectEx(&window_bounds, win_info.dwStyle, FALSE, + win_info.dwExStyle)) { + if (!AdjustWindowToFit(window, window_bounds)) { + // The window fits, reset the bounds. + SetWindowPos(window, 0, window_bounds.left, window_bounds.top, + window_bounds.right - window_bounds.left, + window_bounds.bottom - window_bounds.top, + SWP_NOACTIVATE | SWP_NOZORDER); + } // else case, AdjustWindowToFit set the bounds for us. + } else { + NOTREACHED() << "Unable to adjust window to fit"; + } +} + +HANDLE GetSectionFromProcess(HANDLE section, HANDLE process, bool read_only) { + HANDLE valid_section = NULL; + DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ; + if (!read_only) + access |= FILE_MAP_WRITE; + DuplicateHandle(process, section, GetCurrentProcess(), &valid_section, access, + FALSE, 0); + return valid_section; +} + +bool DoesWindowBelongToActiveWindow(HWND window) { + DCHECK(window); + HWND top_window = ::GetAncestor(window, GA_ROOT); + if (!top_window) + return false; + + HWND active_top_window = ::GetAncestor(::GetForegroundWindow(), GA_ROOT); + return (top_window == active_top_window); +} + +void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, + gfx::Rect* child_rect, + int padding) { + DCHECK(child_rect); + + // We use padding here because it allows some of the original web page to + // bleed through around the edges. + int twice_padding = padding * 2; + + // FIRST, clamp width and height so we don't open child windows larger than + // the containing parent. + if (child_rect->width() > (parent_rect.width() + twice_padding)) + child_rect->set_width(std::max(0, parent_rect.width() - twice_padding)); + if (child_rect->height() > parent_rect.height() + twice_padding) + child_rect->set_height(std::max(0, parent_rect.height() - twice_padding)); + + // SECOND, clamp x,y position to padding,padding so we don't position child + // windows in hyperspace. + if (child_rect->x() < 0 || child_rect->x() > parent_rect.width()) + child_rect->set_x(padding); + if (child_rect->y() < 0 || child_rect->y() > parent_rect.height()) + child_rect->set_y(padding); + + // LAST, nudge the window back up into the client area if its x,y position is + // within the parent bounds but its width/height place it off-screen. + if (child_rect->bottom() > parent_rect.bottom()) + child_rect->set_y(parent_rect.bottom() - child_rect->height() - padding); + if (child_rect->right() > parent_rect.right()) + child_rect->set_x(parent_rect.right() - child_rect->width() - padding); +} + +void SetChildBounds(HWND child_window, HWND parent_window, + HWND insert_after_window, const gfx::Rect& bounds, + int padding, unsigned long flags) { + DCHECK(IsWindow(child_window)); + + // First figure out the bounds of the parent. + RECT parent_rect = {0}; + if (parent_window) { + GetClientRect(parent_window, &parent_rect); + } else { + // If there is no parent, we consider the bounds of the monitor the window + // is on to be the parent bounds. + + // If the child_window isn't visible yet and we've been given a valid, + // visible insert after window, use that window to locate the correct + // monitor instead. + HWND window = child_window; + if (!IsWindowVisible(window) && IsWindow(insert_after_window) && + IsWindowVisible(insert_after_window)) + window = insert_after_window; + + HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST); + if (monitor) { + MONITORINFO mi = {0}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(monitor, &mi); + parent_rect = mi.rcWork; + } else { + NOTREACHED() << "Unable to get default monitor"; + } + } + + gfx::Rect actual_bounds = bounds; + EnsureRectIsVisibleInRect(gfx::Rect(parent_rect), &actual_bounds, padding); + + SetWindowPos(child_window, insert_after_window, actual_bounds.x(), + actual_bounds.y(), actual_bounds.width(), + actual_bounds.height(), flags); +} + +gfx::Rect GetMonitorBoundsForRect(const gfx::Rect& rect) { + RECT p_rect = rect.ToRECT(); + HMONITOR monitor = MonitorFromRect(&p_rect, MONITOR_DEFAULTTONEAREST); + if (monitor) { + MONITORINFO mi = {0}; + mi.cbSize = sizeof(mi); + GetMonitorInfo(monitor, &mi); + return gfx::Rect(mi.rcWork); + } + NOTREACHED(); + return gfx::Rect(); +} + +bool IsNumPadDigit(int key_code, bool extended_key) { + if (key_code >= VK_NUMPAD0 && key_code <= VK_NUMPAD9) + return true; + + // Check for num pad keys without Num Lock. + // Note: there is no easy way to know if a the key that was pressed comes from + // the num pad or the rest of the keyboard. Investigating how + // TranslateMessage() generates the WM_KEYCHAR from an + // ALT + <numpad sequences> it appears it looks at the extended key flag + // (which is on if the key pressed comes from one of the 3 clusters to + // the left of the numeric keypad). So we use it as well. + return !extended_key && + ((key_code >= VK_PRIOR && key_code <= VK_DOWN) || // All keys but 5 + // and 0. + (key_code == VK_CLEAR) || // Key 5. + (key_code == VK_INSERT)); // Key 0. +} + +void GrabWindowSnapshot(HWND window_handle, + std::vector<unsigned char>* png_representation) { + // Create a memory DC that's compatible with the window. + CWindowDC window_hdc(window_handle); + CDC mem_hdc(::CreateCompatibleDC(window_hdc)); + + // Create a DIB that's the same size as the window. + RECT content_rect = {0, 0, 0, 0}; + ::GetWindowRect(window_handle, &content_rect); + content_rect.right++; // Match what PrintWindow wants. + int width = content_rect.right - content_rect.left; + int height = content_rect.bottom - content_rect.top; + BITMAPINFOHEADER hdr; + gfx::CreateBitmapHeader(width, height, &hdr); + unsigned char *bit_ptr = NULL; + CBitmap bitmap(::CreateDIBSection(mem_hdc, + reinterpret_cast<BITMAPINFO*>(&hdr), + DIB_RGB_COLORS, + reinterpret_cast<void **>(&bit_ptr), + NULL, 0)); + + mem_hdc.SelectBitmap(bitmap); + // Clear the bitmap to white (so that rounded corners on windows + // show up on a white background, and strangely-shaped windows + // look reasonable). Not capturing an alpha mask saves a + // bit of space. + mem_hdc.PatBlt(0, 0, width, height, WHITENESS); + // Grab a copy of the window + // First, see if PrintWindow is defined (it's not in Windows 2000). + typedef BOOL (WINAPI *PrintWindowPointer)(HWND, HDC, UINT); + PrintWindowPointer print_window = + reinterpret_cast<PrintWindowPointer>( + GetProcAddress(GetModuleHandle(L"User32.dll"), "PrintWindow")); + + // If PrintWindow is defined, use it. It will work on partially + // obscured windows, and works better for out of process sub-windows. + // Otherwise grab the bits we can get with BitBlt; it's better + // than nothing and will work fine in the average case (window is + // completely on screen). + if (print_window) + (*print_window)(window_handle, mem_hdc, 0); + else + mem_hdc.BitBlt(0, 0, width, height, window_hdc, 0, 0, SRCCOPY); + + // We now have a copy of the window contents in a DIB, so + // encode it into a useful format for posting to the bug report + // server. + PNGEncoder::Encode(bit_ptr, PNGEncoder::FORMAT_BGRA, + width, height, width * 4, true, + png_representation); +} + +bool IsWindowActive(HWND hwnd) { + WINDOWINFO info; + return ::GetWindowInfo(hwnd, &info) && + ((info.dwWindowStatus & WS_ACTIVECAPTION) != 0); +} + +bool IsReservedName(const std::wstring& filename) { + // This list is taken from the MSDN article "Naming a file" + // http://msdn2.microsoft.com/en-us/library/aa365247(VS.85).aspx + // I also added clock$ because GetSaveFileName seems to consider it as a + // reserved name too. + static const wchar_t* const known_devices[] = { + L"con", L"prn", L"aux", L"nul", L"com1", L"com2", L"com3", L"com4", L"com5", + L"com6", L"com7", L"com8", L"com9", L"lpt1", L"lpt2", L"lpt3", L"lpt4", + L"lpt5", L"lpt6", L"lpt7", L"lpt8", L"lpt9", L"clock$" + }; + std::wstring filename_lower = StringToLowerASCII(filename); + + for (int i = 0; i < arraysize(known_devices); ++i) { + // Exact match. + if (filename_lower == known_devices[i]) + return true; + // Starts with "DEVICE.". + if (filename_lower.find(std::wstring(known_devices[i]) + L".") == 0) + return true; + } + + static const wchar_t* const magic_names[] = { + // These file names are used by the "Customize folder" feature of the shell. + L"desktop.ini", + L"thumbs.db", + }; + + for (int i = 0; i < arraysize(magic_names); ++i) { + if (filename_lower == magic_names[i]) + return true; + } + + return false; +} + +bool IsShellIntegratedExtension(const std::wstring& extension) { + std::wstring extension_lower = StringToLowerASCII(extension); + + static const wchar_t* const integrated_extensions[] = { + // See <http://msdn.microsoft.com/en-us/library/ms811694.aspx>. + L"local", + // Right-clicking on shortcuts can be magical. + L"lnk", + }; + + for (int i = 0; i < arraysize(integrated_extensions); ++i) { + if (extension_lower == integrated_extensions[i]) + return true; + } + + // See <http://www.juniper.net/security/auto/vulnerabilities/vuln2612.html>. + // That vulnerability report is not exactly on point, but files become magical + // if their end in a CLSID. Here we block extensions that look like CLSIDs. + if (extension_lower.size() > 0 && extension_lower.at(0) == L'{' && + extension_lower.at(extension_lower.length() - 1) == L'}') + return true; + + return false; +} + +// In addition to passing the RTL flags to ::MessageBox if we are running in an +// RTL locale, we need to make sure that LTR strings are rendered correctly by +// adding the appropriate Unicode directionality marks. +int MessageBox(HWND hwnd, + const std::wstring& text, + const std::wstring& caption, + UINT flags) { + UINT actual_flags = flags; + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) + actual_flags |= MB_RIGHT | MB_RTLREADING; + + std::wstring localized_text; + const wchar_t* text_ptr = text.c_str(); + if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text)) + text_ptr = localized_text.c_str(); + + std::wstring localized_caption; + const wchar_t* caption_ptr = caption.c_str(); + if (l10n_util::AdjustStringForLocaleDirection(caption, &localized_caption)) + caption_ptr = localized_caption.c_str(); + + return ::MessageBox(hwnd, text_ptr, caption_ptr, actual_flags); +} + +} // namespace win_util diff --git a/chrome/common/win_util.h b/chrome/common/win_util.h new file mode 100644 index 0000000..ebf99a2 --- /dev/null +++ b/chrome/common/win_util.h @@ -0,0 +1,265 @@ +// 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 CHROME_COMMON_WIN_UTIL_H__ +#define CHROME_COMMON_WIN_UTIL_H__ + +#include <objbase.h> + +#include <string> +#include <vector> + +#include "base/fix_wp64.h" +#include "base/gfx/rect.h" +#include "base/scoped_handle.h" + +namespace win_util { + +// Import ScopedHandle and friends into this namespace for backwards +// compatibility. TODO(darin): clean this up! +using ::ScopedHandle; +using ::ScopedFindFileHandle; +using ::ScopedHDC; +using ::ScopedBitmap; +using ::ScopedHRGN; + +// Simple scoped memory releaser class for COM allocated memory. +// Example: +// CoMemReleaser<ITEMIDLIST> file_item; +// SHGetSomeInfo(&file_item, ...); +// ... +// return; <-- memory released +template<typename T> +class CoMemReleaser { + public: + explicit CoMemReleaser() : mem_ptr_(NULL) {} + + ~CoMemReleaser() { + if (mem_ptr_) + CoTaskMemFree(mem_ptr_); + } + + T** operator&() { + return &mem_ptr_; + } + + operator T*() { + return mem_ptr_; + } + + private: + T* mem_ptr_; + + DISALLOW_EVIL_CONSTRUCTORS(CoMemReleaser); +}; + +// Initializes COM in the constructor, and uninitializes COM in the +// destructor. +class ScopedCOMInitializer { + public: + ScopedCOMInitializer() { + CoInitialize(NULL); + } + + ~ScopedCOMInitializer() { + CoUninitialize(); + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(ScopedCOMInitializer); +}; + +// Creates a string interpretation of the time of day represented by the given +// SYSTEMTIME that's appropriate for the user's default locale. +// Format can be an empty string (for the default format), or a "format picture" +// as specified in the Windows documentation for GetTimeFormat(). +std::wstring FormatSystemTime(const SYSTEMTIME& time, + const std::wstring& format); + +// Creates a string interpretation of the date represented by the given +// SYSTEMTIME that's appropriate for the user's default locale. +// Format can be an empty string (for the default format), or a "format picture" +// as specified in the Windows documentation for GetDateFormat(). +std::wstring FormatSystemDate(const SYSTEMTIME& date, + const std::wstring& format); + +// Returns the long path name given a short path name. A short path name +// is a path that follows the 8.3 convention and has ~x in it. If the +// path is already a long path name, the function returns the current +// path without modification. +bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path); + +// Returns true if the current point is close enough to the origin point in +// space and time that it would be considered a double click. +bool IsDoubleClick(const POINT& origin, + const POINT& current, + DWORD elapsed_time); + +// Returns true if the current point is far enough from the origin that it +// would be considered a drag. +bool IsDrag(const POINT& origin, const POINT& current); + +// Returns true if we are on Windows Vista and composition is enabled +bool ShouldUseVistaFrame(); + +// Use the Win32 API FormatMessage() function to generate a string, using +// Windows's default Message Compiled resources; ignoring the inserts. +std::wstring FormatMessage(unsigned messageid); + +// Uses the last Win32 error to generate a human readable message string. +std::wstring FormatLastWin32Error(); + +// Open a Windows explorer window with the specified file highlighted. +void ShowItemInFolder(const std::wstring& full_path); + +// Open or run a file via the Windows shell. In the event that there is no +// default application registered for the file specified by 'full_path', +// ask the user, via the Windows "Open With" dialog, for an application to use +// if 'ask_for_app' is true. +// Returns 'true' on successful open, 'false' otherwise. +bool OpenItemViaShell(const std::wstring& full_path, bool ask_for_app); + +// The download manager now writes the alternate data stream with the +// zone on all downloads. This function is equivalent to OpenItemViaShell +// without showing the zone warning dialog. +bool OpenItemViaShellNoZoneCheck(const std::wstring& full_path, + bool ask_for_app); + +// Ask the user, via the Windows "Open With" dialog, for an application to use +// to open the file specified by 'full_path'. +// Returns 'true' on successful open, 'false' otherwise. +bool OpenItemWithExternalApp(const std::wstring& full_path); + +// Prompt the user for location to save a file. 'suggested_name' is a full path +// that gives the dialog box a hint as to how to initialize itself. +// For example, a 'suggested_name' of: +// "C:\Documents and Settings\jojo\My Documents\picture.png" +// will start the dialog in the "C:\Documents and Settings\jojo\My Documents\" +// directory, and filter for .png file types. +// 'owner' is the window to which the dialog box is modal, NULL for a modeless +// dialog box. +// On success, returns true and 'final_name' contains the full path of the file +// that the user chose. On error, returns false, and 'final_name' is not +// modified. +// NOTE: DO NOT CALL THIS FUNCTION DIRECTLY! Instead use the helper objects in +// browser/shell_dialogs.cc to do this asynchronously on a different +// thread so that the app isn't jankified if the Windows shell dialog +// takes a long time to display. +bool SaveFileAs(HWND owner, + const std::wstring& suggested_name, + std::wstring* final_name); + +// Prompt the user for location to save a file. +// Callers should provide the filter string, and also a filter index. +// The parameter |index| indicates the initial index of filter description +// and filter pattern for the dialog box. If |index| is zero or greater than +// the number of total filter types, the system uses the first filter in the +// |filter| buffer. The parameter |final_name| returns the file name which +// contains the drive designator, path, file name, and extension of the user +// selected file name. +bool SaveFileAsWithFilter(HWND owner, + const std::wstring& suggested_name, + const wchar_t* filter, + const std::wstring& def_ext, + unsigned* index, + std::wstring* final_name); + +// If the window does not fit on the default monitor, it is moved and possibly +// resized appropriately. +void AdjustWindowToFit(HWND hwnd); + +// Sizes the window to have a client or window size (depending on the value of +// |pref_is_client|) of pref, then centers the window over parent, ensuring the +// window fits on screen. +void CenterAndSizeWindow(HWND parent, HWND window, const SIZE& pref, + bool pref_is_client); + +// Duplicates a section handle from another process to the current process. +// Returns the new valid handle if the function succeed. NULL otherwise. +HANDLE GetSectionFromProcess(HANDLE section, HANDLE process, bool read_only); + +// Returns true if the specified window is the current active top window or one +// of its children. +bool DoesWindowBelongToActiveWindow(HWND window); + +// Adjusts the value of |child_rect| if necessary to ensure that it is +// completely visible within |parent_rect|. +void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, + gfx::Rect* child_rect, + int padding); + +// Ensures that the child window stays within the boundaries of the parent +// before setting its bounds. If |parent_window| is NULL, the bounds of the +// parent are assumed to be the bounds of the monitor that |child_window| is +// nearest to. If |child_window| isn't visible yet and |insert_after_window| +// is non-NULL and visible, the monitor |insert_after_window| is on is used +// as the parent bounds instead. +void SetChildBounds(HWND child_window, HWND parent_window, + HWND insert_after_window, const gfx::Rect& bounds, + int padding, unsigned long flags); + +// Returns the bounds for the monitor that contains the largest area of +// intersection with the specified rectangle. +gfx::Rect GetMonitorBoundsForRect(const gfx::Rect& rect); + +// Returns true if the virtual key code is a digit coming from the numeric +// keypad (with or without Num Lock on). |extended_key| should be set to the +// extended key flag specified in the WM_KEYDOWN/UP where the |key_code| +// originated. +bool IsNumPadDigit(int key_code, bool extended_key); + +// Grabs a snapshot of the designated window and stores a PNG representation +// into a byte vector. +void GrabWindowSnapshot(HWND window_handle, + std::vector<unsigned char>* png_representation); + +// Returns whether the specified window is the current active window. +bool IsWindowActive(HWND hwnd); + +// Returns whether the specified file name is a reserved name on windows. +// This includes names like "com2.zip" (which correspond to devices) and +// desktop.ini and thumbs.db which have special meaning to the windows shell. +bool IsReservedName(const std::wstring& filename); + +// Returns whether the specified extension is automatically integrated into the +// windows shell. +bool IsShellIntegratedExtension(const std::wstring& eextension); + +// A wrapper around Windows' MessageBox function. Using a Chrome specific +// MessageBox function allows us to control certain RTL locale flags so that +// callers don't have to worry about adding these flags when running in a +// right-to-left locale. +int MessageBox(HWND hwnd, + const std::wstring& text, + const std::wstring& caption, + UINT flags); + +} // namespace win_util + +#endif // WIN_COMMON_WIN_UTIL_H__ diff --git a/chrome/common/win_util_unittest.cc b/chrome/common/win_util_unittest.cc new file mode 100644 index 0000000..31702da --- /dev/null +++ b/chrome/common/win_util_unittest.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 "base/registry.h" +#include "base/string_util.h" +#include "chrome/common/win_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +class WinUtilTest: public testing::Test { +}; + +// Retrieve the OS primary language +unsigned GetSystemLanguage() { + RegKey language_key(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Nls\\Language"); + std::wstring language; + language_key.ReadValue(L"InstallLanguage", &language); + wchar_t * unused_endptr; + return PRIMARYLANGID(wcstol(language.c_str(), &unused_endptr, 16)); +} + +TEST(WinUtilTest, FormatMessage) { + const int kAccessDeniedErrorCode = 5; + SetLastError(kAccessDeniedErrorCode); + ASSERT_EQ(GetLastError(), kAccessDeniedErrorCode); + std::wstring value; + + unsigned language = GetSystemLanguage(); + ASSERT_TRUE(language); + if (language == LANG_ENGLISH) { + // This test would fail on non-English system. + TrimWhitespace(win_util::FormatLastWin32Error(), TRIM_ALL, &value); + EXPECT_EQ(value, std::wstring(L"Access is denied.")); + } else if (language == LANG_FRENCH) { + // This test would fail on non-French system. + TrimWhitespace(win_util::FormatLastWin32Error(), TRIM_ALL, &value); + EXPECT_EQ(value, std::wstring(L"Acc\00e8s refus\00e9.")); + } else { + EXPECT_TRUE(0) << "Please implement the test for your OS language."; + } + + // Manually call the OS function + wchar_t * string_buffer = NULL; + unsigned string_length = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + kAccessDeniedErrorCode, 0, + reinterpret_cast<wchar_t *>(&string_buffer), + 0, NULL); + + // Verify the call succeeded + ASSERT_TRUE(string_length); + ASSERT_TRUE(string_buffer); + + // Verify the string is the same by different calls + EXPECT_EQ(win_util::FormatLastWin32Error(), std::wstring(string_buffer)); + EXPECT_EQ(win_util::FormatMessage(kAccessDeniedErrorCode), + std::wstring(string_buffer)); + + // Done with the buffer allocated by ::FormatMessage() + LocalFree(string_buffer); +} + +TEST(WinUtilTest, EnsureRectIsVisibleInRect) { + gfx::Rect parent_rect(0, 0, 500, 400); + + { + // Child rect x < 0 + gfx::Rect child_rect(-50, 20, 100, 100); + win_util::EnsureRectIsVisibleInRect(parent_rect, &child_rect, 10); + EXPECT_EQ(gfx::Rect(10, 20, 100, 100), child_rect); + } + + { + // Child rect y < 0 + gfx::Rect child_rect(20, -50, 100, 100); + win_util::EnsureRectIsVisibleInRect(parent_rect, &child_rect, 10); + EXPECT_EQ(gfx::Rect(20, 10, 100, 100), child_rect); + } + + { + // Child rect right > parent_rect.right + gfx::Rect child_rect(450, 20, 100, 100); + win_util::EnsureRectIsVisibleInRect(parent_rect, &child_rect, 10); + EXPECT_EQ(gfx::Rect(390, 20, 100, 100), child_rect); + } + + { + // Child rect bottom > parent_rect.bottom + gfx::Rect child_rect(20, 350, 100, 100); + win_util::EnsureRectIsVisibleInRect(parent_rect, &child_rect, 10); + EXPECT_EQ(gfx::Rect(20, 290, 100, 100), child_rect); + } + + { + // Child rect width > parent_rect.width + gfx::Rect child_rect(20, 20, 700, 100); + win_util::EnsureRectIsVisibleInRect(parent_rect, &child_rect, 10); + EXPECT_EQ(gfx::Rect(20, 20, 480, 100), child_rect); + } + + { + // Child rect height > parent_rect.height + gfx::Rect child_rect(20, 20, 100, 700); + win_util::EnsureRectIsVisibleInRect(parent_rect, &child_rect, 10); + EXPECT_EQ(gfx::Rect(20, 20, 100, 380), child_rect); + } +} + diff --git a/chrome/common/worker_thread_ticker.cc b/chrome/common/worker_thread_ticker.cc new file mode 100644 index 0000000..282a0c3 --- /dev/null +++ b/chrome/common/worker_thread_ticker.cc @@ -0,0 +1,140 @@ +// 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 "base/logging.h" +#include "chrome/common/worker_thread_ticker.h" + +WorkerThreadTicker::WorkerThreadTicker(int tick_interval) + : tick_interval_(tick_interval), + wait_handle_(NULL) { +} + +WorkerThreadTicker::~WorkerThreadTicker() { + Stop(); +} + +bool WorkerThreadTicker::RegisterTickHandler(Callback *tick_handler) { + DCHECK(tick_handler); + AutoLock lock(tick_handler_list_lock_); + // You cannot change the list of handlers when the timer is running. + // You need to call Stop first. + if (IsRunning()) { + return false; + } + tick_handler_list_.push_back(tick_handler); + return true; +} + +bool WorkerThreadTicker::UnregisterTickHandler(Callback *tick_handler) { + DCHECK(tick_handler); + AutoLock lock(tick_handler_list_lock_); + // You cannot change the list of handlers when the timer is running. + // You need to call Stop first. + if (IsRunning()) { + return false; + } + TickHandlerListType::iterator index = std::remove(tick_handler_list_.begin(), + tick_handler_list_.end(), + tick_handler); + if (index == tick_handler_list_.end()) { + return false; + } + tick_handler_list_.erase(index, tick_handler_list_.end()); + return true; +} + +bool WorkerThreadTicker::Start() { + // Do this in a lock because we don't want 2 threads to + // call Start at the same time + AutoLock lock(tick_handler_list_lock_); + if (IsRunning()) { + return false; + } + bool ret = false; + HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!event) { + NOTREACHED(); + } else { + if (!RegisterWaitForSingleObject( + &wait_handle_, + event, + reinterpret_cast<WAITORTIMERCALLBACK>(TickCallback), + this, + tick_interval_, + WT_EXECUTEDEFAULT)) { + NOTREACHED(); + CloseHandle(event); + } else { + dummy_event_.Attach(event); + ret = true; + } + } + return ret; +} + +bool WorkerThreadTicker::Stop() { + // Do this in a lock because we don't want 2 threads to + // call Stop at the same time + AutoLock lock(tick_handler_list_lock_); + if (!IsRunning()) { + return false; + } + DCHECK(wait_handle_); + + // Wait for the callbacks to be done. Passing + // INVALID_HANDLE_VALUE to UnregisterWaitEx achieves this. + UnregisterWaitEx(wait_handle_, INVALID_HANDLE_VALUE); + wait_handle_ = NULL; + dummy_event_.Close(); + return true; +} + +bool WorkerThreadTicker::IsRunning() const { + return (wait_handle_ != NULL); +} + +void WorkerThreadTicker::InvokeHandlers() { + // When the ticker is running, the handler list CANNOT be modified. + // So we can do the enumeration safely without a lock + TickHandlerListType::iterator index = tick_handler_list_.begin(); + while (index != tick_handler_list_.end()) { + (*index)->OnTick(); + index++; + } +} + +void CALLBACK WorkerThreadTicker::TickCallback(WorkerThreadTicker* this_ticker, + BOOLEAN timer_or_wait_fired) { + DCHECK(NULL != this_ticker); + if (NULL != this_ticker) { + this_ticker->InvokeHandlers(); + } +} + diff --git a/chrome/common/worker_thread_ticker.h b/chrome/common/worker_thread_ticker.h new file mode 100644 index 0000000..cbb60b5 --- /dev/null +++ b/chrome/common/worker_thread_ticker.h @@ -0,0 +1,117 @@ +// 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 CHROME_COMMON_WORKER_THREAD_TICKER_H__ +#define CHROME_COMMON_WORKER_THREAD_TICKER_H__ + +#include <windows.h> +#include <atlbase.h> +#include <vector> + +#include "base/lock.h" + +// This class provides the follwoing functionality: +// It invokes a set of registered handlers at periodic intervals in +// the context of an arbitrary worker thread. +// This functionality is similar to a waitable timer except that the +// timer in this case is a low-resolution timer (millisecond granularity) +// and it does not require the caller to be in an alertable wait state. +// The callbacks are invoked in the context of an arbitrary worker thread +// from the system thread pool +class WorkerThreadTicker { + public: + // This callback interface to be implemented by clients of this + // class + class Callback { + public: + // Gets invoked when the timer period is up + virtual void OnTick() = 0; + }; + + // tick_interval is the periodic interval in which to invoke the + // registered handlers + explicit WorkerThreadTicker(int tick_interval); + + ~WorkerThreadTicker(); + + // Registers a callback handler interface + // tick_handler is the handler interface to register. The ownership of this + // object is not transferred to this class. + bool RegisterTickHandler(Callback *tick_handler); + + // Unregisters a callback handler interface + // tick_handler is the handler interface to unregister + bool UnregisterTickHandler(Callback *tick_handler); + + // Starts the ticker. Returns false if the ticker is already running + // or if the Start fails. + bool Start(); + // Stops the ticker and waits for all callbacks. to be done. This method + // does not provide a way to stop without waiting for the callbacks to be + // done because this is inherently risky. + // Returns false is the ticker is not running + bool Stop(); + bool IsRunning() const; + + void set_tick_interval(int tick_interval) { + tick_interval_ = tick_interval; + } + + int tick_interval() const { + return tick_interval_; + } + + private: + // A list type that holds all registered callback interfaces + typedef std::vector<Callback*> TickHandlerListType; + + // This is the callback function registered with the + // RegisterWaitForSingleObject API. It gets invoked in a system worker thread + // periodically at intervals of tick_interval_ miliiseconds + static void CALLBACK TickCallback(WorkerThreadTicker* this_ticker, + BOOLEAN timer_or_wait_fired); + + // Helper that invokes all registered handlers + void InvokeHandlers(); + + // A dummy event to be used by the RegisterWaitForSingleObject API + CHandle dummy_event_; + // The wait handle returned by the RegisterWaitForSingleObject API + HANDLE wait_handle_; + // The interval at which the callbacks are to be invoked + int tick_interval_; + // Lock for the tick_handler_list_ list + Lock tick_handler_list_lock_; + // A list that holds all registered callback interfaces + TickHandlerListType tick_handler_list_; + + DISALLOW_EVIL_CONSTRUCTORS(WorkerThreadTicker); +}; + +#endif // CHROME_COMMON_WORKER_THREAD_TICKER_H__ |