summaryrefslogtreecommitdiffstats
path: root/chrome/common
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/common
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_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')
-rw-r--r--chrome/common/SConscript210
-rw-r--r--chrome/common/animation.cc145
-rw-r--r--chrome/common/animation.h144
-rw-r--r--chrome/common/animation_unittest.cc128
-rw-r--r--chrome/common/bzip2_unittest.cc92
-rw-r--r--chrome/common/child_process.cc114
-rw-r--r--chrome/common/child_process.h115
-rw-r--r--chrome/common/chrome_constants.cc73
-rw-r--r--chrome/common/chrome_constants.h68
-rw-r--r--chrome/common/chrome_counters.cc80
-rw-r--r--chrome/common/chrome_counters.h66
-rw-r--r--chrome/common/chrome_paths.cc241
-rw-r--r--chrome/common/chrome_paths.h74
-rw-r--r--chrome/common/chrome_plugin_api.h507
-rw-r--r--chrome/common/chrome_plugin_lib.cc278
-rw-r--r--chrome/common/chrome_plugin_lib.h127
-rw-r--r--chrome/common/chrome_plugin_unittest.cc297
-rw-r--r--chrome/common/chrome_plugin_util.cc191
-rw-r--r--chrome/common/chrome_plugin_util.h112
-rw-r--r--chrome/common/chrome_process_filter.cc57
-rw-r--r--chrome/common/chrome_process_filter.h51
-rw-r--r--chrome/common/chrome_switches.cc340
-rw-r--r--chrome/common/chrome_switches.h145
-rw-r--r--chrome/common/classfactory.cc79
-rw-r--r--chrome/common/classfactory.h94
-rw-r--r--chrome/common/clipboard_service.cc44
-rw-r--r--chrome/common/clipboard_service.h54
-rw-r--r--chrome/common/common.vcproj715
-rw-r--r--chrome/common/common.vsprops8
-rw-r--r--chrome/common/common_resources.h3
-rw-r--r--chrome/common/common_resources.rc16
-rw-r--r--chrome/common/debug_flags.cc70
-rw-r--r--chrome/common/debug_flags.h54
-rw-r--r--chrome/common/drag_drop_types.cc55
-rw-r--r--chrome/common/drag_drop_types.h48
-rw-r--r--chrome/common/env_util.cc82
-rw-r--r--chrome/common/env_util.h67
-rw-r--r--chrome/common/env_vars.cc56
-rw-r--r--chrome/common/env_vars.h46
-rw-r--r--chrome/common/filter_policy.h71
-rw-r--r--chrome/common/gears_api.h109
-rw-r--r--chrome/common/gfx/chrome_canvas.cc404
-rw-r--r--chrome/common/gfx/chrome_canvas.h213
-rw-r--r--chrome/common/gfx/chrome_font.cc188
-rw-r--r--chrome/common/gfx/chrome_font.h181
-rw-r--r--chrome/common/gfx/color_utils.cc279
-rw-r--r--chrome/common/gfx/color_utils.h88
-rw-r--r--chrome/common/gfx/emf.cc342
-rw-r--r--chrome/common/gfx/emf.h204
-rw-r--r--chrome/common/gfx/emf_unittest.cc140
-rw-r--r--chrome/common/gfx/favicon_size.h53
-rw-r--r--chrome/common/gfx/icon_util.cc501
-rw-r--r--chrome/common/gfx/icon_util.h218
-rw-r--r--chrome/common/gfx/icon_util_unittest.cc287
-rw-r--r--chrome/common/gfx/insets.h86
-rw-r--r--chrome/common/gfx/path.cc54
-rw-r--r--chrome/common/gfx/path.h54
-rw-r--r--chrome/common/gfx/url_elider.cc435
-rw-r--r--chrome/common/gfx/url_elider.h59
-rw-r--r--chrome/common/gfx/url_elider_unittest.cc189
-rw-r--r--chrome/common/gfx/utils.h37
-rw-r--r--chrome/common/ipc_channel.cc457
-rw-r--r--chrome/common/ipc_channel.h187
-rw-r--r--chrome/common/ipc_channel_proxy.cc286
-rw-r--r--chrome/common/ipc_channel_proxy.h219
-rw-r--r--chrome/common/ipc_fuzzing_tests.cc443
-rw-r--r--chrome/common/ipc_logging.cc296
-rw-r--r--chrome/common/ipc_logging.h125
-rw-r--r--chrome/common/ipc_message.cc97
-rw-r--r--chrome/common/ipc_message.h263
-rw-r--r--chrome/common/ipc_message_macros.h1074
-rw-r--r--chrome/common/ipc_message_unittest.cc74
-rw-r--r--chrome/common/ipc_message_utils.h1414
-rw-r--r--chrome/common/ipc_sync_channel.cc470
-rw-r--r--chrome/common/ipc_sync_channel.h138
-rw-r--r--chrome/common/ipc_sync_channel_unittest.cc625
-rw-r--r--chrome/common/ipc_sync_channel_unittest.h43
-rw-r--r--chrome/common/ipc_sync_message.cc140
-rw-r--r--chrome/common/ipc_sync_message.h108
-rw-r--r--chrome/common/ipc_sync_message_unittest.cc277
-rw-r--r--chrome/common/ipc_sync_message_unittest.h108
-rw-r--r--chrome/common/ipc_sync_sender.cc150
-rw-r--r--chrome/common/ipc_sync_sender.h123
-rw-r--r--chrome/common/ipc_tests.cc426
-rw-r--r--chrome/common/ipc_tests.h54
-rw-r--r--chrome/common/ipc_tests.vcproj165
-rw-r--r--chrome/common/jpeg_codec.cc545
-rw-r--r--chrome/common/jpeg_codec.h84
-rw-r--r--chrome/common/jpeg_codec_unittest.cc172
-rw-r--r--chrome/common/json_value_serializer.cc101
-rw-r--r--chrome/common/json_value_serializer.h115
-rw-r--r--chrome/common/json_value_serializer_perftest.cc116
-rw-r--r--chrome/common/json_value_serializer_unittest.cc349
-rw-r--r--chrome/common/jstemplate_builder.cc77
-rw-r--r--chrome/common/jstemplate_builder.h54
-rw-r--r--chrome/common/l10n_util.cc570
-rw-r--r--chrome/common/l10n_util.h197
-rw-r--r--chrome/common/l10n_util_unittest.cc156
-rw-r--r--chrome/common/libxml_utils.cc167
-rw-r--r--chrome/common/libxml_utils.h208
-rw-r--r--chrome/common/logging_chrome.cc197
-rw-r--r--chrome/common/logging_chrome.h82
-rw-r--r--chrome/common/logging_chrome_uitest.cc162
-rw-r--r--chrome/common/message_router.cc66
-rw-r--r--chrome/common/message_router.h84
-rw-r--r--chrome/common/mru_cache.h256
-rw-r--r--chrome/common/mru_cache_unittest.cc261
-rw-r--r--chrome/common/navigation_types.h45
-rw-r--r--chrome/common/net/cache_uitest.cc201
-rw-r--r--chrome/common/net/cookie_monster_sqlite.cc378
-rw-r--r--chrome/common/net/cookie_monster_sqlite.h76
-rw-r--r--chrome/common/net/dns.h49
-rw-r--r--chrome/common/net/url_request_intercept_job.cc234
-rw-r--r--chrome/common/net/url_request_intercept_job.h88
-rw-r--r--chrome/common/net/url_util_unittest.cc77
-rw-r--r--chrome/common/notification_details.h80
-rw-r--r--chrome/common/notification_service.cc140
-rw-r--r--chrome/common/notification_service.h144
-rw-r--r--chrome/common/notification_service_unittest.cc197
-rw-r--r--chrome/common/notification_source.h79
-rw-r--r--chrome/common/notification_types.h456
-rw-r--r--chrome/common/os_exchange_data.cc703
-rw-r--r--chrome/common/os_exchange_data.h168
-rw-r--r--chrome/common/os_exchange_data_unittest.cc369
-rw-r--r--chrome/common/page_transition_types.h177
-rw-r--r--chrome/common/plugin_messages.cc48
-rw-r--r--chrome/common/plugin_messages.h550
-rw-r--r--chrome/common/plugin_messages_internal.h318
-rw-r--r--chrome/common/pref_member.h216
-rw-r--r--chrome/common/pref_member_unittest.cc214
-rw-r--r--chrome/common/pref_names.cc425
-rw-r--r--chrome/common/pref_names.h167
-rw-r--r--chrome/common/pref_service.cc709
-rw-r--r--chrome/common/pref_service.h263
-rw-r--r--chrome/common/pref_service_uitest.cc159
-rw-r--r--chrome/common/pref_service_unittest.cc432
-rw-r--r--chrome/common/process_watcher.cc118
-rw-r--r--chrome/common/process_watcher.h59
-rw-r--r--chrome/common/rand_util.cc72
-rw-r--r--chrome/common/rand_util.h48
-rw-r--r--chrome/common/ref_counted_util.h55
-rw-r--r--chrome/common/render_messages.cc47
-rw-r--r--chrome/common/render_messages.h1548
-rw-r--r--chrome/common/render_messages_internal.h1007
-rw-r--r--chrome/common/resource_bundle.cc333
-rw-r--r--chrome/common/resource_bundle.h183
-rw-r--r--chrome/common/resource_dispatcher.cc534
-rw-r--r--chrome/common/resource_dispatcher.h145
-rw-r--r--chrome/common/resource_dispatcher_unittest.cc208
-rw-r--r--chrome/common/scoped_vector.h76
-rw-r--r--chrome/common/security_filter_peer.cc339
-rw-r--r--chrome/common/security_filter_peer.h157
-rw-r--r--chrome/common/slide_animation.cc148
-rw-r--r--chrome/common/slide_animation.h125
-rw-r--r--chrome/common/sqlite_compiled_statement.cc88
-rw-r--r--chrome/common/sqlite_compiled_statement.h171
-rw-r--r--chrome/common/sqlite_utils.cc95
-rw-r--r--chrome/common/sqlite_utils.h641
-rw-r--r--chrome/common/stl_util-inl.h476
-rw-r--r--chrome/common/task_queue.cc71
-rw-r--r--chrome/common/task_queue.h67
-rw-r--r--chrome/common/text_zoom.h42
-rw-r--r--chrome/common/throb_animation.cc93
-rw-r--r--chrome/common/throb_animation.h84
-rw-r--r--chrome/common/thumbnail_score.cc126
-rw-r--r--chrome/common/thumbnail_score.h92
-rw-r--r--chrome/common/time_format.cc334
-rw-r--r--chrome/common/time_format.h93
-rw-r--r--chrome/common/time_format_unittest.cc109
-rw-r--r--chrome/common/visitedlink_common.cc117
-rw-r--r--chrome/common/visitedlink_common.h153
-rw-r--r--chrome/common/win_safe_util.cc211
-rw-r--r--chrome/common/win_safe_util.h74
-rw-r--r--chrome/common/win_util.cc844
-rw-r--r--chrome/common/win_util.h265
-rw-r--r--chrome/common/win_util_unittest.cc134
-rw-r--r--chrome/common/worker_thread_ticker.cc140
-rw-r--r--chrome/common/worker_thread_ticker.h117
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(&params));
+ 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, &params);
+
+ 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, &params);
+
+ 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, &region);
+ 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, &region);
+ 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, &reg_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, &reg_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, &center_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__