diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-09-28 18:37:14 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-09-29 07:47:06 +0000 |
commit | 0e8ff63a407fe323e215bb1a2c423c09a4747c8a (patch) | |
tree | e27e357e125d2d705bd504e1e3c8a3da1ed20f1d | |
parent | c3d0bb5bb15d008606b18b865841e19cd9bb5847 (diff) | |
download | qtwebengine-chromium-upstream-45.tar.gz |
BASELINE: Update chromium to 45.0.2454.101upstream-45
Also adds web_cache component
Change-Id: I51238ceea8ee99854cc4989ae70a4fc2fc6bedcb
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
98 files changed, 2191 insertions, 252 deletions
diff --git a/chromium/DEPS b/chromium/DEPS index 9296acad29f..ae1795bd274 100644 --- a/chromium/DEPS +++ b/chromium/DEPS @@ -4,7 +4,7 @@ vars = { 'boringssl_revision': 'de24aadc5bc01130b6a9d25582203bb5308fabe1', 'buildspec_platforms': - 'android,', + 'all', 'buildtools_revision': 'ecc8e253abac3b6186a97573871a084f4c0ca3ae', 'chromium_git': @@ -34,9 +34,7 @@ vars = { 'swarming_revision': 'b39a448d8522392389b28f6997126a6ab04bfe87', 'v8_revision': - '7f211533faba9dd85708b1394186c7fe99b88392', - 'webkit_revision': - 'abaaf1d0b6b6483140b5dca34e80fc259833ddf7' + '7f211533faba9dd85708b1394186c7fe99b88392' } allowed_hosts = [ @@ -65,8 +63,6 @@ deps = { (Var("chromium_git")) + '/external/googlemock.git@29763965ab52f24565299976b936d1265cb6a271', 'src/testing/gtest': (Var("chromium_git")) + '/external/googletest.git@23574bf2333f834ff665f894c97bef8a5b33a0a9', - 'src/third_party/WebKit': - (Var("chromium_git")) + '/chromium/blink.git@4a6bcd0338160a0d9a8750dc71e1c8332b1fd6f3', 'src/third_party/angle': (Var("chromium_git")) + '/angle/angle.git@6f0fd8c5457f9dcffc9fa9fab3852417311be0a9', 'src/third_party/bidichecker': @@ -120,7 +116,7 @@ deps = { 'src/third_party/opus/src': (Var("chromium_git")) + '/chromium/deps/opus.git@cae696156f1e60006e39821e79a1811ae1933c69', 'src/third_party/pdfium': - 'https://pdfium.googlesource.com/pdfium.git@f81f724838983add18125f41292e98ef94e1bb39', + 'https://pdfium.googlesource.com/pdfium.git@860a3eb0f3c18853f95df5a70dc50a95a29aafb1', 'src/third_party/py_trace_event/src': (Var("chromium_git")) + '/external/py_trace_event.git@dd463ea9e2c430de2b9e53dea57a77b4c3ac9b30', 'src/third_party/pyftpdlib/src': @@ -164,7 +160,7 @@ deps = { 'src/tools/swarming_client': (Var("chromium_git")) + '/external/swarming.client.git@b39a448d8522392389b28f6997126a6ab04bfe87', 'src/v8': - (Var("chromium_git")) + '/v8/v8.git@d2356f267b8ee81447223bf7b22d1829eac3e608' + (Var("chromium_git")) + '/v8/v8.git@96dddb455daff3d8626bc4e5d7b2898fbab55991' } deps_os = { @@ -388,6 +384,7 @@ hooks = [ 'action': [ 'python', 'src/build/util/lastchange.py', + '--git-hash-only', '-s', 'src/third_party/WebKit', '-o', diff --git a/chromium/ash/ash.gyp b/chromium/ash/ash.gyp index 13d84cefe1e..c4fd69453e8 100644 --- a/chromium/ash/ash.gyp +++ b/chromium/ash/ash.gyp @@ -827,6 +827,7 @@ 'metrics/user_metrics_recorder_unittest.cc', 'popup_message_unittest.cc', 'root_window_controller_unittest.cc', + 'rotator/screen_rotation_animation_unittest.cc', 'screen_util_unittest.cc', 'shelf/scoped_observer_with_duplicated_sources_unittest.cc', 'shelf/shelf_button_pressed_metric_tracker_unittest.cc', diff --git a/chromium/base/win/scoped_handle.cc b/chromium/base/win/scoped_handle.cc index ce944e43b9f..2ebef320bb8 100644 --- a/chromium/base/win/scoped_handle.cc +++ b/chromium/base/win/scoped_handle.cc @@ -152,12 +152,6 @@ void ActiveVerifier::StartTracking(HANDLE handle, const void* owner, if (!enabled_) return; - // Idea here is to make our handles non-closable until we close it ourselves. - // Handles provided could be totally fabricated especially through our - // unittest, we are ignoring that for now by not checking return value. - ::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, - HANDLE_FLAG_PROTECT_FROM_CLOSE); - // Grab the thread id before the lock. DWORD thread_id = GetCurrentThreadId(); @@ -178,15 +172,6 @@ void ActiveVerifier::StopTracking(HANDLE handle, const void* owner, if (!enabled_) return; - // We expect handle to be protected till this point. - DWORD flags = 0; - if (::GetHandleInformation(handle, &flags)) { - CHECK_NE(0U, (flags & HANDLE_FLAG_PROTECT_FROM_CLOSE)); - - // Unprotect handle so that it could be closed. - ::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0); - } - AutoNativeLock lock(*lock_); HandleMap::iterator i = map_.find(handle); if (i == map_.end()) diff --git a/chromium/build/util/LASTCHANGE b/chromium/build/util/LASTCHANGE index e8df4b9dcb5..146de03183c 100644 --- a/chromium/build/util/LASTCHANGE +++ b/chromium/build/util/LASTCHANGE @@ -1 +1 @@ -LASTCHANGE=1f6e893ac083faeb6f7779a9aa740c4bd7db248c +LASTCHANGE=25cac1820a03dc7a2feb10c40813c9e3dc418fb0 diff --git a/chromium/build/util/LASTCHANGE.blink b/chromium/build/util/LASTCHANGE.blink index 16b0ebb3702..5b2c6ec0bbf 100644 --- a/chromium/build/util/LASTCHANGE.blink +++ b/chromium/build/util/LASTCHANGE.blink @@ -1 +1 @@ -LASTCHANGE=cb0d6dcc6b193a8927ceea52f508124b067589db +LASTCHANGE=8cee86b1597ea97322ea5d54881fc04be701d11c diff --git a/chromium/build/util/lastchange.py b/chromium/build/util/lastchange.py index 3f3ee4af471..ce1926afc4d 100755 --- a/chromium/build/util/lastchange.py +++ b/chromium/build/util/lastchange.py @@ -90,7 +90,7 @@ def RunGitCommand(directory, command): return None -def FetchGitRevision(directory): +def FetchGitRevision(directory, hash_only): """ Fetch the Git hash for a given directory. @@ -116,7 +116,7 @@ def FetchGitRevision(directory): if line.startswith('Cr-Commit-Position:'): pos = line.rsplit()[-1].strip() break - if not pos: + if hash_only or not pos: return VersionInfo('git', hsh) return VersionInfo('git', '%s-%s' % (hsh, pos)) @@ -166,7 +166,7 @@ def FetchGitSVNRevision(directory, svn_url_regex, go_deeper): def FetchVersionInfo(default_lastchange, directory=None, directory_regex_prior_to_src_url='chrome|blink|svn', - go_deeper=False): + go_deeper=False, hash_only=False): """ Returns the last change (in the form of a branch, revision tuple), from some appropriate revision control system. @@ -176,7 +176,7 @@ def FetchVersionInfo(default_lastchange, directory=None, version_info = (FetchSVNRevision(directory, svn_url_regex) or FetchGitSVNRevision(directory, svn_url_regex, go_deeper) or - FetchGitRevision(directory)) + FetchGitRevision(directory, hash_only)) if not version_info: if default_lastchange and os.path.exists(default_lastchange): revision = open(default_lastchange, 'r').read().strip() @@ -263,6 +263,9 @@ def main(argv=None): parser.add_option("--git-svn-go-deeper", action='store_true', help="In a Git-SVN repo, dig down to the last committed " + "SVN change (historic behaviour).") + parser.add_option("--git-hash-only", action="store_true", + help="In a Git repo with commit positions, only report " + + "the hash.") opts, args = parser.parse_args(argv[1:]) out_file = opts.output @@ -283,7 +286,8 @@ def main(argv=None): version_info = FetchVersionInfo(opts.default_lastchange, directory=src_dir, - go_deeper=opts.git_svn_go_deeper) + go_deeper=opts.git_svn_go_deeper, + hash_only=opts.git_hash_only) if version_info.revision == None: version_info.revision = '0' diff --git a/chromium/build/whitespace_file.txt b/chromium/build/whitespace_file.txt index ea82f4e4ca7..3a03ab0d798 100644 --- a/chromium/build/whitespace_file.txt +++ b/chromium/build/whitespace_file.txt @@ -154,3 +154,5 @@ In the BUILD we trust. ^_^ In the masters we don't. + +chrome-tpm diff --git a/chromium/cc/layers/picture_layer_impl.cc b/chromium/cc/layers/picture_layer_impl.cc index c7076bdc1e1..90fdeb1efb4 100644 --- a/chromium/cc/layers/picture_layer_impl.cc +++ b/chromium/cc/layers/picture_layer_impl.cc @@ -183,7 +183,9 @@ void PictureLayerImpl::AppendQuads(RenderPass* render_pass, gfx::Rect opaque_rect = contents_opaque() ? geometry_rect : gfx::Rect(); gfx::Rect visible_geometry_rect = scaled_occlusion.GetUnoccludedContentRect(geometry_rect); - if (visible_geometry_rect.IsEmpty()) + // TODO(enne): HasRecordings is a workaround for crash in crbug.com/526402. + // Need proper fix for when recording does not cover visible rect. + if (visible_geometry_rect.IsEmpty() || !raster_source_->HasRecordings()) return; gfx::Rect quad_content_rect = shared_quad_state->visible_quad_layer_rect; diff --git a/chromium/chrome/VERSION b/chromium/chrome/VERSION index 152e81f0aaf..64c1fb9445c 100644 --- a/chromium/chrome/VERSION +++ b/chromium/chrome/VERSION @@ -1,4 +1,4 @@ MAJOR=45 MINOR=0 BUILD=2454 -PATCH=79 +PATCH=101 diff --git a/chromium/chrome/app/theme/default_100_percent/cros/logo_google_color_90.png b/chromium/chrome/app/theme/default_100_percent/cros/logo_google_color_90.png Binary files differindex 9ac7df1da31..e836fc30994 100644 --- a/chromium/chrome/app/theme/default_100_percent/cros/logo_google_color_90.png +++ b/chromium/chrome/app/theme/default_100_percent/cros/logo_google_color_90.png diff --git a/chromium/chrome/app/theme/default_200_percent/cros/logo_google_color_90.png b/chromium/chrome/app/theme/default_200_percent/cros/logo_google_color_90.png Binary files differindex ccd9a53c4e9..ea1ee1ad7c1 100644 --- a/chromium/chrome/app/theme/default_200_percent/cros/logo_google_color_90.png +++ b/chromium/chrome/app/theme/default_200_percent/cros/logo_google_color_90.png diff --git a/chromium/chrome/browser/resources/pdf/pdf.js b/chromium/chrome/browser/resources/pdf/pdf.js index 43e085e434b..c4975746d57 100644 --- a/chromium/chrome/browser/resources/pdf/pdf.js +++ b/chromium/chrome/browser/resources/pdf/pdf.js @@ -84,6 +84,7 @@ function PDFViewer(browserApi) { this.browserApi_ = browserApi; this.loadState_ = LoadState.LOADING; this.parentWindow_ = null; + this.parentOrigin_ = null; this.delayedScriptingMessages_ = []; @@ -674,6 +675,7 @@ PDFViewer.prototype = { handleScriptingMessage: function(message) { if (this.parentWindow_ != message.source) { this.parentWindow_ = message.source; + this.parentOrigin_ = message.origin; // Ensure that we notify the embedder if the document is loaded. if (this.loadState_ != LoadState.LOADING) this.sendDocumentLoadedMessage_(); @@ -760,11 +762,22 @@ PDFViewer.prototype = { * @param {Object} message the message to send. */ sendScriptingMessage_: function(message) { - if (this.parentWindow_) - this.parentWindow_.postMessage(message, '*'); + if (this.parentWindow_ && this.parentOrigin_) { + var targetOrigin; + // Only send data back to the embedder if it is from the same origin, + // unless we're sending it to ourselves (which could happen in the case + // of tests). We also allow documentLoaded messages through as this won't + // leak important information. + if (this.parentOrigin_ == window.location.origin) + targetOrigin = this.parentOrigin_; + else if (message.type == 'documentLoaded') + targetOrigin = '*'; + else + targetOrigin = this.browserApi_.getStreamInfo().originalUrl; + this.parentWindow_.postMessage(message, targetOrigin); + } }, - /** * @type {Viewport} the viewport of the PDF viewer. */ diff --git a/chromium/chrome/chrome_browser.gypi b/chromium/chrome/chrome_browser.gypi index 6300411b413..58639ea625f 100644 --- a/chromium/chrome/chrome_browser.gypi +++ b/chromium/chrome/chrome_browser.gypi @@ -72,8 +72,6 @@ 'browser/android/compositor/layer/tab_handle_layer.h', 'browser/android/compositor/layer/tab_layer.cc', 'browser/android/compositor/layer/tab_layer.h', - 'browser/android/compositor/layer/throbber_layer.cc', - 'browser/android/compositor/layer/throbber_layer.h', 'browser/android/compositor/layer/thumbnail_layer.cc', 'browser/android/compositor/layer/thumbnail_layer.h', 'browser/android/compositor/layer/toolbar_layer.cc', diff --git a/chromium/chrome/chrome_installer.gypi b/chromium/chrome/chrome_installer.gypi index efc5f889fd2..eeb633514bc 100644 --- a/chromium/chrome/chrome_installer.gypi +++ b/chromium/chrome/chrome_installer.gypi @@ -211,6 +211,7 @@ 'target_name': 'setup', 'type': 'executable', 'dependencies': [ + 'chrome_version_header', 'installer_util', 'installer_util_strings', '../base/base.gyp:base', diff --git a/chromium/chrome/common/localized_error.cc b/chromium/chrome/common/localized_error.cc index 3089c7c829e..7033f6499fe 100644 --- a/chromium/chrome/common/localized_error.cc +++ b/chromium/chrome/common/localized_error.cc @@ -44,8 +44,7 @@ namespace { static const char kRedirectLoopLearnMoreUrl[] = "https://support.google.com/chrome/answer/95626"; static const char kWeakDHKeyLearnMoreUrl[] = - "https://www.chromium.org/administrators/" - "err_ssl_weak_server_ephemeral_dh_key"; + "https://support.google.com/chrome?p=dh_error"; static const char kCachedCopyButtonFieldTrial[] = "EnableGoogleCachedCopyTextExperiment"; static const char kCachedCopyButtonExpTypeControl[] = "control"; diff --git a/chromium/chrome/renderer/resources/plugins/plugin_placeholders.css b/chromium/chrome/renderer/resources/plugins/plugin_placeholders.css index 119119f6803..345ef673f95 100644 --- a/chromium/chrome/renderer/resources/plugins/plugin_placeholders.css +++ b/chromium/chrome/renderer/resources/plugins/plugin_placeholders.css @@ -31,26 +31,16 @@ p { } #outer { + align-items: center; border: 1px black solid; box-sizing: border-box; + display: flex; height: 100%; + justify-content: center; position: absolute; width: 100%; } -#inner { - height: 100%; -<if expr="not is_android"> - margin-top: -70px; -</if> -<if expr="is_android"> - margin-top: -14px; -</if> - position: relative; - top: 50%; - width: 100%; -} - #close { background-image: -webkit-image-set( url(../../../../ui/resources/default_100_percent/close_2.png) 1x, diff --git a/chromium/chrome/renderer/resources/plugins/plugin_poster.html b/chromium/chrome/renderer/resources/plugins/plugin_poster.html index d5945d1314a..4a4b90c8bd3 100644 --- a/chromium/chrome/renderer/resources/plugins/plugin_poster.html +++ b/chromium/chrome/renderer/resources/plugins/plugin_poster.html @@ -21,7 +21,6 @@ #outer { border: none; cursor: pointer; - position: relative; } #shielding { @@ -34,11 +33,12 @@ z-index: 2; } -#plugin_icon { +#plugin-icon { + display: block; opacity: 0.8; } -#plugin_icon:hover { +#plugin-icon:hover { opacity: 0.95; } @@ -49,14 +49,14 @@ z-index: 1; } -#inner_container { - height: 100%; - position: relative; - width: 100%; -} - -#inner { - margin-top: -25px; +#inner-container { + align-items: center; + display: flex; + justify-content: center; + left: 0px; + position: absolute; + top: 0px; + z-index: 2; } </style> <base i18n-values="href:baseurl"> @@ -65,12 +65,13 @@ <body> <div i18n-values="title:name" id="outer"> <img id="poster" i18n-values="srcset:poster"> - <div id="shielding"> - <div id="inner_container" - i18n-values=".style.width:visibleWidth;.style.height:visibleHeight"> - <div id="inner"> - <img id="plugin_icon" src="plugin_power_saver_play.png" /> - </div> + <div id="shielding"></div> + <div id="inner-container" + i18n-values=".style.width:visibleWidth;.style.height:visibleHeight"> + <div id="inner"> + <img id="plugin-icon" src="plugin_power_saver_play.png" + i18n-values=".style.maxWidth:visibleWidth; + .style.maxHeight:visibleHeight" /> </div> </div> </div> @@ -84,7 +85,7 @@ }; window.setPosterMargin = function(marginLeft, marginTop) { - var container = document.getElementById('inner_container'); + var container = document.getElementById('inner-container'); container.style.marginLeft = marginLeft; container.style.marginTop = marginTop; }; diff --git a/chromium/components/web_cache/OWNERS b/chromium/components/web_cache/OWNERS new file mode 100644 index 00000000000..5948538407c --- /dev/null +++ b/chromium/components/web_cache/OWNERS @@ -0,0 +1,6 @@ +# Reviewers: +jochen@chromium.org +jhawkins@chromium.org +sky@chromium.org +thakis@chromium.org +thestig@chromium.org diff --git a/chromium/components/web_cache/browser/BUILD.gn b/chromium/components/web_cache/browser/BUILD.gn new file mode 100644 index 00000000000..b9ba85bd634 --- /dev/null +++ b/chromium/components/web_cache/browser/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +static_library("browser") { + output_name = "web_cache_browser" + sources = [ + "web_cache_manager.cc", + "web_cache_manager.h", + ] + + configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + + deps = [ + "//base", + "//components/web_cache/common", + "//content/public/browser", + "//third_party/WebKit/public:blink", + ] +} diff --git a/chromium/components/web_cache/browser/DEPS b/chromium/components/web_cache/browser/DEPS new file mode 100644 index 00000000000..f6417ed8f4e --- /dev/null +++ b/chromium/components/web_cache/browser/DEPS @@ -0,0 +1,5 @@ +include_rules = [ + "+content/public/browser", + "+content/public", + '+third_party/WebKit/public/web', +] diff --git a/chromium/components/web_cache/browser/web_cache_manager.cc b/chromium/components/web_cache/browser/web_cache_manager.cc new file mode 100644 index 00000000000..dfed012ebfb --- /dev/null +++ b/chromium/components/web_cache/browser/web_cache_manager.cc @@ -0,0 +1,443 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/web_cache/browser/web_cache_manager.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/location.h" +#include "base/memory/singleton.h" +#include "base/metrics/histogram_macros.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/single_thread_task_runner.h" +#include "base/sys_info.h" +#include "base/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "components/web_cache/common/web_cache_messages.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" + +using base::Time; +using base::TimeDelta; +using blink::WebCache; + +namespace web_cache { + +static const int kReviseAllocationDelayMS = 200; + +// The default size limit of the in-memory cache is 8 MB +static const int kDefaultMemoryCacheSize = 8 * 1024 * 1024; + +namespace { + +int GetDefaultCacheSize() { + // Start off with a modest default + int default_cache_size = kDefaultMemoryCacheSize; + + // Check how much physical memory the OS has + int mem_size_mb = base::SysInfo::AmountOfPhysicalMemoryMB(); + if (mem_size_mb >= 1000) // If we have a GB of memory, set a larger default. + default_cache_size *= 4; + else if (mem_size_mb >= 512) // With 512 MB, set a slightly larger default. + default_cache_size *= 2; + + UMA_HISTOGRAM_MEMORY_MB("Cache.MaxCacheSizeMB", + default_cache_size / 1024 / 1024); + + return default_cache_size; +} + +} // anonymous namespace + +// static +WebCacheManager* WebCacheManager::GetInstance() { + return Singleton<WebCacheManager>::get(); +} + +WebCacheManager::WebCacheManager() + : global_size_limit_(GetDefaultGlobalSizeLimit()), + weak_factory_(this) { + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED, + content::NotificationService::AllBrowserContextsAndSources()); + registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, + content::NotificationService::AllBrowserContextsAndSources()); +} + +WebCacheManager::~WebCacheManager() { +} + +void WebCacheManager::Add(int renderer_id) { + DCHECK(inactive_renderers_.count(renderer_id) == 0); + + // It is tempting to make the following DCHECK here, but it fails when a new + // tab is created as we observe activity from that tab because the + // RenderProcessHost is recreated and adds itself. + // + // DCHECK(active_renderers_.count(renderer_id) == 0); + // + // However, there doesn't seem to be much harm in receiving the calls in this + // order. + + active_renderers_.insert(renderer_id); + + RendererInfo* stats = &(stats_[renderer_id]); + memset(stats, 0, sizeof(*stats)); + stats->access = Time::Now(); + + // Revise our allocation strategy to account for this new renderer. + ReviseAllocationStrategyLater(); +} + +void WebCacheManager::Remove(int renderer_id) { + // Erase all knowledge of this renderer + active_renderers_.erase(renderer_id); + inactive_renderers_.erase(renderer_id); + stats_.erase(renderer_id); + + // Reallocate the resources used by this renderer + ReviseAllocationStrategyLater(); +} + +void WebCacheManager::ObserveActivity(int renderer_id) { + StatsMap::iterator item = stats_.find(renderer_id); + if (item == stats_.end()) + return; // We might see stats for a renderer that has been destroyed. + + // Record activity. + active_renderers_.insert(renderer_id); + item->second.access = Time::Now(); + + std::set<int>::iterator elmt = inactive_renderers_.find(renderer_id); + if (elmt != inactive_renderers_.end()) { + inactive_renderers_.erase(elmt); + + // A renderer that was inactive, just became active. We should make sure + // it is given a fair cache allocation, but we defer this for a bit in + // order to make this function call cheap. + ReviseAllocationStrategyLater(); + } +} + +void WebCacheManager::ObserveStats(int renderer_id, + const WebCache::UsageStats& stats) { + StatsMap::iterator entry = stats_.find(renderer_id); + if (entry == stats_.end()) + return; // We might see stats for a renderer that has been destroyed. + + // Record the updated stats. + entry->second.capacity = stats.capacity; + entry->second.deadSize = stats.deadSize; + entry->second.liveSize = stats.liveSize; + entry->second.maxDeadCapacity = stats.maxDeadCapacity; + entry->second.minDeadCapacity = stats.minDeadCapacity; +} + +void WebCacheManager::SetGlobalSizeLimit(size_t bytes) { + global_size_limit_ = bytes; + ReviseAllocationStrategyLater(); +} + +void WebCacheManager::ClearCache() { + // Tell each renderer process to clear the cache. + ClearRendererCache(active_renderers_, INSTANTLY); + ClearRendererCache(inactive_renderers_, INSTANTLY); +} + +void WebCacheManager::ClearCacheOnNavigation() { + // Tell each renderer process to clear the cache when a tab is reloaded or + // the user navigates to a new website. + ClearRendererCache(active_renderers_, ON_NAVIGATION); + ClearRendererCache(inactive_renderers_, ON_NAVIGATION); +} + +void WebCacheManager::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + switch (type) { + case content::NOTIFICATION_RENDERER_PROCESS_CREATED: { + content::RenderProcessHost* process = + content::Source<content::RenderProcessHost>(source).ptr(); + Add(process->GetID()); + break; + } + case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { + content::RenderProcessHost* process = + content::Source<content::RenderProcessHost>(source).ptr(); + Remove(process->GetID()); + break; + } + default: + NOTREACHED(); + break; + } +} + +// static +size_t WebCacheManager::GetDefaultGlobalSizeLimit() { + return GetDefaultCacheSize(); +} + +void WebCacheManager::GatherStats(const std::set<int>& renderers, + WebCache::UsageStats* stats) { + DCHECK(stats); + + memset(stats, 0, sizeof(WebCache::UsageStats)); + + std::set<int>::const_iterator iter = renderers.begin(); + while (iter != renderers.end()) { + StatsMap::iterator elmt = stats_.find(*iter); + if (elmt != stats_.end()) { + stats->minDeadCapacity += elmt->second.minDeadCapacity; + stats->maxDeadCapacity += elmt->second.maxDeadCapacity; + stats->capacity += elmt->second.capacity; + stats->liveSize += elmt->second.liveSize; + stats->deadSize += elmt->second.deadSize; + } + ++iter; + } +} + +// static +size_t WebCacheManager::GetSize(AllocationTactic tactic, + const WebCache::UsageStats& stats) { + switch (tactic) { + case DIVIDE_EVENLY: + // We aren't going to reserve any space for existing objects. + return 0; + case KEEP_CURRENT_WITH_HEADROOM: + // We need enough space for our current objects, plus some headroom. + return 3 * GetSize(KEEP_CURRENT, stats) / 2; + case KEEP_CURRENT: + // We need enough space to keep our current objects. + return stats.liveSize + stats.deadSize; + case KEEP_LIVE_WITH_HEADROOM: + // We need enough space to keep out live resources, plus some headroom. + return 3 * GetSize(KEEP_LIVE, stats) / 2; + case KEEP_LIVE: + // We need enough space to keep our live resources. + return stats.liveSize; + default: + NOTREACHED() << "Unknown cache allocation tactic"; + return 0; + } +} + +bool WebCacheManager::AttemptTactic( + AllocationTactic active_tactic, + const WebCache::UsageStats& active_stats, + AllocationTactic inactive_tactic, + const WebCache::UsageStats& inactive_stats, + AllocationStrategy* strategy) { + DCHECK(strategy); + + size_t active_size = GetSize(active_tactic, active_stats); + size_t inactive_size = GetSize(inactive_tactic, inactive_stats); + + // Give up if we don't have enough space to use this tactic. + if (global_size_limit_ < active_size + inactive_size) + return false; + + // Compute the unreserved space available. + size_t total_extra = global_size_limit_ - (active_size + inactive_size); + + // The plan for the extra space is to divide it evenly amoung the active + // renderers. + size_t shares = active_renderers_.size(); + + // The inactive renderers get one share of the extra memory to be divided + // among themselves. + size_t inactive_extra = 0; + if (!inactive_renderers_.empty()) { + ++shares; + inactive_extra = total_extra / shares; + } + + // The remaining memory is allocated to the active renderers. + size_t active_extra = total_extra - inactive_extra; + + // Actually compute the allocations for each renderer. + AddToStrategy(active_renderers_, active_tactic, active_extra, strategy); + AddToStrategy(inactive_renderers_, inactive_tactic, inactive_extra, strategy); + + // We succeeded in computing an allocation strategy. + return true; +} + +void WebCacheManager::AddToStrategy(const std::set<int>& renderers, + AllocationTactic tactic, + size_t extra_bytes_to_allocate, + AllocationStrategy* strategy) { + DCHECK(strategy); + + // Nothing to do if there are no renderers. It is common for there to be no + // inactive renderers if there is a single active tab. + if (renderers.empty()) + return; + + // Divide the extra memory evenly among the renderers. + size_t extra_each = extra_bytes_to_allocate / renderers.size(); + + std::set<int>::const_iterator iter = renderers.begin(); + while (iter != renderers.end()) { + size_t cache_size = extra_each; + + // Add in the space required to implement |tactic|. + StatsMap::iterator elmt = stats_.find(*iter); + if (elmt != stats_.end()) + cache_size += GetSize(tactic, elmt->second); + + // Record the allocation in our strategy. + strategy->push_back(Allocation(*iter, cache_size)); + ++iter; + } +} + +void WebCacheManager::EnactStrategy(const AllocationStrategy& strategy) { + // Inform each render process of its cache allocation. + AllocationStrategy::const_iterator allocation = strategy.begin(); + while (allocation != strategy.end()) { + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(allocation->first); + if (host) { + // This is the capacity this renderer has been allocated. + size_t capacity = allocation->second; + + // We don't reserve any space for dead objects in the cache. Instead, we + // prefer to keep live objects around. There is probably some performance + // tuning to be done here. + size_t min_dead_capacity = 0; + + // We allow the dead objects to consume up to half of the cache capacity. + size_t max_dead_capacity = capacity / 2; + if (base::SysInfo::IsLowEndDevice()) { + max_dead_capacity = std::min(static_cast<size_t>(512 * 1024), + max_dead_capacity); + } + host->Send(new WebCacheMsg_SetCacheCapacities(min_dead_capacity, + max_dead_capacity, + capacity)); + } + ++allocation; + } +} + +void WebCacheManager::ClearCacheForProcess(int render_process_id) { + std::set<int> renderers; + renderers.insert(render_process_id); + ClearRendererCache(renderers, INSTANTLY); +} + +void WebCacheManager::ClearRendererCache( + const std::set<int>& renderers, + WebCacheManager::ClearCacheOccasion occasion) { + std::set<int>::const_iterator iter = renderers.begin(); + for (; iter != renderers.end(); ++iter) { + content::RenderProcessHost* host = + content::RenderProcessHost::FromID(*iter); + if (host) + host->Send(new WebCacheMsg_ClearCache(occasion == ON_NAVIGATION)); + } +} + +void WebCacheManager::ReviseAllocationStrategy() { + DCHECK(stats_.size() <= + active_renderers_.size() + inactive_renderers_.size()); + + // Check if renderers have gone inactive. + FindInactiveRenderers(); + + // Gather statistics + WebCache::UsageStats active; + WebCache::UsageStats inactive; + GatherStats(active_renderers_, &active); + GatherStats(inactive_renderers_, &inactive); + + UMA_HISTOGRAM_COUNTS_100("Cache.ActiveTabs", active_renderers_.size()); + UMA_HISTOGRAM_COUNTS_100("Cache.InactiveTabs", inactive_renderers_.size()); + UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveCapacityMB", + active.capacity / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveDeadSizeMB", + active.deadSize / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.ActiveLiveSizeMB", + active.liveSize / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveCapacityMB", + inactive.capacity / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveDeadSizeMB", + inactive.deadSize / 1024 / 1024); + UMA_HISTOGRAM_MEMORY_MB("Cache.InactiveLiveSizeMB", + inactive.liveSize / 1024 / 1024); + + // Compute an allocation strategy. + // + // We attempt various tactics in order of preference. Our first preference + // is not to evict any objects. If we don't have enough resources, we'll + // first try to evict dead data only. If that fails, we'll just divide the + // resources we have evenly. + // + // We always try to give the active renderers some head room in their + // allocations so they can take memory away from an inactive renderer with + // a large cache allocation. + // + // Notice the early exit will prevent attempting less desirable tactics once + // we've found a workable strategy. + AllocationStrategy strategy; + if ( // Ideally, we'd like to give the active renderers some headroom and + // keep all our current objects. + AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active, + KEEP_CURRENT, inactive, &strategy) || + // If we can't have that, then we first try to evict the dead objects in + // the caches of inactive renderers. + AttemptTactic(KEEP_CURRENT_WITH_HEADROOM, active, + KEEP_LIVE, inactive, &strategy) || + // Next, we try to keep the live objects in the active renders (with some + // room for new objects) and give whatever is left to the inactive + // renderers. + AttemptTactic(KEEP_LIVE_WITH_HEADROOM, active, + DIVIDE_EVENLY, inactive, &strategy) || + // If we've gotten this far, then we are very tight on memory. Let's try + // to at least keep around the live objects for the active renderers. + AttemptTactic(KEEP_LIVE, active, DIVIDE_EVENLY, inactive, &strategy) || + // We're basically out of memory. The best we can do is just divide up + // what we have and soldier on. + AttemptTactic(DIVIDE_EVENLY, active, DIVIDE_EVENLY, inactive, + &strategy)) { + // Having found a workable strategy, we enact it. + EnactStrategy(strategy); + } else { + // DIVIDE_EVENLY / DIVIDE_EVENLY should always succeed. + NOTREACHED() << "Unable to find a cache allocation"; + } +} + +void WebCacheManager::ReviseAllocationStrategyLater() { + // Ask to be called back in a few milliseconds to actually recompute our + // allocation. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&WebCacheManager::ReviseAllocationStrategy, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(kReviseAllocationDelayMS)); +} + +void WebCacheManager::FindInactiveRenderers() { + std::set<int>::const_iterator iter = active_renderers_.begin(); + while (iter != active_renderers_.end()) { + StatsMap::iterator elmt = stats_.find(*iter); + DCHECK(elmt != stats_.end()); + TimeDelta idle = Time::Now() - elmt->second.access; + if (idle >= TimeDelta::FromMinutes(kRendererInactiveThresholdMinutes)) { + // Moved to inactive status. This invalidates our iterator. + inactive_renderers_.insert(*iter); + active_renderers_.erase(*iter); + iter = active_renderers_.begin(); + continue; + } + ++iter; + } +} + +} // namespace web_cache diff --git a/chromium/components/web_cache/browser/web_cache_manager.h b/chromium/components/web_cache/browser/web_cache_manager.h new file mode 100644 index 00000000000..0d2d3ffb644 --- /dev/null +++ b/chromium/components/web_cache/browser/web_cache_manager.h @@ -0,0 +1,244 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is the browser side of the cache manager, it tracks the activity of the +// render processes and allocates available memory cache resources. + +#ifndef COMPONENTS_WEB_CACHE_BROWSER_WEB_CACHE_MANAGER_H_ +#define COMPONENTS_WEB_CACHE_BROWSER_WEB_CACHE_MANAGER_H_ + +#include <list> +#include <map> +#include <set> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/memory/weak_ptr.h" +#include "base/time/time.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "third_party/WebKit/public/web/WebCache.h" + +template<typename Type> +struct DefaultSingletonTraits; +class PrefRegistrySimple; + +namespace web_cache { + +class WebCacheManager : public content::NotificationObserver { + friend class WebCacheManagerTest; + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_1); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_2); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_3); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_4); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_5); + FRIEND_TEST_ALL_PREFIXES( + WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_6); + + public: + // Gets the singleton WebCacheManager object. The first time this method + // is called, a WebCacheManager object is constructed and returned. + // Subsequent calls will return the same object. + static WebCacheManager* GetInstance(); + + // When a render process is created, it registers itself with the cache + // manager host, causing the renderer to be allocated cache resources. + void Add(int renderer_id); + + // When a render process ends, it removes itself from the cache manager host, + // freeing the manager to assign its cache resources to other renderers. + void Remove(int renderer_id); + + // The cache manager assigns more cache resources to active renderer. When a + // renderer is active, it should inform the cache manager to receive more + // cache resources. + // + // When a renderer moves from being inactive to being active, the cache + // manager may decide to adjust its resource allocation, but it will delay + // the recalculation, allowing ObserveActivity to return quickly. + void ObserveActivity(int renderer_id); + + // Periodically, renderers should inform the cache manager of their current + // statistics. The more up-to-date the cache manager's statistics, the + // better it can allocate cache resources. + void ObserveStats( + int renderer_id, const blink::WebCache::UsageStats& stats); + + // The global limit on the number of bytes in all the in-memory caches. + size_t global_size_limit() const { return global_size_limit_; } + + // Sets the global size limit, forcing a recalculation of cache allocations. + void SetGlobalSizeLimit(size_t bytes); + + // Clears all in-memory caches. + void ClearCache(); + + // Instantly clears renderer cache for a process. + void ClearCacheForProcess(int process_id); + + // Clears all in-memory caches when a tab is reloaded or the user navigates + // to a different website. + void ClearCacheOnNavigation(); + + // content::NotificationObserver implementation: + void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) override; + + // Gets the default global size limit. This interrogates system metrics to + // tune the default size to the current system. + static size_t GetDefaultGlobalSizeLimit(); + + protected: + // The amount of idle time before we consider a tab to be "inactive" + static const int kRendererInactiveThresholdMinutes = 5; + + // Keep track of some renderer information. + struct RendererInfo : blink::WebCache::UsageStats { + // The access time for this renderer. + base::Time access; + }; + + typedef std::map<int, RendererInfo> StatsMap; + + // An allocation is the number of bytes a specific renderer should use for + // its cache. + typedef std::pair<int,size_t> Allocation; + + // An allocation strategy is a list of allocations specifying the resources + // each renderer is permitted to consume for its cache. + typedef std::list<Allocation> AllocationStrategy; + + // This class is a singleton. Do not instantiate directly. + WebCacheManager(); + friend struct DefaultSingletonTraits<WebCacheManager>; + + ~WebCacheManager() override; + + // Recomputes the allocation of cache resources among the renderers. Also + // informs the renderers of their new allocation. + void ReviseAllocationStrategy(); + + // Schedules a call to ReviseAllocationStrategy after a short delay. + void ReviseAllocationStrategyLater(); + + // The various tactics used as part of an allocation strategy. To decide + // how many resources a given renderer should be allocated, we consider its + // usage statistics. Each tactic specifies the function that maps usage + // statistics to resource allocations. + // + // Determining a resource allocation strategy amounts to picking a tactic + // for each renderer and checking that the total memory required fits within + // our |global_size_limit_|. + enum AllocationTactic { + // Ignore cache statistics and divide resources equally among the given + // set of caches. + DIVIDE_EVENLY, + + // Allow each renderer to keep its current set of cached resources, with + // some extra allocation to store new objects. + KEEP_CURRENT_WITH_HEADROOM, + + // Allow each renderer to keep its current set of cached resources. + KEEP_CURRENT, + + // Allow each renderer to keep cache resources it believes are currently + // being used, with some extra allocation to store new objects. + KEEP_LIVE_WITH_HEADROOM, + + // Allow each renderer to keep cache resources it believes are currently + // being used, but instruct the renderer to discard all other data. + KEEP_LIVE, + }; + + // Helper functions for devising an allocation strategy + + // Add up all the stats from the given set of renderers and place the result + // in |stats|. + void GatherStats(const std::set<int>& renderers, + blink::WebCache::UsageStats* stats); + + // Get the amount of memory that would be required to implement |tactic| + // using the specified allocation tactic. This function defines the + // semantics for each of the tactics. + static size_t GetSize(AllocationTactic tactic, + const blink::WebCache::UsageStats& stats); + + // Attempt to use the specified tactics to compute an allocation strategy + // and place the result in |strategy|. |active_stats| and |inactive_stats| + // are the aggregate statistics for |active_renderers_| and + // |inactive_renderers_|, respectively. + // + // Returns |true| on success and |false| on failure. Does not modify + // |strategy| on failure. + bool AttemptTactic(AllocationTactic active_tactic, + const blink::WebCache::UsageStats& active_stats, + AllocationTactic inactive_tactic, + const blink::WebCache::UsageStats& inactive_stats, + AllocationStrategy* strategy); + + // For each renderer in |renderers|, computes its allocation according to + // |tactic| and add the result to |strategy|. Any |extra_bytes_to_allocate| + // is divided evenly among the renderers. + void AddToStrategy(const std::set<int>& renderers, + AllocationTactic tactic, + size_t extra_bytes_to_allocate, + AllocationStrategy* strategy); + + // Enact an allocation strategy by informing the renderers of their + // allocations according to |strategy|. + void EnactStrategy(const AllocationStrategy& strategy); + + enum ClearCacheOccasion { + // Instructs to clear the cache instantly. + INSTANTLY, + // Instructs to clear the cache when a navigation takes place (this + // includes reloading a tab). + ON_NAVIGATION + }; + + // Inform all |renderers| to clear their cache. + void ClearRendererCache(const std::set<int>& renderers, + ClearCacheOccasion occation); + + // Check to see if any active renderers have fallen inactive. + void FindInactiveRenderers(); + + // The global size limit for all in-memory caches. + size_t global_size_limit_; + + // Maps every renderer_id our most recent copy of its statistics. + StatsMap stats_; + + // Every renderer we think is still around is in one of these two sets. + // + // Active renderers are those renderers that have been active more recently + // than they have been inactive. + std::set<int> active_renderers_; + // Inactive renderers are those renderers that have been inactive more + // recently than they have been active. + std::set<int> inactive_renderers_; + + content::NotificationRegistrar registrar_; + + base::WeakPtrFactory<WebCacheManager> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(WebCacheManager); +}; + +} // namespace web_cache + +#endif // COMPONENTS_WEB_CACHE_BROWSER_WEB_CACHE_MANAGER_H_ diff --git a/chromium/components/web_cache/browser/web_cache_manager_unittest.cc b/chromium/components/web_cache/browser/web_cache_manager_unittest.cc new file mode 100644 index 00000000000..bc8bcbd5076 --- /dev/null +++ b/chromium/components/web_cache/browser/web_cache_manager_unittest.cc @@ -0,0 +1,466 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/message_loop/message_loop.h" +#include "components/web_cache/browser/web_cache_manager.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; +using content::BrowserThread; +using blink::WebCache; + +namespace web_cache { + +class WebCacheManagerTest : public testing::Test { + protected: + typedef WebCacheManager::StatsMap StatsMap; + typedef WebCacheManager::Allocation Allocation; + typedef WebCacheManager::AllocationStrategy AllocationStrategy; + + static const int kRendererID; + static const int kRendererID2; + static const WebCache::UsageStats kStats; + static const WebCache::UsageStats kStats2; + + WebCacheManagerTest() + : ui_thread_(BrowserThread::UI, &message_loop_) { + } + + // Thunks to access protected members of WebCacheManager + static std::map<int, WebCacheManager::RendererInfo>& stats( + WebCacheManager* h) { + return h->stats_; + } + + static void SimulateInactivity(WebCacheManager* h, int renderer_id) { + stats(h)[renderer_id].access = Time::Now() - TimeDelta::FromMinutes( + WebCacheManager::kRendererInactiveThresholdMinutes); + h->FindInactiveRenderers(); + } + + static std::set<int>& active_renderers(WebCacheManager* h) { + return h->active_renderers_; + } + static std::set<int>& inactive_renderers(WebCacheManager* h) { + return h->inactive_renderers_; + } + static void GatherStats(WebCacheManager* h, + std::set<int> renderers, + WebCache::UsageStats* stats) { + h->GatherStats(renderers, stats); + } + static size_t GetSize(int tactic, + const WebCache::UsageStats& stats) { + return WebCacheManager::GetSize( + static_cast<WebCacheManager::AllocationTactic>(tactic), stats); + } + static bool AttemptTactic(WebCacheManager* h, + int active_tactic, + const WebCache::UsageStats& active_stats, + int inactive_tactic, + const WebCache::UsageStats& inactive_stats, + std::list< std::pair<int,size_t> >* strategy) { + return h->AttemptTactic( + static_cast<WebCacheManager::AllocationTactic>(active_tactic), + active_stats, + static_cast<WebCacheManager::AllocationTactic>(inactive_tactic), + inactive_stats, + strategy); + } + static void AddToStrategy(WebCacheManager* h, + std::set<int> renderers, + int tactic, + size_t extra_bytes_to_allocate, + std::list< std::pair<int,size_t> >* strategy) { + h->AddToStrategy(renderers, + static_cast<WebCacheManager::AllocationTactic>(tactic), + extra_bytes_to_allocate, + strategy); + } + + enum { + DIVIDE_EVENLY = WebCacheManager::DIVIDE_EVENLY, + KEEP_CURRENT_WITH_HEADROOM = WebCacheManager::KEEP_CURRENT_WITH_HEADROOM, + KEEP_CURRENT = WebCacheManager::KEEP_CURRENT, + KEEP_LIVE_WITH_HEADROOM = WebCacheManager::KEEP_LIVE_WITH_HEADROOM, + KEEP_LIVE = WebCacheManager::KEEP_LIVE, + }; + + WebCacheManager* manager() { return &manager_; } + + private: + WebCacheManager manager_; + base::MessageLoop message_loop_; + content::TestBrowserThread ui_thread_; +}; + +// static +const int WebCacheManagerTest::kRendererID = 146; + +// static +const int WebCacheManagerTest::kRendererID2 = 245; + +// static +const WebCache::UsageStats WebCacheManagerTest::kStats = { + 0, + 1024 * 1024, + 1024 * 1024, + 256 * 1024, + 512, + }; + +// static +const WebCache::UsageStats WebCacheManagerTest::kStats2 = { + 0, + 2 * 1024 * 1024, + 2 * 1024 * 1024, + 2 * 256 * 1024, + 2 * 512, + }; + +static bool operator==(const WebCache::UsageStats& lhs, + const WebCache::UsageStats& rhs) { + return !::memcmp(&lhs, &rhs, sizeof(WebCache::UsageStats)); +} + +TEST_F(WebCacheManagerTest, AddRemoveRendererTest) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + manager()->Add(kRendererID); + EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID)); + + manager()->Remove(kRendererID); + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); +} + +TEST_F(WebCacheManagerTest, ActiveInactiveTest) { + manager()->Add(kRendererID); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID)); + + SimulateInactivity(manager(), kRendererID); + EXPECT_EQ(0U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(1U, inactive_renderers(manager()).count(kRendererID)); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(1U, active_renderers(manager()).count(kRendererID)); + EXPECT_EQ(0U, inactive_renderers(manager()).count(kRendererID)); + + manager()->Remove(kRendererID); +} + +TEST_F(WebCacheManagerTest, ObserveStatsTest) { + manager()->Add(kRendererID); + + EXPECT_EQ(1U, stats(manager()).size()); + + manager()->ObserveStats(kRendererID, kStats); + + EXPECT_EQ(1U, stats(manager()).size()); + EXPECT_TRUE(kStats == stats(manager())[kRendererID]); + + manager()->Remove(kRendererID); +} + +TEST_F(WebCacheManagerTest, SetGlobalSizeLimitTest) { + size_t limit = manager()->GetDefaultGlobalSizeLimit(); + manager()->SetGlobalSizeLimit(limit); + EXPECT_EQ(limit, manager()->global_size_limit()); + + manager()->SetGlobalSizeLimit(0); + EXPECT_EQ(0U, manager()->global_size_limit()); +} + +TEST_F(WebCacheManagerTest, GatherStatsTest) { + manager()->Add(kRendererID); + manager()->Add(kRendererID2); + + manager()->ObserveStats(kRendererID, kStats); + manager()->ObserveStats(kRendererID2, kStats2); + + std::set<int> renderer_set; + renderer_set.insert(kRendererID); + + WebCache::UsageStats stats; + GatherStats(manager(), renderer_set, &stats); + + EXPECT_TRUE(kStats == stats); + + renderer_set.insert(kRendererID2); + GatherStats(manager(), renderer_set, &stats); + + WebCache::UsageStats expected_stats = kStats; + expected_stats.minDeadCapacity += kStats2.minDeadCapacity; + expected_stats.maxDeadCapacity += kStats2.maxDeadCapacity; + expected_stats.capacity += kStats2.capacity; + expected_stats.liveSize += kStats2.liveSize; + expected_stats.deadSize += kStats2.deadSize; + + EXPECT_TRUE(expected_stats == stats); + + manager()->Remove(kRendererID); + manager()->Remove(kRendererID2); +} + +TEST_F(WebCacheManagerTest, GetSizeTest) { + EXPECT_EQ(0U, GetSize(DIVIDE_EVENLY, kStats)); + EXPECT_LT(256 * 1024u + 512, GetSize(KEEP_CURRENT_WITH_HEADROOM, kStats)); + EXPECT_EQ(256 * 1024u + 512, GetSize(KEEP_CURRENT, kStats)); + EXPECT_LT(256 * 1024u, GetSize(KEEP_LIVE_WITH_HEADROOM, kStats)); + EXPECT_EQ(256 * 1024u, GetSize(KEEP_LIVE, kStats)); +} + +TEST_F(WebCacheManagerTest, AttemptTacticTest) { + manager()->Add(kRendererID); + manager()->Add(kRendererID2); + + manager()->ObserveActivity(kRendererID); + SimulateInactivity(manager(), kRendererID2); + + manager()->ObserveStats(kRendererID, kStats); + manager()->ObserveStats(kRendererID2, kStats2); + + manager()->SetGlobalSizeLimit(kStats.liveSize + kStats.deadSize + + kStats2.liveSize + kStats2.deadSize/2); + + AllocationStrategy strategy; + + EXPECT_FALSE(AttemptTactic(manager(), + KEEP_CURRENT, + kStats, + KEEP_CURRENT, + kStats2, + &strategy)); + EXPECT_TRUE(strategy.empty()); + + EXPECT_TRUE(AttemptTactic(manager(), + KEEP_CURRENT, + kStats, + KEEP_LIVE, + kStats2, + &strategy)); + EXPECT_EQ(2U, strategy.size()); + + AllocationStrategy::iterator iter = strategy.begin(); + while (iter != strategy.end()) { + if (iter->first == kRendererID) + EXPECT_LE(kStats.liveSize + kStats.deadSize, iter->second); + else if (iter->first == kRendererID2) + EXPECT_LE(kStats2.liveSize, iter->second); + else + ADD_FAILURE(); // Unexpected entry in strategy. + ++iter; + } + + manager()->Remove(kRendererID); + manager()->Remove(kRendererID2); +} + +TEST_F(WebCacheManagerTest, AddToStrategyTest) { + manager()->Add(kRendererID); + manager()->Add(kRendererID2); + + std::set<int> renderer_set; + renderer_set.insert(kRendererID); + renderer_set.insert(kRendererID2); + + manager()->ObserveStats(kRendererID, kStats); + manager()->ObserveStats(kRendererID2, kStats2); + + const size_t kExtraBytesToAllocate = 10 * 1024; + + AllocationStrategy strategy; + AddToStrategy(manager(), + renderer_set, + KEEP_CURRENT, + kExtraBytesToAllocate, + &strategy); + + EXPECT_EQ(2U, strategy.size()); + + size_t total_bytes = 0; + AllocationStrategy::iterator iter = strategy.begin(); + while (iter != strategy.end()) { + total_bytes += iter->second; + + if (iter->first == kRendererID) + EXPECT_LE(kStats.liveSize + kStats.deadSize, iter->second); + else if (iter->first == kRendererID2) + EXPECT_LE(kStats2.liveSize + kStats2.deadSize, iter->second); + else + ADD_FAILURE(); // Unexpected entry in strategy. + ++iter; + } + + size_t expected_total_bytes = kExtraBytesToAllocate + + kStats.liveSize + kStats.deadSize + + kStats2.liveSize + kStats2.deadSize; + + EXPECT_GE(expected_total_bytes, total_bytes); + + manager()->Remove(kRendererID); + manager()->Remove(kRendererID2); +} + +// Regression test for http://crbug.com/12362. +// There are three operations in the following order will cause the crash: +// Remove(kRendererID) -> ObserveActivity(kRendererID) -> Remove(kRendererID2) +// To prevent similar failures in the future, 6 tests are added in total to +// cover all the possible orderings of these three operations. +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_1) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + // The following order will cause a crash in http://crbug.com/12362. + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_2) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); + +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_3) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_4) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_5) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); +} + +TEST_F(WebCacheManagerTest, + CallRemoveRendererAndObserveActivityInAnyOrderShouldNotCrashTest_6) { + EXPECT_EQ(0U, active_renderers(manager()).size()); + EXPECT_EQ(0U, inactive_renderers(manager()).size()); + + // Add, Remove, and ObserveActivity trigger deferred + // calls to ReviseAllocationStrategy and that we call it directly after each + // operation to sidestep the need to wait for an unobservable background + // operation. + manager()->Add(kRendererID); + manager()->ReviseAllocationStrategy(); + manager()->Add(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID2); + manager()->ReviseAllocationStrategy(); + + manager()->Remove(kRendererID); + manager()->ReviseAllocationStrategy(); + + manager()->ObserveActivity(kRendererID); + manager()->ReviseAllocationStrategy(); +} + +} // namespace web_cache diff --git a/chromium/components/web_cache/common/BUILD.gn b/chromium/components/web_cache/common/BUILD.gn new file mode 100644 index 00000000000..5c7b2ef196c --- /dev/null +++ b/chromium/components/web_cache/common/BUILD.gn @@ -0,0 +1,16 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +static_library("common") { + output_name = "web_cache_common" + sources = [ + "web_cache_message_generator.cc", + "web_cache_message_generator.h", + "web_cache_messages.h", + ] + + deps = [ + "//ipc", + ] +} diff --git a/chromium/components/web_cache/common/DEPS b/chromium/components/web_cache/common/DEPS new file mode 100644 index 00000000000..1c40d981eb6 --- /dev/null +++ b/chromium/components/web_cache/common/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+ipc", +] diff --git a/chromium/components/web_cache/common/OWNERS b/chromium/components/web_cache/common/OWNERS new file mode 100644 index 00000000000..5ecb4069fa5 --- /dev/null +++ b/chromium/components/web_cache/common/OWNERS @@ -0,0 +1,11 @@ +# Changes to IPC messages require a security review to avoid introducing +# new sandbox escapes. +per-file *_messages*.h=set noparent +per-file *_messages*.h=dcheng@chromium.org +per-file *_messages*.h=inferno@chromium.org +per-file *_messages*.h=jln@chromium.org +per-file *_messages*.h=jschuh@chromium.org +per-file *_messages*.h=kenrb@chromium.org +per-file *_messages*.h=nasko@chromium.org +per-file *_messages*.h=tsepez@chromium.org +per-file *_messages*.h=wfh@chromium.org diff --git a/chromium/components/web_cache/common/web_cache_message_generator.cc b/chromium/components/web_cache/common/web_cache_message_generator.cc new file mode 100644 index 00000000000..b4b2d265674 --- /dev/null +++ b/chromium/components/web_cache/common/web_cache_message_generator.cc @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Get basic type definitions. +#define IPC_MESSAGE_IMPL +#include "components/web_cache/common/web_cache_message_generator.h" + +// Generate constructors. +#include "ipc/struct_constructor_macros.h" +#include "components/web_cache/common/web_cache_message_generator.h" + +// Generate destructors. +#include "ipc/struct_destructor_macros.h" +#include "components/web_cache/common/web_cache_message_generator.h" + +// Generate param traits write methods. +#include "ipc/param_traits_write_macros.h" +namespace IPC { +#include "components/web_cache/common/web_cache_message_generator.h" +} // namespace IPC + +// Generate param traits read methods. +#include "ipc/param_traits_read_macros.h" +namespace IPC { +#include "components/web_cache/common/web_cache_message_generator.h" +} // namespace IPC + +// Generate param traits log methods. +#include "ipc/param_traits_log_macros.h" +namespace IPC { +#include "components/web_cache/common/web_cache_message_generator.h" +} // namespace IPC diff --git a/chromium/components/web_cache/common/web_cache_message_generator.h b/chromium/components/web_cache/common/web_cache_message_generator.h new file mode 100644 index 00000000000..d88eb86447e --- /dev/null +++ b/chromium/components/web_cache/common/web_cache_message_generator.h @@ -0,0 +1,7 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Multiply-included file, no traditional include guard. + +#include "components/web_cache/common/web_cache_messages.h" diff --git a/chromium/components/web_cache/common/web_cache_messages.h b/chromium/components/web_cache/common/web_cache_messages.h new file mode 100644 index 00000000000..a521399101c --- /dev/null +++ b/chromium/components/web_cache/common/web_cache_messages.h @@ -0,0 +1,23 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Multiply-included file, no traditional include guard. +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" + +#define IPC_MESSAGE_START WebCacheMsgStart + +//----------------------------------------------------------------------------- +// RenderView messages +// These are messages sent from the browser to the renderer process. + +// Tells the renderer to set its maximum cache size to the supplied value. +IPC_MESSAGE_CONTROL3(WebCacheMsg_SetCacheCapacities, + size_t /* min_dead_capacity */, + size_t /* max_dead_capacity */, + size_t /* capacity */) + +// Tells the renderer to clear the cache. +IPC_MESSAGE_CONTROL1(WebCacheMsg_ClearCache, + bool /* on_navigation */) diff --git a/chromium/components/web_cache/renderer/BUILD.gn b/chromium/components/web_cache/renderer/BUILD.gn new file mode 100644 index 00000000000..cdd703a8145 --- /dev/null +++ b/chromium/components/web_cache/renderer/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +static_library("renderer") { + output_name = "web_cache_renderer" + sources = [ + "web_cache_render_process_observer.cc", + "web_cache_render_process_observer.h", + ] + + deps = [ + "//components/web_cache/common", + "//content/public/renderer", + "//third_party/WebKit/public:blink", + ] +} diff --git a/chromium/components/web_cache/renderer/DEPS b/chromium/components/web_cache/renderer/DEPS new file mode 100644 index 00000000000..100b3d3a109 --- /dev/null +++ b/chromium/components/web_cache/renderer/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+content/public/renderer", + "+third_party/WebKit/public/web", +] diff --git a/chromium/components/web_cache/renderer/web_cache_render_process_observer.cc b/chromium/components/web_cache/renderer/web_cache_render_process_observer.cc new file mode 100644 index 00000000000..a273817a2c0 --- /dev/null +++ b/chromium/components/web_cache/renderer/web_cache_render_process_observer.cc @@ -0,0 +1,84 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/web_cache/renderer/web_cache_render_process_observer.h" + +#include <limits> + +#include "components/web_cache/common/web_cache_messages.h" +#include "third_party/WebKit/public/web/WebCache.h" + +using blink::WebCache; + +namespace web_cache { + +namespace { +const size_t kUnitializedCacheCapacity = UINT_MAX; +} + +WebCacheRenderProcessObserver::WebCacheRenderProcessObserver() + : clear_cache_pending_(false), + webkit_initialized_(false), + pending_cache_min_dead_capacity_(0), + pending_cache_max_dead_capacity_(0), + pending_cache_capacity_(kUnitializedCacheCapacity) { +} + +WebCacheRenderProcessObserver::~WebCacheRenderProcessObserver() { +} + +void WebCacheRenderProcessObserver::ExecutePendingClearCache() { + if (clear_cache_pending_ && webkit_initialized_) { + clear_cache_pending_ = false; + WebCache::clear(); + } +} + +bool WebCacheRenderProcessObserver::OnControlMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(WebCacheRenderProcessObserver, message) + IPC_MESSAGE_HANDLER(WebCacheMsg_SetCacheCapacities, OnSetCacheCapacities) + IPC_MESSAGE_HANDLER(WebCacheMsg_ClearCache, OnClearCache) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void WebCacheRenderProcessObserver::WebKitInitialized() { + webkit_initialized_ = true; + if (pending_cache_capacity_ != kUnitializedCacheCapacity) { + WebCache::setCapacities(pending_cache_min_dead_capacity_, + pending_cache_max_dead_capacity_, + pending_cache_capacity_); + } +} + +void WebCacheRenderProcessObserver::OnRenderProcessShutdown() { + webkit_initialized_ = false; +} + +void WebCacheRenderProcessObserver::OnSetCacheCapacities( + size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity) { + if (!webkit_initialized_) { + pending_cache_min_dead_capacity_ = min_dead_capacity; + pending_cache_max_dead_capacity_ = max_dead_capacity; + pending_cache_capacity_ = capacity; + return; + } + + WebCache::setCapacities( + min_dead_capacity, max_dead_capacity, capacity); +} + +void WebCacheRenderProcessObserver::OnClearCache(bool on_navigation) { + if (on_navigation || !webkit_initialized_) + clear_cache_pending_ = true; + else + WebCache::clear(); +} + +} // namespace web_cache diff --git a/chromium/components/web_cache/renderer/web_cache_render_process_observer.h b/chromium/components/web_cache/renderer/web_cache_render_process_observer.h new file mode 100644 index 00000000000..06ffc53319c --- /dev/null +++ b/chromium/components/web_cache/renderer/web_cache_render_process_observer.h @@ -0,0 +1,51 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_WEB_CACHE_RENDERER_WEB_CACHE_RENDER_PROCESS_OBSERVER_H_ +#define COMPONENTS_WEB_CACHE_RENDERER_WEB_CACHE_RENDER_PROCESS_OBSERVER_H_ + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "content/public/renderer/render_process_observer.h" + +namespace web_cache { + +// This class filters the incoming cache related control messages. +class WebCacheRenderProcessObserver : public content::RenderProcessObserver { + public: + WebCacheRenderProcessObserver(); + ~WebCacheRenderProcessObserver() override; + + // Needs to be called by RenderViews in case of navigations to execute + // any 'clear cache' commands that were delayed until the next navigation. + void ExecutePendingClearCache(); + + private: + // RenderProcessObserver implementation. + bool OnControlMessageReceived(const IPC::Message& message) override; + void WebKitInitialized() override; + void OnRenderProcessShutdown() override; + + // Message handlers. + void OnSetCacheCapacities(size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity); + // If |on_navigation| is true, the clearing is delayed until the next + // navigation event. + void OnClearCache(bool on_navigation); + + // If true, the web cache shall be cleared before the next navigation event. + bool clear_cache_pending_; + bool webkit_initialized_; + size_t pending_cache_min_dead_capacity_; + size_t pending_cache_max_dead_capacity_; + size_t pending_cache_capacity_; + + DISALLOW_COPY_AND_ASSIGN(WebCacheRenderProcessObserver); +}; + +} // namespace web_cache + +#endif // COMPONENTS_WEB_CACHE_RENDERER_WEB_CACHE_RENDER_PROCESS_OBSERVER_H_ + diff --git a/chromium/content/browser/android/background_sync_launcher_android.cc b/chromium/content/browser/android/background_sync_launcher_android.cc index 78110c14f64..693817baa8a 100644 --- a/chromium/content/browser/android/background_sync_launcher_android.cc +++ b/chromium/content/browser/android/background_sync_launcher_android.cc @@ -46,7 +46,8 @@ void BackgroundSyncLauncherAndroid::LaunchBrowserWhenNextOnlineImpl( if (was_launching != now_launching) { JNIEnv* env = base::android::AttachCurrentThread(); Java_BackgroundSyncLauncher_setLaunchWhenNextOnline( - env, java_launcher_.obj(), now_launching); + env, java_launcher_.obj(), base::android::GetApplicationContext(), + now_launching); } } diff --git a/chromium/content/browser/service_worker/service_worker_context_core.cc b/chromium/content/browser/service_worker/service_worker_context_core.cc index 17ff54548f6..bb17f8e3be1 100644 --- a/chromium/content/browser/service_worker/service_worker_context_core.cc +++ b/chromium/content/browser/service_worker/service_worker_context_core.cc @@ -494,6 +494,9 @@ void ServiceWorkerContextCore::TransferProviderHostIn( scoped_ptr<ServiceWorkerProviderHost> transferee) { ProviderMap* map = GetProviderMapForProcess(new_process_id); ServiceWorkerProviderHost* temp = map->Lookup(new_provider_id); + if (!temp) + return; + DCHECK(temp->document_url().is_empty()); transferee->CompleteCrossSiteTransfer(new_process_id, temp->frame_id(), diff --git a/chromium/content/browser/site_per_process_browsertest.cc b/chromium/content/browser/site_per_process_browsertest.cc index c5514f65877..9e48aa7b37e 100644 --- a/chromium/content/browser/site_per_process_browsertest.cc +++ b/chromium/content/browser/site_per_process_browsertest.cc @@ -1524,7 +1524,7 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, // Create the cross-site URL to navigate to. GURL cross_site_url = - embedded_test_server()->GetURL("foo.com", "/frame_tree/1-1.html"); + embedded_test_server()->GetURL("foo.com", "/frame_tree/title2.html"); // Load cross-site page into the second iframe without waiting for the // navigation to complete. Once LoadURLWithParams returns, we would expect @@ -1584,7 +1584,7 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, } // Load another cross-site page into the same iframe. - cross_site_url = embedded_test_server()->GetURL("bar.com", "/title2.html"); + cross_site_url = embedded_test_server()->GetURL("bar.com", "/title3.html"); { // Perform the same checks as the first cross-site navigation, since // there have been issues in subsequent cross-site navigations. Also ensure diff --git a/chromium/content/child/service_worker/web_service_worker_impl.cc b/chromium/content/child/service_worker/web_service_worker_impl.cc index 0939aaa7b6d..60d2a292565 100644 --- a/chromium/content/child/service_worker/web_service_worker_impl.cc +++ b/chromium/content/child/service_worker/web_service_worker_impl.cc @@ -44,7 +44,11 @@ WebServiceWorkerImpl::~WebServiceWorkerImpl() { void WebServiceWorkerImpl::OnStateChanged( blink::WebServiceWorkerState new_state) { state_ = new_state; - proxy_->dispatchStateChangeEvent(); + + // TODO(nhiroki): This is a quick fix for http://crbug.com/507110 + DCHECK(proxy_); + if (proxy_) + proxy_->dispatchStateChangeEvent(); } void WebServiceWorkerImpl::setProxy(blink::WebServiceWorkerProxy* proxy) { diff --git a/chromium/content/common/gpu/media/h264_decoder.cc b/chromium/content/common/gpu/media/h264_decoder.cc index 9953ff21a59..6f9c9180482 100644 --- a/chromium/content/common/gpu/media/h264_decoder.cc +++ b/chromium/content/common/gpu/media/h264_decoder.cc @@ -362,9 +362,6 @@ void H264Decoder::ConstructReferencePicListsP( dpb_.GetLongTermRefPicsAppending(&ref_pic_list_p0_); std::sort(ref_pic_list_p0_.begin() + num_short_refs, ref_pic_list_p0_.end(), LongTermPicNumAscCompare()); - - // Cut off if we have more than requested in slice header. - ref_pic_list_p0_.resize(slice_hdr->num_ref_idx_l0_active_minus1 + 1); } struct POCAscCompare { @@ -436,12 +433,6 @@ void H264Decoder::ConstructReferencePicListsB( std::equal(ref_pic_list_b0_.begin(), ref_pic_list_b0_.end(), ref_pic_list_b1_.begin())) std::swap(ref_pic_list_b1_[0], ref_pic_list_b1_[1]); - - // Per 8.2.4.2 it's possible for num_ref_idx_lX_active_minus1 to indicate - // there should be more ref pics on list than we constructed. - // Those superfluous ones should be treated as non-reference. - ref_pic_list_b0_.resize(slice_hdr->num_ref_idx_l0_active_minus1 + 1); - ref_pic_list_b1_.resize(slice_hdr->num_ref_idx_l1_active_minus1 + 1); } // See 8.2.4 @@ -489,25 +480,36 @@ static void ShiftRightAndInsert(H264Picture::Vector* v, bool H264Decoder::ModifyReferencePicList(media::H264SliceHeader* slice_hdr, int list, H264Picture::Vector* ref_pic_listx) { + bool ref_pic_list_modification_flag_lX; int num_ref_idx_lX_active_minus1; media::H264ModificationOfPicNum* list_mod; // This can process either ref_pic_list0 or ref_pic_list1, depending on // the list argument. Set up pointers to proper list to be processed here. if (list == 0) { - if (!slice_hdr->ref_pic_list_modification_flag_l0) - return true; - + ref_pic_list_modification_flag_lX = + slice_hdr->ref_pic_list_modification_flag_l0; + num_ref_idx_lX_active_minus1 = + slice_hdr->num_ref_idx_l0_active_minus1; list_mod = slice_hdr->ref_list_l0_modifications; } else { - if (!slice_hdr->ref_pic_list_modification_flag_l1) - return true; - + ref_pic_list_modification_flag_lX = + slice_hdr->ref_pic_list_modification_flag_l1; + num_ref_idx_lX_active_minus1 = + slice_hdr->num_ref_idx_l1_active_minus1; list_mod = slice_hdr->ref_list_l1_modifications; } - num_ref_idx_lX_active_minus1 = ref_pic_listx->size() - 1; + // Resize the list to the size requested in the slice header. + // Note that per 8.2.4.2 it's possible for num_ref_idx_lX_active_minus1 to + // indicate there should be more ref pics on list than we constructed. + // Those superfluous ones should be treated as non-reference and will be + // initialized to nullptr, which must be handled by clients. DCHECK_GE(num_ref_idx_lX_active_minus1, 0); + ref_pic_listx->resize(num_ref_idx_lX_active_minus1 + 1); + + if (!ref_pic_list_modification_flag_lX) + return true; // Spec 8.2.4.3: // Reorder pictures on the list in a way specified in the stream. diff --git a/chromium/content/content.gyp b/chromium/content/content.gyp index 9ce90ef469b..efcbf740586 100644 --- a/chromium/content/content.gyp +++ b/chromium/content/content.gyp @@ -430,6 +430,7 @@ 'dependencies': [ '../base/base.gyp:base', '../device/battery/battery.gyp:device_battery_java', + '../device/bluetooth/bluetooth.gyp:device_bluetooth_java', '../device/vibration/vibration.gyp:device_vibration_java', '../media/media.gyp:media_java', '../mojo/mojo_base.gyp:mojo_application_bindings', diff --git a/chromium/content/public/android/BUILD.gn b/chromium/content/public/android/BUILD.gn index 663ccf69e82..98727004a89 100644 --- a/chromium/content/public/android/BUILD.gn +++ b/chromium/content/public/android/BUILD.gn @@ -34,6 +34,7 @@ android_library("content_java") { "//base:base_java", "//device/battery/android:battery_monitor_android", "//device/battery:mojo_bindings_java", + "//device/bluetooth:java", "//media/base/android:media_java", "//media/midi:midi_java", "//mojo/android:system_java", diff --git a/chromium/google_apis/gaia/oauth2_token_service_delegate.cc b/chromium/google_apis/gaia/oauth2_token_service_delegate.cc index 2f976145ea1..aed5db8bc23 100644 --- a/chromium/google_apis/gaia/oauth2_token_service_delegate.cc +++ b/chromium/google_apis/gaia/oauth2_token_service_delegate.cc @@ -48,8 +48,7 @@ void OAuth2TokenServiceDelegate::RemoveObserver( // static bool OAuth2TokenServiceDelegate::IsError(const GoogleServiceAuthError& error) { - // TODO(rogerta): should we distinguish between transient and persistent? - return error.state() != GoogleServiceAuthError::NONE; + return error.IsPersistentError(); } void OAuth2TokenServiceDelegate::StartBatchChanges() { diff --git a/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp b/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp index ecd8bde98fb..ba3aeead268 100644 --- a/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp +++ b/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptController.cpp @@ -139,6 +139,10 @@ void ScriptController::clearForClose() void ScriptController::updateSecurityOrigin(SecurityOrigin* origin) { m_windowProxyManager->mainWorldProxy()->updateSecurityOrigin(origin); + Vector<std::pair<ScriptState*, SecurityOrigin*>> isolatedContexts; + m_windowProxyManager->collectIsolatedContexts(isolatedContexts); + for (auto isolatedContext : isolatedContexts) + m_windowProxyManager->windowProxy(isolatedContext.first->world())->updateSecurityOrigin(isolatedContext.second); } v8::MaybeLocal<v8::Value> ScriptController::callFunction(v8::Local<v8::Function> function, v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]) diff --git a/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp b/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp index 72b7724bd88..52bbd0230fd 100644 --- a/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp +++ b/chromium/third_party/WebKit/Source/bindings/core/v8/ScriptStreamer.cpp @@ -169,6 +169,7 @@ public: , m_queueLeadPosition(0) , m_queueTailPosition(0) , m_bookmarkPosition(0) + , m_lengthOfBOM(0) { } @@ -201,7 +202,7 @@ public: { ASSERT(!isMainThread()); m_bookmarkPosition = m_queueLeadPosition; - return m_bookmarkPosition != 0; // Don't mess with BOM. + return true; } // Called by V8 on background thread. @@ -211,7 +212,9 @@ public: { MutexLocker locker(m_mutex); m_queueLeadPosition = m_bookmarkPosition; - m_queueTailPosition = m_bookmarkPosition; + // See comments at m_lengthOfBOM declaration below for why + // we need this here. + m_queueTailPosition = m_bookmarkPosition + m_lengthOfBOM; m_dataQueue.clear(); } @@ -308,6 +311,11 @@ private: } } + if (lengthOfBOM > 0) { + ASSERT(!m_lengthOfBOM); // There should be only one BOM. + m_lengthOfBOM = lengthOfBOM; + } + // Copy the data chunks into a new buffer, since we're going to give the // data to a background thread. if (dataLength > lengthOfBOM) { @@ -346,6 +354,22 @@ private: unsigned m_queueLeadPosition; // Only used by v8 thread. unsigned m_queueTailPosition; // Used by both threads; guarded by m_mutex. unsigned m_bookmarkPosition; // Only used by v8 thread. + + // BOM (Unicode Byte Order Mark) handling: + // This class is responsible for stripping out the BOM, since Chrome + // delivers the input stream potentially with BOM, but V8 doesn't want + // to see the BOM. This is mostly easy to do, except for a funky edge + // condition with bookmarking: + // - m_queueLeadPosition counts the bytes that V8 has received + // (i.e., without BOM) + // - m_queueTailPosition counts the bytes that Chrome has sent + // (i.e., with BOM) + // So when resetting the bookmark, we have to adjust the lead position + // to account for the BOM (which happens implicitly in the regular + // streaming case). + // We store this separately, to avoid having to guard all + // m_queueLeadPosition references with a mutex. + unsigned m_lengthOfBOM; // Used by both threads; guarded by m_mutex. }; size_t ScriptStreamer::kSmallScriptThreshold = 30 * 1024; diff --git a/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp b/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp index d1d463530fc..c83dd50607c 100644 --- a/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp +++ b/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.cpp @@ -34,6 +34,7 @@ #include "bindings/core/v8/V8Binding.h" #include "bindings/core/v8/V8HTMLCollection.h" #include "bindings/core/v8/V8HTMLDocument.h" +#include "bindings/core/v8/V8Location.h" #include "bindings/core/v8/V8ObjectConstructor.h" #include "bindings/core/v8/V8PerContextData.h" #include "bindings/core/v8/V8PerIsolateData.h" @@ -72,7 +73,12 @@ static v8::Local<v8::Object> wrapInShadowTemplate(v8::Local<v8::Object> wrapper, v8::Local<v8::Object> V8DOMWrapper::createWrapper(v8::Isolate* isolate, v8::Local<v8::Object> creationContext, const WrapperTypeInfo* type, ScriptWrappable* scriptWrappable) { - V8WrapperInstantiationScope scope(creationContext, isolate); + ASSERT(!type->equals(&V8Window::wrapperTypeInfo)); + // According to https://html.spec.whatwg.org/multipage/browsers.html#security-location, + // cross-origin script access to a few properties of Location is allowed. + // Location already implements the necessary security checks. + bool withSecurityCheck = !type->equals(&V8Location::wrapperTypeInfo); + V8WrapperInstantiationScope scope(creationContext, isolate, withSecurityCheck); V8PerContextData* perContextData = V8PerContextData::from(scope.context()); v8::Local<v8::Object> wrapper; @@ -124,4 +130,31 @@ bool V8DOMWrapper::hasInternalFieldsSet(v8::Local<v8::Value> value) && untrustedWrapperTypeInfo->ginEmbedder == gin::kEmbedderBlink; } +void V8WrapperInstantiationScope::securityCheck(v8::Isolate* isolate, v8::Local<v8::Context> contextForWrapper) +{ + if (m_context.IsEmpty()) + return; + // If the context is different, we need to make sure that the current + // context has access to the creation context. + Frame* frame = toFrameIfNotDetached(contextForWrapper); + if (!frame) + return; + const DOMWrapperWorld& currentWorld = DOMWrapperWorld::world(m_context); + RELEASE_ASSERT(currentWorld.worldId() == DOMWrapperWorld::world(contextForWrapper).worldId()); + if (currentWorld.isMainWorld()) { + RELEASE_ASSERT(BindingSecurity::shouldAllowAccessToFrame(isolate, frame, DoNotReportSecurityError)); + } +} + +void V8WrapperInstantiationScope::convertException() +{ + v8::Isolate* isolate = m_context->GetIsolate(); + // TODO(jochen): Currently, Location is the only object for which we can reach this code path. Should be generalized. + ExceptionState exceptionState(ExceptionState::ConstructionContext, "Location", isolate->GetCurrentContext()->Global(), isolate); + LocalDOMWindow* callingWindow = callingDOMWindow(isolate); + DOMWindow* targetWindow = toFrameIfNotDetached(m_context)->domWindow(); + exceptionState.throwSecurityError(targetWindow->sanitizedCrossDomainAccessErrorMessage(callingWindow), targetWindow->crossDomainAccessErrorMessage(callingWindow)); + exceptionState.throwIfNeeded(); +} + } // namespace blink diff --git a/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h b/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h index dbe68a1b083..e436cc39064 100644 --- a/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h +++ b/chromium/third_party/WebKit/Source/bindings/core/v8/V8DOMWrapper.h @@ -31,8 +31,10 @@ #ifndef V8DOMWrapper_h #define V8DOMWrapper_h +#include "bindings/core/v8/BindingSecurity.h" #include "bindings/core/v8/DOMDataStore.h" #include "bindings/core/v8/ScriptWrappable.h" +#include "bindings/core/v8/V8Binding.h" #include "wtf/PassRefPtr.h" #include "wtf/RawPtr.h" #include "wtf/text/AtomicString.h" @@ -106,19 +108,27 @@ inline v8::Local<v8::Object> V8DOMWrapper::associateObjectWithWrapper(v8::Isolat class V8WrapperInstantiationScope { public: - V8WrapperInstantiationScope(v8::Local<v8::Object> creationContext, v8::Isolate* isolate) + V8WrapperInstantiationScope(v8::Local<v8::Object> creationContext, v8::Isolate* isolate, bool withSecurityCheck = true) : m_didEnterContext(false) , m_context(isolate->GetCurrentContext()) + , m_tryCatch(isolate) + , m_convertExceptions(false) { // creationContext should not be empty. Because if we have an // empty creationContext, we will end up creating // a new object in the context currently entered. This is wrong. RELEASE_ASSERT(!creationContext.IsEmpty()); v8::Local<v8::Context> contextForWrapper = creationContext->CreationContext(); + // For performance, we enter the context only if the currently running context // is different from the context that we are about to enter. if (contextForWrapper == m_context) return; + if (withSecurityCheck) { + securityCheck(isolate, contextForWrapper); + } else { + m_convertExceptions = true; + } m_context = v8::Local<v8::Context>::New(isolate, contextForWrapper); m_didEnterContext = true; m_context->Enter(); @@ -126,16 +136,31 @@ public: ~V8WrapperInstantiationScope() { - if (!m_didEnterContext) + if (!m_didEnterContext) { + m_tryCatch.ReThrow(); return; + } m_context->Exit(); + // Rethrow any cross-context exceptions as security error. + if (m_tryCatch.HasCaught()) { + if (m_convertExceptions) { + m_tryCatch.Reset(); + convertException(); + } + m_tryCatch.ReThrow(); + } } v8::Local<v8::Context> context() const { return m_context; } private: + void securityCheck(v8::Isolate*, v8::Local<v8::Context> contextForWrapper); + void convertException(); + bool m_didEnterContext; v8::Local<v8::Context> m_context; + v8::TryCatch m_tryCatch; + bool m_convertExceptions; }; } // namespace blink diff --git a/chromium/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp b/chromium/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp index 7b322634614..532e36cbfab 100644 --- a/chromium/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp +++ b/chromium/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp @@ -428,8 +428,22 @@ void WindowProxy::setSecurityToken(SecurityOrigin* origin) return; } - if (m_world->isPrivateScriptIsolatedWorld()) + if (m_world->isPrivateScriptIsolatedWorld()) { token = "private-script://" + token; + } else if (m_world->isIsolatedWorld()) { + SecurityOrigin* frameSecurityOrigin = m_frame->securityContext()->securityOrigin(); + String frameSecurityToken = frameSecurityOrigin->toString(); + // We need to check the return value of domainWasSetInDOM() on the + // frame's SecurityOrigin because, if that's the case, only + // SecurityOrigin::m_domain would have been modified. + // m_domain is not used by SecurityOrigin::toString(), so we would end + // up generating the same token that was already set. + if (frameSecurityOrigin->domainWasSetInDOM() || frameSecurityToken.isEmpty() || frameSecurityToken == "null") { + context->UseDefaultSecurityToken(); + return; + } + token = frameSecurityToken + token; + } CString utf8Token = token.utf8(); // NOTE: V8 does identity comparison in fast path, must use a symbol @@ -522,7 +536,6 @@ void WindowProxy::namedItemRemoved(HTMLDocument* document, const AtomicString& n void WindowProxy::updateSecurityOrigin(SecurityOrigin* origin) { - ASSERT(m_world->isMainWorld()); if (!isContextInitialized()) return; setSecurityToken(origin); diff --git a/chromium/third_party/WebKit/Source/bindings/templates/interface.cpp b/chromium/third_party/WebKit/Source/bindings/templates/interface.cpp index 439672c30b1..c67a5c4d7a5 100644 --- a/chromium/third_party/WebKit/Source/bindings/templates/interface.cpp +++ b/chromium/third_party/WebKit/Source/bindings/templates/interface.cpp @@ -560,8 +560,8 @@ void {{v8_class}}::visitDOMWrapper(v8::Isolate* isolate, ScriptWrappable* script { {{cpp_class}}* impl = scriptWrappable->toImpl<{{cpp_class}}>(); {% if set_wrapper_reference_to %} - v8::Local<v8::Object> creationContext = v8::Local<v8::Object>::New(isolate, wrapper); - V8WrapperInstantiationScope scope(creationContext, isolate); + v8::Local<v8::Object> context = v8::Local<v8::Object>::New(isolate, wrapper); + v8::Context::Scope scope(context->CreationContext()); {{set_wrapper_reference_to.cpp_type}} {{set_wrapper_reference_to.name}} = impl->{{set_wrapper_reference_to.name}}(); if ({{set_wrapper_reference_to.name}}) { if (DOMDataStore::containsWrapper({{set_wrapper_reference_to.name}}, isolate)) diff --git a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp index bc59b437401..1764fbef7b2 100644 --- a/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp +++ b/chromium/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp @@ -100,6 +100,11 @@ static PassRefPtrWillBeRawPtr<StringKeyframeEffectModel> createKeyframeEffectMod timingFunction = CSSTimingData::initialTimingFunction(); } keyframe->setEasing(timingFunction.release()); + } else if (property == CSSPropertyFilter) { + // TODO(alancutter): We will not support animating filter until -webkit-filter is an alias for it. + // This is to prevent animations on both -webkit-filter and filter from being run on the main thread when + // they would otherwise run on the compositor. + continue; } else if (CSSAnimations::isAnimatableProperty(property)) { keyframe->setPropertyValue(property, properties.propertyAt(j).value()); } diff --git a/chromium/third_party/WebKit/Source/core/dom/Document.cpp b/chromium/third_party/WebKit/Source/core/dom/Document.cpp index d1a46222e65..b21280d217a 100644 --- a/chromium/third_party/WebKit/Source/core/dom/Document.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/Document.cpp @@ -4073,6 +4073,9 @@ String Document::lastModified() const const KURL& Document::firstPartyForCookies() const { + if (SchemeRegistry::shouldTreatURLSchemeAsFirstPartyWhenTopLevel(topDocument().url().protocol())) + return topDocument().url(); + // We're intentionally using the URL of each document rather than the document's SecurityOrigin. // Sandboxing a document into a unique origin shouldn't effect first-/third-party status for // cookies and site data. @@ -5377,10 +5380,10 @@ void Document::updateHoverActiveState(const HitTestRequest& request, Element* in if (oldActiveElement && !request.active()) { // The oldActiveElement layoutObject is null, dropped on :active by setting display: none, // for instance. We still need to clear the ActiveChain as the mouse is released. - for (Node* node = oldActiveElement; node; node = ComposedTreeTraversal::parent(*node)) { + for (RefPtrWillBeRawPtr<Node> node = oldActiveElement; node; node = ComposedTreeTraversal::parent(*node)) { ASSERT(!node->isTextNode()); node->setActive(false); - m_userActionElements.setInActiveChain(node, false); + m_userActionElements.setInActiveChain(node.get(), false); } setActiveHoverElement(nullptr); } else { diff --git a/chromium/third_party/WebKit/Source/core/dom/Text.cpp b/chromium/third_party/WebKit/Source/core/dom/Text.cpp index 71ce46fafe3..4f46e8abb87 100644 --- a/chromium/third_party/WebKit/Source/core/dom/Text.cpp +++ b/chromium/third_party/WebKit/Source/core/dom/Text.cpp @@ -237,31 +237,12 @@ PassRefPtrWillBeRawPtr<Node> Text::cloneNode(bool /*deep*/) return cloneWithData(data()); } -static inline bool hasGeneratedAnonymousTableCells(const LayoutObject& parent) -{ - // We're checking whether the table part has generated anonymous table - // part wrappers to hold its contents, so inspecting its first child will suffice. - LayoutObject* child = parent.slowFirstChild(); - if (!child || !child->isAnonymous()) - return false; - if (child->isTableCell()) - return true; - if (child->isTableSection() || child->isTableRow()) - return hasGeneratedAnonymousTableCells(*child); - return false; -} - static inline bool canHaveWhitespaceChildren(const LayoutObject& parent) { // <button> should allow whitespace even though LayoutFlexibleBox doesn't. if (parent.isLayoutButton()) return true; - // Allow whitespace when the text is inside a table, section or row element that - // has generated anonymous table cells to hold its contents. - if (hasGeneratedAnonymousTableCells(parent)) - return true; - if (parent.isTable() || parent.isTableRow() || parent.isTableSection() || parent.isLayoutTableCol() || parent.isFrameSet() || parent.isFlexibleBox() || parent.isLayoutGrid() diff --git a/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp b/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp index 5954fd198e8..421b23333f8 100644 --- a/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp +++ b/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.cpp @@ -122,11 +122,6 @@ void ResourceLoader::start() ASSERT(!m_request.isNull()); ASSERT(m_deferredRequest.isNull()); - if (responseNeedsAccessControlCheck() && m_fetcher->isControlledByServiceWorker()) { - m_fallbackRequestForServiceWorker = adoptPtr(new ResourceRequest(m_request)); - m_fallbackRequestForServiceWorker->setSkipServiceWorker(true); - } - m_fetcher->willStartLoadingResource(m_resource, m_request); if (m_options.synchronousPolicy == RequestSynchronously) { @@ -331,13 +326,13 @@ void ResourceLoader::didReceiveResponse(WebURLLoader*, const WebURLResponse& res if (responseNeedsAccessControlCheck()) { if (response.wasFetchedViaServiceWorker()) { if (response.wasFallbackRequiredByServiceWorker()) { - ASSERT(m_fallbackRequestForServiceWorker); m_loader->cancel(); m_loader.clear(); m_connectionState = ConnectionStateStarted; - m_request = *m_fallbackRequestForServiceWorker; m_loader = adoptPtr(Platform::current()->createURLLoader()); ASSERT(m_loader); + ASSERT(!m_request.skipServiceWorker()); + m_request.setSkipServiceWorker(true); WrappedResourceRequest wrappedRequest(m_request); m_loader->loadAsynchronously(wrappedRequest, this); return; diff --git a/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.h b/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.h index 68ca49a0414..1cb4d81569a 100644 --- a/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.h +++ b/chromium/third_party/WebKit/Source/core/fetch/ResourceLoader.h @@ -111,7 +111,6 @@ private: bool m_defersLoading; bool m_loadingMultipartContent; - OwnPtr<ResourceRequest> m_fallbackRequestForServiceWorker; ResourceRequest m_deferredRequest; ResourceLoaderOptions m_options; diff --git a/chromium/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/chromium/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp index 45477c7040b..5ba8e8d21ce 100644 --- a/chromium/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp +++ b/chromium/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp @@ -661,8 +661,17 @@ ApplicationCache* LocalDOMWindow::applicationCache() const Navigator* LocalDOMWindow::navigator() const { + if (!isCurrentlyDisplayedInFrame() && (!m_navigator || m_navigator->frame())) { + // We return a navigator with null frame instead of returning null + // pointer as other functions do, in order to allow users to access + // functions such as navigator.product. + m_navigator = Navigator::create(nullptr); + } if (!m_navigator) m_navigator = Navigator::create(frame()); + // As described above, when not dispayed in the frame, the returning + // navigator should not be associated with the frame. + ASSERT(isCurrentlyDisplayedInFrame() || !m_navigator->frame()); return m_navigator.get(); } diff --git a/chromium/third_party/WebKit/Source/core/html/forms/BaseMultipleFieldsDateAndTimeInputType.cpp b/chromium/third_party/WebKit/Source/core/html/forms/BaseMultipleFieldsDateAndTimeInputType.cpp index 0370672ff31..60883065ed3 100644 --- a/chromium/third_party/WebKit/Source/core/html/forms/BaseMultipleFieldsDateAndTimeInputType.cpp +++ b/chromium/third_party/WebKit/Source/core/html/forms/BaseMultipleFieldsDateAndTimeInputType.cpp @@ -432,6 +432,8 @@ void BaseMultipleFieldsDateAndTimeInputType::requiredAttributeChanged() void BaseMultipleFieldsDateAndTimeInputType::handleKeydownEvent(KeyboardEvent* event) { + if (!element().focused()) + return; if (m_pickerIndicatorIsVisible && ((event->keyIdentifier() == "Down" && event->getModifierState("Alt")) || (LayoutTheme::theme().shouldOpenPickerWithF4Key() && event->keyIdentifier() == "F4"))) { if (PickerIndicatorElement* element = pickerIndicatorElement()) diff --git a/chromium/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp b/chromium/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp index 4ab73282d46..05e43ad632e 100644 --- a/chromium/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp +++ b/chromium/third_party/WebKit/Source/core/layout/LayoutBlockFlow.cpp @@ -459,7 +459,7 @@ void LayoutBlockFlow::determineLogicalLeftPositionForChild(LayoutBox& child) LayoutUnit newPosition = startPosition + childMarginStart; LayoutUnit positionToAvoidFloats; - if (child.avoidsFloats() && containsFloats() && !flowThreadContainingBlock()) + if (child.avoidsFloats() && containsFloats()) positionToAvoidFloats = startOffsetForLine(logicalTopForChild(child), false, logicalHeightForChild(child)); // If the child has an offset from the content edge to avoid floats then use that, otherwise let any negative @@ -808,7 +808,9 @@ LayoutUnit LayoutBlockFlow::adjustForUnsplittableChild(LayoutBox& child, LayoutU if (!pageLogicalHeight) return logicalOffset; LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary); - if (remainingLogicalHeight < childLogicalHeight) + // Break if there's not enough space left for us, but only as long as we're not already at the + // top of a page. No point in leaving a page completely blank. + if (remainingLogicalHeight < childLogicalHeight && remainingLogicalHeight < pageLogicalHeight) return logicalOffset + remainingLogicalHeight; return logicalOffset; } diff --git a/chromium/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/chromium/third_party/WebKit/Source/core/layout/LayoutObject.cpp index 74c449fdef3..7c5a4f66404 100644 --- a/chromium/third_party/WebKit/Source/core/layout/LayoutObject.cpp +++ b/chromium/third_party/WebKit/Source/core/layout/LayoutObject.cpp @@ -864,6 +864,16 @@ LayoutBlock* LayoutObject::containingBlockForAbsolutePosition() const if (o->canContainFixedPositionObjects()) break; + // For relpositioned inlines, we return the nearest non-anonymous enclosing block. We don't try + // to return the inline itself. This allows us to avoid having a positioned objects + // list in all LayoutInlines and lets us return a strongly-typed LayoutBlock* result + // from this method. The container() method can actually be used to obtain the + // inline directly. + if (o->style()->hasInFlowPosition() && o->isInline() && !o->isReplaced()) { + o = o->containingBlock(); + break; + } + o = o->parent(); } @@ -3253,7 +3263,7 @@ void LayoutObject::invalidateDisplayItemClientForNonCompositingDescendantsOf(con explicit Functor(const LayoutBoxModelObject& paintInvalidationContainer) : m_paintInvalidationContainer(paintInvalidationContainer) { } void operator()(LayoutObject& object) const override { - m_paintInvalidationContainer.invalidateDisplayItemClientOnBacking(object); + object.invalidateDisplayItemClients(m_paintInvalidationContainer); } private: const LayoutBoxModelObject& m_paintInvalidationContainer; diff --git a/chromium/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp b/chromium/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp index a01f145b6f0..91ada25c62f 100644 --- a/chromium/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp +++ b/chromium/third_party/WebKit/Source/core/xml/parser/XMLDocumentParser.cpp @@ -723,6 +723,7 @@ PassRefPtr<XMLParserContext> XMLParserContext::createStringParser(xmlSAXHandlerP { initializeLibXMLIfNecessary(); xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(handlers, 0, 0, 0, 0); + xmlCtxtUseOptions(parser, XML_PARSE_HUGE); parser->_private = userData; parser->replaceEntities = true; return adoptRef(new XMLParserContext(parser)); @@ -745,7 +746,8 @@ PassRefPtr<XMLParserContext> XMLParserContext::createMemoryParser(xmlSAXHandlerP // Set parser options. // XML_PARSE_NODICT: default dictionary option. // XML_PARSE_NOENT: force entities substitutions. - xmlCtxtUseOptions(parser, XML_PARSE_NODICT | XML_PARSE_NOENT); + // XML_PARSE_HUGE: don't impose arbitrary limits on document size. + xmlCtxtUseOptions(parser, XML_PARSE_NODICT | XML_PARSE_NOENT | XML_PARSE_HUGE); // Internal initialization parser->sax2 = 1; diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js b/chromium/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js index 7b5b1de58cf..ec7b8a21f50 100644 --- a/chromium/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js +++ b/chromium/third_party/WebKit/Source/devtools/front_end/elements/Spectrum.js @@ -91,6 +91,7 @@ WebInspector.Spectrum = function() this._hexValue = this._hexContainer.createChild("input", "spectrum-text-value"); this._hexValue.maxLength = 7; this._hexValue.addEventListener("keydown", this._inputChanged.bind(this), false); + this._hexValue.addEventListener("input", this._inputChanged.bind(this), false); this._hexValue.addEventListener("mousewheel", this._inputChanged.bind(this), false); var label = this._hexContainer.createChild("div", "spectrum-text-label"); diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js index c11bd01e5e2..36d5a594b76 100644 --- a/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js +++ b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/TimelineUIUtils.js @@ -1389,7 +1389,7 @@ WebInspector.TimelineUIUtils.generateDetailsContentForFrame = function(frameMode var filmStripPreview = createElementWithClass("img", "timeline-filmstrip-preview"); filmStripFrame.imageDataPromise().then(onGotImageData.bind(null, filmStripPreview)); contentHelper.appendElementRow(WebInspector.UIString("Screenshot"), filmStripPreview); - filmStripPreview.addEventListener("click", filmStripClicked.bind(null, filmStripFrame), false); + filmStripPreview.addEventListener("click", frameClicked.bind(null, filmStripFrame), false); } contentHelper.appendTextRow(WebInspector.UIString("Duration"), durationText); contentHelper.appendTextRow(WebInspector.UIString("FPS"), Math.floor(1000 / durationInMillis)); @@ -1413,9 +1413,9 @@ WebInspector.TimelineUIUtils.generateDetailsContentForFrame = function(frameMode /** * @param {!WebInspector.FilmStripModel.Frame} filmStripFrame */ - function filmStripClicked(filmStripFrame) + function frameClicked(filmStripFrame) { - WebInspector.Dialog.show(null, new WebInspector.FilmStripView.DialogDelegate(filmStripFrame, 0)); + new WebInspector.FilmStripView.DialogDelegate(filmStripFrame, 0); } return contentHelper.element; diff --git a/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp b/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp index 822ae67a5fe..59f663c0779 100644 --- a/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp +++ b/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.cpp @@ -176,6 +176,13 @@ static URLSchemesSet& serviceWorkerSchemes() return serviceWorkerSchemes; } +static URLSchemesSet& firstPartyWhenTopLevelSchemes() +{ + assertLockHeld(); + DEFINE_STATIC_LOCAL_NOASSERT(URLSchemesSet, firstPartyWhenTopLevelSchemes, ()); + return firstPartyWhenTopLevelSchemes; +} + static URLSchemesMap<SchemeRegistry::PolicyAreas>& ContentSecurityPolicyBypassingSchemes() { assertLockHeld(); @@ -353,6 +360,20 @@ bool SchemeRegistry::shouldTreatURLSchemeAsAllowingServiceWorkers(const String& return serviceWorkerSchemes().contains(scheme); } +void SchemeRegistry::registerURLSchemeAsFirstPartyWhenTopLevel(const String& scheme) +{ + MutexLocker locker(mutex()); + firstPartyWhenTopLevelSchemes().add(scheme); +} + +bool SchemeRegistry::shouldTreatURLSchemeAsFirstPartyWhenTopLevel(const String& scheme) +{ + if (scheme.isEmpty()) + return false; + MutexLocker locker(mutex()); + return firstPartyWhenTopLevelSchemes().contains(scheme); +} + void SchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy(const String& scheme, PolicyAreas policyAreas) { MutexLocker locker(mutex()); diff --git a/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h b/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h index e85d2bbec88..a9823d257ec 100644 --- a/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h +++ b/chromium/third_party/WebKit/Source/platform/weborigin/SchemeRegistry.h @@ -91,6 +91,10 @@ public: static void registerURLSchemeAsAllowingServiceWorkers(const String& scheme); static bool shouldTreatURLSchemeAsAllowingServiceWorkers(const String& scheme); + // Schemes which override the first-/third-party checks on a Document. + static void registerURLSchemeAsFirstPartyWhenTopLevel(const String& scheme); + static bool shouldTreatURLSchemeAsFirstPartyWhenTopLevel(const String& scheme); + // Allow resources from some schemes to load on a page, regardless of its // Content Security Policy. // This enum should be kept in sync with public/web/WebSecurityPolicy.h. diff --git a/chromium/third_party/WebKit/Source/web/WebSecurityPolicy.cpp b/chromium/third_party/WebKit/Source/web/WebSecurityPolicy.cpp index 41e77211828..f309ea6b96a 100644 --- a/chromium/third_party/WebKit/Source/web/WebSecurityPolicy.cpp +++ b/chromium/third_party/WebKit/Source/web/WebSecurityPolicy.cpp @@ -91,6 +91,11 @@ void WebSecurityPolicy::registerURLSchemeAsBypassingContentSecurityPolicy(const SchemeRegistry::registerURLSchemeAsBypassingContentSecurityPolicy(scheme, static_cast<SchemeRegistry::PolicyAreas>(policyAreas)); } +void WebSecurityPolicy::registerURLSchemeAsFirstPartyWhenTopLevel(const WebString& scheme) +{ + SchemeRegistry::registerURLSchemeAsFirstPartyWhenTopLevel(scheme); +} + void WebSecurityPolicy::registerURLSchemeAsEmptyDocument(const WebString& scheme) { SchemeRegistry::registerURLSchemeAsEmptyDocument(scheme); diff --git a/chromium/third_party/WebKit/public/web/WebSecurityPolicy.h b/chromium/third_party/WebKit/public/web/WebSecurityPolicy.h index 33b4c9984b8..e32093fd580 100644 --- a/chromium/third_party/WebKit/public/web/WebSecurityPolicy.h +++ b/chromium/third_party/WebKit/public/web/WebSecurityPolicy.h @@ -79,6 +79,9 @@ public: // Registers a URL scheme whose resources can be loaded regardless of a page's Content Security Policy. BLINK_EXPORT static void registerURLSchemeAsBypassingContentSecurityPolicy(const WebString&); + // Registers a URL scheme which will always be considered the first-party when loaded in a top-level context. + BLINK_EXPORT static void registerURLSchemeAsFirstPartyWhenTopLevel(const WebString&); + // Registers a URL scheme for which some kinds of resources bypass Content Security Policy. // This enum should be kept in sync with Source/platform/weborigin/SchemeRegistry.h. // Enforced in AssertMatchingEnums.cpp. diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc b/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc index e3c39252569..6c0e1ca0964 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc @@ -5,6 +5,7 @@ #include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h" #include "base/time/time.h" +#include "ui/ozone/platform/drm/gpu/crtc_controller.h" #include "ui/ozone/platform/drm/gpu/drm_window.h" #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h" @@ -21,9 +22,11 @@ void DrmVSyncProvider::GetVSyncParameters(const UpdateVSyncCallback& callback) { if (!controller) return; + // If we're in mirror mode the 2 CRTCs should have similar modes with the same + // refresh rates. + CrtcController* crtc = controller->crtc_controllers()[0]; // The value is invalid, so we can't update the parameters. - if (controller->GetTimeOfLastFlip() == 0 || - controller->get_mode().vrefresh == 0) + if (controller->GetTimeOfLastFlip() == 0 || crtc->mode().vrefresh == 0) return; // Stores the time of the last refresh. @@ -31,7 +34,7 @@ void DrmVSyncProvider::GetVSyncParameters(const UpdateVSyncCallback& callback) { base::TimeTicks::FromInternalValue(controller->GetTimeOfLastFlip()); // Stores the refresh rate. base::TimeDelta interval = - base::TimeDelta::FromSeconds(1) / controller->get_mode().vrefresh; + base::TimeDelta::FromSeconds(1) / crtc->mode().vrefresh; callback.Run(timebase, interval); } diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc index 5ee481931ff..efadce735e7 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc @@ -27,7 +27,6 @@ HardwareDisplayController::HardwareDisplayController( scoped_ptr<CrtcController> controller, const gfx::Point& origin) : origin_(origin), - mode_(controller->mode()), is_disabled_(controller->is_disabled()) { AddCrtc(controller.Pass()); } @@ -46,7 +45,20 @@ bool HardwareDisplayController::Modeset(const OverlayPlane& primary, status &= crtc_controllers_[i]->Modeset(primary, mode); is_disabled_ = false; - mode_ = mode; + + return status; +} + +bool HardwareDisplayController::Enable(const OverlayPlane& primary) { + TRACE_EVENT0("drm", "HDC::Enable"); + DCHECK(primary.buffer.get()); + bool status = true; + for (size_t i = 0; i < crtc_controllers_.size(); ++i) { + status &= + crtc_controllers_[i]->Modeset(primary, crtc_controllers_[i]->mode()); + } + + is_disabled_ = false; return status; } @@ -192,7 +204,9 @@ bool HardwareDisplayController::IsDisabled() const { } gfx::Size HardwareDisplayController::GetModeSize() const { - return gfx::Size(mode_.hdisplay, mode_.vdisplay); + // If there are multiple CRTCs they should all have the same size. + return gfx::Size(crtc_controllers_[0]->mode().hdisplay, + crtc_controllers_[0]->mode().vdisplay); } uint64_t HardwareDisplayController::GetTimeOfLastFlip() const { diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h index 3772c35302f..830a357ce59 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h @@ -98,6 +98,9 @@ class OZONE_EXPORT HardwareDisplayController { // framebuffer for |primary| with |mode|. bool Modeset(const OverlayPlane& primary, drmModeModeInfo mode); + // Performs a CRTC configuration re-using the modes from the CRTCs. + bool Enable(const OverlayPlane& primary); + // Disables the CRTC. void Disable(); @@ -147,8 +150,6 @@ class OZONE_EXPORT HardwareDisplayController { gfx::Point origin() const { return origin_; } void set_origin(const gfx::Point& origin) { origin_ = origin; } - const drmModeModeInfo& get_mode() const { return mode_; }; - uint64_t GetTimeOfLastFlip() const; const std::vector<CrtcController*>& crtc_controllers() const { @@ -168,9 +169,6 @@ class OZONE_EXPORT HardwareDisplayController { // Location of the controller on the screen. gfx::Point origin_; - // The mode used by the last modesetting operation. - drmModeModeInfo mode_; - bool is_disabled_; DISALLOW_COPY_AND_ASSIGN(HardwareDisplayController); diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc index 4b4d3f13d75..97bdfe33dbc 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc @@ -65,6 +65,18 @@ void FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, modeset_buffer.canvas()->drawImage(image.get(), 0, 0, &paint); } +CrtcController* GetCrtcController(HardwareDisplayController* controller, + const scoped_refptr<DrmDevice>& drm, + uint32_t crtc) { + for (CrtcController* crtc_controller : controller->crtc_controllers()) { + if (crtc_controller->crtc() == crtc) + return crtc_controller; + } + + NOTREACHED(); + return nullptr; +} + } // namespace ScreenManager::ScreenManager(ScanoutBufferGenerator* buffer_generator) @@ -133,9 +145,10 @@ bool ScreenManager::ActualConfigureDisplayController( << ") doesn't exist."; HardwareDisplayController* controller = *it; + CrtcController* crtc_controller = GetCrtcController(controller, drm, crtc); // If nothing changed just enable the controller. Note, we perform an exact // comparison on the mode since the refresh rate may have changed. - if (SameMode(mode, controller->get_mode()) && + if (SameMode(mode, crtc_controller->mode()) && origin == controller->origin()) { if (controller->IsDisabled()) { HardwareDisplayControllers::iterator mirror = @@ -143,12 +156,11 @@ bool ScreenManager::ActualConfigureDisplayController( // If there is an active controller at the same location then start mirror // mode. if (mirror != controllers_.end()) - return HandleMirrorMode(it, mirror, drm, crtc, connector); + return HandleMirrorMode(it, mirror, drm, crtc, connector, mode); } // Just re-enable the controller to re-use the current state. - return EnableController(controller, controller->origin(), - controller->get_mode()); + return EnableController(controller); } // Either the mode or the location of the display changed, so exit mirror @@ -166,9 +178,9 @@ bool ScreenManager::ActualConfigureDisplayController( FindActiveDisplayControllerByLocation(modeset_bounds); // Handle mirror mode. if (mirror != controllers_.end() && it != mirror) - return HandleMirrorMode(it, mirror, drm, crtc, connector); + return HandleMirrorMode(it, mirror, drm, crtc, connector, mode); - return EnableController(controller, origin, mode); + return ModesetController(controller, origin, mode); } bool ScreenManager::DisableDisplayController( @@ -256,9 +268,20 @@ bool ScreenManager::HandleMirrorMode( HardwareDisplayControllers::iterator mirror, const scoped_refptr<DrmDevice>& drm, uint32_t crtc, - uint32_t connector) { - (*mirror)->AddCrtc((*original)->RemoveCrtc(drm, crtc)); - if (EnableController(*mirror, (*mirror)->origin(), (*mirror)->get_mode())) { + uint32_t connector, + const drmModeModeInfo& mode) { + gfx::Point last_origin = (*original)->origin(); + // There should only be one CRTC in this controller. + drmModeModeInfo last_mode = (*original)->crtc_controllers()[0]->mode(); + + // Modeset the CRTC with its mode in the original controller so that only this + // CRTC is affected by the mode. Otherwise it could apply a mode with the same + // resolution and refresh rate but with different timings to the other CRTC. + // TODO(dnicoara): This is hacky, instead the DrmDisplay and CrtcController + // should be merged and picking the mode should be done properly within + // HardwareDisplayController. + if (ModesetController(*original, (*mirror)->origin(), mode)) { + (*mirror)->AddCrtc((*original)->RemoveCrtc(drm, crtc)); controllers_.erase(original); return true; } @@ -268,8 +291,7 @@ bool ScreenManager::HandleMirrorMode( // When things go wrong revert back to the previous configuration since // it is expected that the configuration would not have changed if // things fail. - (*original)->AddCrtc((*mirror)->RemoveCrtc(drm, crtc)); - EnableController(*original, (*original)->origin(), (*original)->get_mode()); + ModesetController(*original, last_origin, last_mode); return false; } @@ -299,36 +321,50 @@ void ScreenManager::UpdateControllerToWindowMapping() { } } -bool ScreenManager::EnableController(HardwareDisplayController* controller, - const gfx::Point& origin, - const drmModeModeInfo& mode) { - DCHECK(!controller->crtc_controllers().empty()); - gfx::Rect rect(origin, gfx::Size(mode.hdisplay, mode.vdisplay)); - controller->set_origin(origin); - - DrmWindow* window = FindWindowAt(rect); +OverlayPlane ScreenManager::GetModesetBuffer( + HardwareDisplayController* controller, + const gfx::Rect& bounds) { + DrmWindow* window = FindWindowAt(bounds); if (window) { const OverlayPlane* primary = window->GetLastModesetBuffer(); - if (primary) { - if (!controller->Modeset(*primary, mode)) { - LOG(ERROR) << "Failed to modeset controller"; - return false; - } - - return true; - } + if (primary && primary->buffer->GetSize() == bounds.size()) + return *primary; } scoped_refptr<DrmDevice> drm = controller->GetAllocationDrmDevice(); scoped_refptr<ScanoutBuffer> buffer = - buffer_generator_->Create(drm, rect.size()); + buffer_generator_->Create(drm, bounds.size()); if (!buffer) { LOG(ERROR) << "Failed to create scanout buffer"; - return false; + return OverlayPlane(nullptr, 0, gfx::OVERLAY_TRANSFORM_INVALID, gfx::Rect(), + gfx::RectF()); } FillModesetBuffer(drm, controller, buffer.get()); - if (!controller->Modeset(OverlayPlane(buffer), mode)) { + return OverlayPlane(buffer); +} + +bool ScreenManager::EnableController(HardwareDisplayController* controller) { + DCHECK(!controller->crtc_controllers().empty()); + gfx::Rect rect(controller->origin(), controller->GetModeSize()); + OverlayPlane plane = GetModesetBuffer(controller, rect); + if (!plane.buffer || !controller->Enable(plane)) { + LOG(ERROR) << "Failed to enable controller"; + return false; + } + + return true; +} + +bool ScreenManager::ModesetController(HardwareDisplayController* controller, + const gfx::Point& origin, + const drmModeModeInfo& mode) { + DCHECK(!controller->crtc_controllers().empty()); + gfx::Rect rect(origin, gfx::Size(mode.hdisplay, mode.vdisplay)); + controller->set_origin(origin); + + OverlayPlane plane = GetModesetBuffer(controller, rect); + if (!plane.buffer || !controller->Modeset(plane, mode)) { LOG(ERROR) << "Failed to modeset controller"; return false; } diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h index f9c5bc0cfde..6c067ba2b31 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.h @@ -108,13 +108,19 @@ class OZONE_EXPORT ScreenManager { HardwareDisplayControllers::iterator mirror, const scoped_refptr<DrmDevice>& drm, uint32_t crtc, - uint32_t connector); + uint32_t connector, + const drmModeModeInfo& mode); + + OverlayPlane GetModesetBuffer(HardwareDisplayController* controller, + const gfx::Rect& bounds); + + bool EnableController(HardwareDisplayController* controller); // Modeset the |controller| using |origin| and |mode|. If there is a window at // the controller location, then we'll re-use the current buffer. - bool EnableController(HardwareDisplayController* controller, - const gfx::Point& origin, - const drmModeModeInfo& mode); + bool ModesetController(HardwareDisplayController* controller, + const gfx::Point& origin, + const drmModeModeInfo& mode); DrmWindow* FindWindowAt(const gfx::Rect& bounds) const; diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc index f8b59a66a97..662aba94bbf 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc @@ -148,8 +148,9 @@ TEST_F(ScreenManagerTest, CheckChangingMode) { gfx::Rect new_bounds(0, 0, new_mode.hdisplay, new_mode.vdisplay); EXPECT_TRUE(screen_manager_->GetDisplayController(new_bounds)); EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds())); - drmModeModeInfo mode = - screen_manager_->GetDisplayController(new_bounds)->get_mode(); + drmModeModeInfo mode = screen_manager_->GetDisplayController(new_bounds) + ->crtc_controllers()[0] + ->mode(); EXPECT_EQ(new_mode.vdisplay, mode.vdisplay); EXPECT_EQ(new_mode.hdisplay, mode.hdisplay); } diff --git a/chromium/ui/resources/default_100_percent/common/ntp_google_logo.png b/chromium/ui/resources/default_100_percent/common/ntp_google_logo.png Binary files differindex a6fa11fd227..fee9f7695aa 100644 --- a/chromium/ui/resources/default_100_percent/common/ntp_google_logo.png +++ b/chromium/ui/resources/default_100_percent/common/ntp_google_logo.png diff --git a/chromium/ui/resources/default_200_percent/common/ntp_google_logo.png b/chromium/ui/resources/default_200_percent/common/ntp_google_logo.png Binary files differindex f2f5b8c58d5..0b55f7b38a7 100644 --- a/chromium/ui/resources/default_200_percent/common/ntp_google_logo.png +++ b/chromium/ui/resources/default_200_percent/common/ntp_google_logo.png diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc index 93b3c5b81fb..9278c2968c9 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc @@ -906,6 +906,16 @@ void DesktopWindowTreeHostWin::HandleWindowSizeChanging() { compositor()->DisableSwapUntilResize(); } +void DesktopWindowTreeHostWin::HandleWindowSizeChanged() { + // A resize may not have occurred if the window size happened not to have + // changed (can occur on Windows 10 when snapping a window to the side of + // the screen). In that case do a resize to the current size to reenable + // swaps. + if (compositor()) + compositor()->SetScaleAndSize(compositor()->device_scale_factor(), + compositor()->size()); +} + //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostWin, private: diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h index c213ffe453e..047498f81b6 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_win.h @@ -191,6 +191,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostWin void PostHandleMSG(UINT message, WPARAM w_param, LPARAM l_param) override; bool HandleScrollEvent(const ui::ScrollEvent& event) override; void HandleWindowSizeChanging() override; + void HandleWindowSizeChanged() override; Widget* GetWidget(); const Widget* GetWidget() const; diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index d2233494fed..cf71db8234e 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -329,9 +329,9 @@ HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate) touch_down_contexts_(0), last_mouse_hwheel_time_(0), dwm_transition_desired_(false), + sent_window_size_changing_(false), autohide_factory_(this), - weak_factory_(this) { -} + weak_factory_(this) {} HWNDMessageHandler::~HWNDMessageHandler() { delegate_ = NULL; @@ -2470,6 +2470,7 @@ void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) { if ((old_size != new_size && !(window_pos->flags & SWP_NOSIZE)) || window_pos->flags & SWP_FRAMECHANGED) { delegate_->HandleWindowSizeChanging(); + sent_window_size_changing_ = true; } if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) { @@ -2509,6 +2510,10 @@ void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) { if (direct_manipulation_helper_) direct_manipulation_helper_->Deactivate(hwnd()); } + if (sent_window_size_changing_) { + sent_window_size_changing_ = false; + delegate_->HandleWindowSizeChanged(); + } SetMsgHandled(FALSE); } diff --git a/chromium/ui/views/win/hwnd_message_handler.h b/chromium/ui/views/win/hwnd_message_handler.h index e7fc8af7b51..6b8f719699b 100644 --- a/chromium/ui/views/win/hwnd_message_handler.h +++ b/chromium/ui/views/win/hwnd_message_handler.h @@ -584,6 +584,10 @@ class VIEWS_EXPORT HWNDMessageHandler : // glass. Defaults to false. bool dwm_transition_desired_; + // True if HandleWindowSizeChanging has been called in the delegate, but not + // HandleWindowSizeChanged. + bool sent_window_size_changing_; + // Manages observation of Windows Session Change messages. scoped_ptr<WindowsSessionChangeObserver> windows_session_change_observer_; diff --git a/chromium/ui/views/win/hwnd_message_handler_delegate.h b/chromium/ui/views/win/hwnd_message_handler_delegate.h index e773b817701..8f8957ae245 100644 --- a/chromium/ui/views/win/hwnd_message_handler_delegate.h +++ b/chromium/ui/views/win/hwnd_message_handler_delegate.h @@ -224,6 +224,9 @@ class VIEWS_EXPORT HWNDMessageHandlerDelegate { // Called when the window size is about to change. virtual void HandleWindowSizeChanging() = 0; + // Called when the window size has finished changing. + virtual void HandleWindowSizeChanged() = 0; + protected: virtual ~HWNDMessageHandlerDelegate() {} }; diff --git a/chromium/url/url_canon_path.cc b/chromium/url/url_canon_path.cc index ceff6896319..d155dfbf4dc 100644 --- a/chromium/url/url_canon_path.cc +++ b/chromium/url/url_canon_path.cc @@ -162,6 +162,76 @@ void BackUpToPreviousSlash(int path_begin_in_output, output->set_length(i + 1); } +// Looks for problematic nested escape sequences and escapes the output as +// needed to ensure they can't be misinterpreted. +// +// Our concern is that in input escape sequence that's invalid because it +// contains nested escape sequences might look valid once those are unescaped. +// For example, "%%300" is not a valid escape sequence, but after unescaping the +// inner "%30" this becomes "%00" which is valid. Leaving this in the output +// string can result in callers re-canonicalizing the string and unescaping this +// sequence, thus resulting in something fundamentally different than the +// original input here. This can cause a variety of problems. +// +// This function is called after we've just unescaped a sequence that's within +// two output characters of a previous '%' that we know didn't begin a valid +// escape sequence in the input string. We look for whether the output is going +// to turn into a valid escape sequence, and if so, convert the initial '%' into +// an escaped "%25" so the output can't be misinterpreted. +// +// |spec| is the input string we're canonicalizing. +// |next_input_index| is the index of the next unprocessed character in |spec|. +// |input_len| is the length of |spec|. +// |last_invalid_percent_index| is the index in |output| of a previously-seen +// '%' character. The caller knows this '%' character isn't followed by a valid +// escape sequence in the input string. +// |output| is the canonicalized output thus far. The caller guarantees this +// ends with a '%' followed by one or two characters, and the '%' is the one +// pointed to by |last_invalid_percent_index|. The last character in the string +// was just unescaped. +template<typename CHAR> +void CheckForNestedEscapes(const CHAR* spec, + int next_input_index, + int input_len, + int last_invalid_percent_index, + CanonOutput* output) { + const int length = output->length(); + const char last_unescaped_char = output->at(length - 1); + + // If |output| currently looks like "%c", we need to try appending the next + // input character to see if this will result in a problematic escape + // sequence. Note that this won't trigger on the first nested escape of a + // two-escape sequence like "%%30%30" -- we'll allow the conversion to + // "%0%30" -- but the second nested escape will be caught by this function + // when it's called again in that case. + const bool append_next_char = last_invalid_percent_index == length - 2; + if (append_next_char) { + // If the input doesn't contain a 7-bit character next, this case won't be a + // problem. + if ((next_input_index == input_len) || (spec[next_input_index] >= 0x80)) + return; + output->push_back(static_cast<char>(spec[next_input_index])); + } + + // Now output ends like "%cc". Try to unescape this. + int begin = last_invalid_percent_index; + unsigned char temp; + if (DecodeEscaped(output->data(), &begin, output->length(), &temp)) { + // New escape sequence found. Overwrite the characters following the '%' + // with "25", and push_back() the one or two characters that were following + // the '%' when we were called. + if (!append_next_char) + output->push_back(output->at(last_invalid_percent_index + 1)); + output->set(last_invalid_percent_index + 1, '2'); + output->set(last_invalid_percent_index + 2, '5'); + output->push_back(last_unescaped_char); + } else if (append_next_char) { + // Not a valid escape sequence, but we still need to undo appending the next + // source character so the caller can process it normally. + output->set_length(length); + } +} + // Appends the given path to the output. It assumes that if the input path // starts with a slash, it should be copied to the output. If no path has // already been appended to the output (the case when not resolving @@ -182,10 +252,15 @@ bool DoPartialPath(const CHAR* spec, CanonOutput* output) { int end = path.end(); + // We use this variable to minimize the amount of work done when unescaping -- + // we'll only call CheckForNestedEscapes() when this points at one of the last + // couple of characters in |output|. + int last_invalid_percent_index = INT_MIN; + bool success = true; for (int i = path.begin; i < end; i++) { UCHAR uch = static_cast<UCHAR>(spec[i]); - if (sizeof(CHAR) > sizeof(char) && uch >= 0x80) { + if (sizeof(CHAR) > 1 && uch >= 0x80) { // We only need to test wide input for having non-ASCII characters. For // narrow input, we'll always just use the lookup table. We don't try to // do anything tricky with decoding/validating UTF-8. This function will @@ -245,33 +320,40 @@ bool DoPartialPath(const CHAR* spec, unsigned char unescaped_value; if (DecodeEscaped(spec, &i, end, &unescaped_value)) { // Valid escape sequence, see if we keep, reject, or unescape it. + // Note that at this point DecodeEscape() will have advanced |i| to + // the last character of the escape sequence. char unescaped_flags = kPathCharLookup[unescaped_value]; if (unescaped_flags & UNESCAPE) { - // This escaped value shouldn't be escaped, copy it. + // This escaped value shouldn't be escaped. Try to copy it. output->push_back(unescaped_value); - } else if (unescaped_flags & INVALID_BIT) { - // Invalid escaped character, copy it and remember the error. - output->push_back('%'); - output->push_back(static_cast<char>(spec[i - 1])); - output->push_back(static_cast<char>(spec[i])); - success = false; + // If we just unescaped a value within 2 output characters of the + // '%' from a previously-detected invalid escape sequence, we + // might have an input string with problematic nested escape + // sequences; detect and fix them. + if (last_invalid_percent_index >= (output->length() - 3)) { + CheckForNestedEscapes(spec, i + 1, end, + last_invalid_percent_index, output); + } } else { - // Valid escaped character but we should keep it escaped. We - // don't want to change the case of any hex letters in case - // the server is sensitive to that, so we just copy the two - // characters without checking (DecodeEscape will have advanced - // to the last character of the pair). + // Either this is an invalid escaped character, or it's a valid + // escaped character we should keep escaped. In the first case we + // should just copy it exactly and remember the error. In the + // second we also copy exactly in case the server is sensitive to + // changing the case of any hex letters. output->push_back('%'); output->push_back(static_cast<char>(spec[i - 1])); output->push_back(static_cast<char>(spec[i])); + if (unescaped_flags & INVALID_BIT) + success = false; } } else { - // Invalid escape sequence. IE7 rejects any URLs with such - // sequences, while Firefox, IE6, and Safari all pass it through - // unchanged. We are more permissive unlike IE7. I don't think this - // can cause significant problems, if it does, we should change - // to be more like IE7. + // Invalid escape sequence. IE7+ rejects any URLs with such + // sequences, while other browsers pass them through unchanged. We + // use the permissive behavior. + // TODO(brettw): Consider testing IE's strict behavior, which would + // allow removing the code to handle nested escapes above. + last_invalid_percent_index = output->length(); output->push_back('%'); } diff --git a/chromium/url/url_canon_unittest.cc b/chromium/url/url_canon_unittest.cc index 67cced24607..f2247d1dd9d 100644 --- a/chromium/url/url_canon_unittest.cc +++ b/chromium/url/url_canon_unittest.cc @@ -1059,6 +1059,21 @@ TEST(URLCanonTest, Path) { {"/%7Ffp3%3Eju%3Dduvgw%3Dd", L"/%7Ffp3%3Eju%3Dduvgw%3Dd", "/%7Ffp3%3Eju%3Dduvgw%3Dd", Component(0, 24), true}, // @ should be passed through unchanged (escaped or unescaped). {"/@asdf%40", L"/@asdf%40", "/@asdf%40", Component(0, 9), true}, + // Nested escape sequences should result in escaping the leading '%' if + // unescaping would result in a new escape sequence. + {"/%A%42", L"/%A%42", "/%25AB", Component(0, 6), true}, + {"/%%41B", L"/%%41B", "/%25AB", Component(0, 6), true}, + {"/%%41%42", L"/%%41%42", "/%25AB", Component(0, 6), true}, + // Make sure truncated "nested" escapes don't result in reading off the + // string end. + {"/%%41", L"/%%41", "/%A", Component(0, 3), true}, + // Don't unescape the leading '%' if unescaping doesn't result in a valid + // new escape sequence. + {"/%%470", L"/%%470", "/%G0", Component(0, 4), true}, + {"/%%2D%41", L"/%%2D%41", "/%-A", Component(0, 4), true}, + // Don't erroneously downcast a UTF-16 charater in a way that makes it + // look like part of an escape sequence. + {NULL, L"/%%41\x0130", "/%A%C4%B0", Component(0, 9), true}, // ----- encoding tests ----- // Basic conversions diff --git a/chromium/v8/build/features.gypi b/chromium/v8/build/features.gypi index 0e26969f646..59d15134ddf 100644 --- a/chromium/v8/build/features.gypi +++ b/chromium/v8/build/features.gypi @@ -108,7 +108,7 @@ 'DebugBaseCommon': { 'abstract': 1, 'variables': { - 'v8_enable_handle_zapping%': 0, + 'v8_enable_handle_zapping%': 1, }, 'conditions': [ ['v8_enable_handle_zapping==1', { @@ -118,7 +118,7 @@ }, # Debug 'Release': { 'variables': { - 'v8_enable_handle_zapping%': 1, + 'v8_enable_handle_zapping%': 0, }, 'conditions': [ ['v8_enable_handle_zapping==1', { diff --git a/chromium/v8/codereview.settings b/chromium/v8/codereview.settings index 7c4dd8e2556..532e4b4d7b0 100644 --- a/chromium/v8/codereview.settings +++ b/chromium/v8/codereview.settings @@ -1,5 +1,5 @@ CODE_REVIEW_SERVER: https://codereview.chromium.org -CC_LIST: v8-dev@googlegroups.com +CC_LIST: v8-reviews@googlegroups.com VIEW_VC: https://chromium.googlesource.com/v8/v8/+/ STATUS: http://v8-status.appspot.com/status TRY_ON_UPLOAD: False diff --git a/chromium/v8/include/v8-version.h b/chromium/v8/include/v8-version.h index 66e8c6eaa58..98dca238c85 100644 --- a/chromium/v8/include/v8-version.h +++ b/chromium/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 4 #define V8_MINOR_VERSION 5 #define V8_BUILD_NUMBER 103 -#define V8_PATCH_LEVEL 28 +#define V8_PATCH_LEVEL 35 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/chromium/v8/src/arm/assembler-arm.cc b/chromium/v8/src/arm/assembler-arm.cc index 96bdf79face..481a3b5cedd 100644 --- a/chromium/v8/src/arm/assembler-arm.cc +++ b/chromium/v8/src/arm/assembler-arm.cc @@ -128,7 +128,8 @@ void CpuFeatures::ProbeImpl(bool cross_compile) { if (FLAG_enable_32dregs && cpu.has_vfp3_d32()) supported_ |= 1u << VFP32DREGS; if (cpu.implementer() == base::CPU::NVIDIA && - cpu.variant() == base::CPU::NVIDIA_DENVER) { + cpu.variant() == base::CPU::NVIDIA_DENVER && + cpu.part() <= base::CPU::NVIDIA_DENVER_V10) { supported_ |= 1u << COHERENT_CACHE; } #endif diff --git a/chromium/v8/src/arm64/assembler-arm64.cc b/chromium/v8/src/arm64/assembler-arm64.cc index 5445fe1a1b1..f27d3b97b0f 100644 --- a/chromium/v8/src/arm64/assembler-arm64.cc +++ b/chromium/v8/src/arm64/assembler-arm64.cc @@ -53,7 +53,8 @@ void CpuFeatures::ProbeImpl(bool cross_compile) { // Probe for runtime features base::CPU cpu; if (cpu.implementer() == base::CPU::NVIDIA && - cpu.variant() == base::CPU::NVIDIA_DENVER) { + cpu.variant() == base::CPU::NVIDIA_DENVER && + cpu.part() <= base::CPU::NVIDIA_DENVER_V10) { supported_ |= 1u << COHERENT_CACHE; } } diff --git a/chromium/v8/src/ast.cc b/chromium/v8/src/ast.cc index e52504a86fb..ec74e4afaf7 100644 --- a/chromium/v8/src/ast.cc +++ b/chromium/v8/src/ast.cc @@ -441,6 +441,7 @@ void ObjectLiteral::BuildConstantProperties(Isolate* isolate) { if (position == boilerplate_properties_ * 2) { DCHECK(property->is_computed_name()); + is_simple = false; break; } DCHECK(!property->is_computed_name()); diff --git a/chromium/v8/src/base/cpu.h b/chromium/v8/src/base/cpu.h index f6c5a8506a0..1dc0a91f650 100644 --- a/chromium/v8/src/base/cpu.h +++ b/chromium/v8/src/base/cpu.h @@ -59,6 +59,9 @@ class CPU final { static const int ARM_CORTEX_A12 = 0xc0c; static const int ARM_CORTEX_A15 = 0xc0f; + // Denver-specific part code + static const int NVIDIA_DENVER_V10 = 0x002; + // PPC-specific part codes enum { PPC_POWER5, diff --git a/chromium/v8/src/flag-definitions.h b/chromium/v8/src/flag-definitions.h index a89c8e4d4ba..79611270dd4 100644 --- a/chromium/v8/src/flag-definitions.h +++ b/chromium/v8/src/flag-definitions.h @@ -613,6 +613,8 @@ DEFINE_INT(trace_allocation_stack_interval, -1, DEFINE_BOOL(trace_fragmentation, false, "report fragmentation for old space") DEFINE_BOOL(trace_fragmentation_verbose, false, "report fragmentation for old space (detailed)") +DEFINE_BOOL(trace_mutator_utilization, false, + "print mutator utilization, allocation speed, gc speed") DEFINE_BOOL(weak_embedded_maps_in_optimized_code, true, "make maps embedded in optimized code weak") DEFINE_BOOL(weak_embedded_objects_in_optimized_code, true, diff --git a/chromium/v8/src/heap/gc-tracer.cc b/chromium/v8/src/heap/gc-tracer.cc index 214bb8b24d6..6728f09bdad 100644 --- a/chromium/v8/src/heap/gc-tracer.cc +++ b/chromium/v8/src/heap/gc-tracer.cc @@ -682,11 +682,15 @@ size_t GCTracer::AllocationThroughputInBytesPerMillisecond( } +size_t GCTracer::CurrentAllocationThroughputInBytesPerMillisecond() const { + return AllocationThroughputInBytesPerMillisecond(kThroughputTimeFrameMs); +} + + size_t GCTracer::CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const { - static const double kThroughputTimeFrame = 5000; return OldGenerationAllocationThroughputInBytesPerMillisecond( - kThroughputTimeFrame); + kThroughputTimeFrameMs); } diff --git a/chromium/v8/src/heap/gc-tracer.h b/chromium/v8/src/heap/gc-tracer.h index 468fc9c0be7..e26fc898f99 100644 --- a/chromium/v8/src/heap/gc-tracer.h +++ b/chromium/v8/src/heap/gc-tracer.h @@ -291,6 +291,8 @@ class GCTracer { typedef RingBuffer<SurvivalEvent, kRingBufferMaxSize> SurvivalEventBuffer; + static const int kThroughputTimeFrameMs = 5000; + explicit GCTracer(Heap* heap); // Start collecting data. @@ -408,6 +410,11 @@ class GCTracer { // Returns 0 if no allocation events have been recorded. size_t AllocationThroughputInBytesPerMillisecond(double time_ms) const; + // Allocation throughput in heap in bytes/milliseconds in + // the last five seconds. + // Returns 0 if no allocation events have been recorded. + size_t CurrentAllocationThroughputInBytesPerMillisecond() const; + // Allocation throughput in old generation in bytes/milliseconds in // the last five seconds. // Returns 0 if no allocation events have been recorded. diff --git a/chromium/v8/src/heap/heap.cc b/chromium/v8/src/heap/heap.cc index fc36be8041f..3d953730013 100644 --- a/chromium/v8/src/heap/heap.cc +++ b/chromium/v8/src/heap/heap.cc @@ -775,7 +775,8 @@ void Heap::PreprocessStackTraces() { void Heap::HandleGCRequest() { if (incremental_marking()->request_type() == IncrementalMarking::COMPLETE_MARKING) { - CollectAllGarbage(Heap::kNoGCFlags, "GC interrupt"); + CollectAllGarbage(Heap::kNoGCFlags, "GC interrupt", + incremental_marking()->CallbackFlags()); return; } DCHECK(FLAG_overapproximate_weak_closure); @@ -985,7 +986,7 @@ bool Heap::CollectGarbage(GarbageCollector collector, const char* gc_reason, if (!mark_compact_collector()->abort_incremental_marking() && incremental_marking()->IsStopped() && incremental_marking()->ShouldActivateEvenWithoutIdleNotification()) { - incremental_marking()->Start(kNoGCFlags); + incremental_marking()->Start(kNoGCFlags, kNoGCCallbackFlags, "GC epilogue"); } return next_gc_likely_to_collect_more; @@ -1012,9 +1013,18 @@ int Heap::NotifyContextDisposed(bool dependant_context) { } +void Heap::StartIncrementalMarking(int gc_flags, + const GCCallbackFlags gc_callback_flags, + const char* reason) { + DCHECK(incremental_marking()->IsStopped()); + incremental_marking()->Start(gc_flags, gc_callback_flags, reason); +} + + void Heap::StartIdleIncrementalMarking() { gc_idle_time_handler_.ResetNoProgressCounter(); - incremental_marking()->Start(kReduceMemoryFootprintMask); + StartIncrementalMarking(kReduceMemoryFootprintMask, kNoGCCallbackFlags, + "idle"); } @@ -4762,27 +4772,63 @@ void Heap::MakeHeapIterable() { } -bool Heap::HasLowYoungGenerationAllocationRate() { - const double high_mutator_utilization = 0.993; +static double ComputeMutatorUtilization(double mutator_speed, double gc_speed) { + const double kMinMutatorUtilization = 0.0; + const double kConservativeGcSpeedInBytesPerMillisecond = 200000; + if (mutator_speed == 0) return kMinMutatorUtilization; + if (gc_speed == 0) gc_speed = kConservativeGcSpeedInBytesPerMillisecond; + // Derivation: + // mutator_utilization = mutator_time / (mutator_time + gc_time) + // mutator_time = 1 / mutator_speed + // gc_time = 1 / gc_speed + // mutator_utilization = (1 / mutator_speed) / + // (1 / mutator_speed + 1 / gc_speed) + // mutator_utilization = gc_speed / (mutator_speed + gc_speed) + return gc_speed / (mutator_speed + gc_speed); +} + + +double Heap::YoungGenerationMutatorUtilization() { double mutator_speed = static_cast<double>( tracer()->NewSpaceAllocationThroughputInBytesPerMillisecond()); double gc_speed = static_cast<double>( tracer()->ScavengeSpeedInBytesPerMillisecond(kForSurvivedObjects)); - if (mutator_speed == 0 || gc_speed == 0) return false; - double mutator_utilization = gc_speed / (mutator_speed + gc_speed); - return mutator_utilization > high_mutator_utilization; + double result = ComputeMutatorUtilization(mutator_speed, gc_speed); + if (FLAG_trace_mutator_utilization) { + PrintIsolate(isolate(), + "Young generation mutator utilization = %.3f (" + "mutator_speed=%.f, gc_speed=%.f)\n", + result, mutator_speed, gc_speed); + } + return result; } -bool Heap::HasLowOldGenerationAllocationRate() { - const double high_mutator_utilization = 0.993; +double Heap::OldGenerationMutatorUtilization() { double mutator_speed = static_cast<double>( tracer()->OldGenerationAllocationThroughputInBytesPerMillisecond()); double gc_speed = static_cast<double>( tracer()->CombinedMarkCompactSpeedInBytesPerMillisecond()); - if (mutator_speed == 0 || gc_speed == 0) return false; - double mutator_utilization = gc_speed / (mutator_speed + gc_speed); - return mutator_utilization > high_mutator_utilization; + double result = ComputeMutatorUtilization(mutator_speed, gc_speed); + if (FLAG_trace_mutator_utilization) { + PrintIsolate(isolate(), + "Old generation mutator utilization = %.3f (" + "mutator_speed=%.f, gc_speed=%.f)\n", + result, mutator_speed, gc_speed); + } + return result; +} + + +bool Heap::HasLowYoungGenerationAllocationRate() { + const double high_mutator_utilization = 0.993; + return YoungGenerationMutatorUtilization() > high_mutator_utilization; +} + + +bool Heap::HasLowOldGenerationAllocationRate() { + const double high_mutator_utilization = 0.993; + return OldGenerationMutatorUtilization() > high_mutator_utilization; } @@ -4808,7 +4854,13 @@ bool Heap::HasHighFragmentation(intptr_t used, intptr_t committed) { void Heap::ReduceNewSpaceSize() { - if (!FLAG_predictable && HasLowAllocationRate()) { + // TODO(ulan): Unify this constant with the similar constant in + // GCIdleTimeHandler once the change is merged to 4.5. + static const size_t kLowAllocationThroughput = 1000; + size_t allocation_throughput = + tracer()->CurrentAllocationThroughputInBytesPerMillisecond(); + if (FLAG_predictable || allocation_throughput == 0) return; + if (allocation_throughput < kLowAllocationThroughput) { new_space_.Shrink(); UncommitFromSpace(); } @@ -4867,6 +4919,32 @@ GCIdleTimeHandler::HeapState Heap::ComputeHeapState() { } +double Heap::AdvanceIncrementalMarking( + intptr_t step_size_in_bytes, double deadline_in_ms, + IncrementalMarking::StepActions step_actions) { + DCHECK(!incremental_marking()->IsStopped()); + + if (step_size_in_bytes == 0) { + step_size_in_bytes = GCIdleTimeHandler::EstimateMarkingStepSize( + static_cast<size_t>(GCIdleTimeHandler::kIncrementalMarkingStepTimeInMs), + static_cast<size_t>( + tracer()->FinalIncrementalMarkCompactSpeedInBytesPerMillisecond())); + } + + double remaining_time_in_ms = 0.0; + do { + incremental_marking()->Step( + step_size_in_bytes, step_actions.completion_action, + step_actions.force_marking, step_actions.force_completion); + remaining_time_in_ms = deadline_in_ms - MonotonicallyIncreasingTimeInMs(); + } while (remaining_time_in_ms >= + 2.0 * GCIdleTimeHandler::kIncrementalMarkingStepTimeInMs && + !incremental_marking()->IsComplete() && + !mark_compact_collector_.marking_deque()->IsEmpty()); + return remaining_time_in_ms; +} + + bool Heap::PerformIdleTimeAction(GCIdleTimeAction action, GCIdleTimeHandler::HeapState heap_state, double deadline_in_ms) { @@ -4876,19 +4954,9 @@ bool Heap::PerformIdleTimeAction(GCIdleTimeAction action, result = true; break; case DO_INCREMENTAL_MARKING: { - DCHECK(!incremental_marking()->IsStopped()); - double remaining_idle_time_in_ms = 0.0; - do { - incremental_marking()->Step( - action.parameter, IncrementalMarking::NO_GC_VIA_STACK_GUARD, - IncrementalMarking::FORCE_MARKING, - IncrementalMarking::DO_NOT_FORCE_COMPLETION); - remaining_idle_time_in_ms = - deadline_in_ms - MonotonicallyIncreasingTimeInMs(); - } while (remaining_idle_time_in_ms >= - 2.0 * GCIdleTimeHandler::kIncrementalMarkingStepTimeInMs && - !incremental_marking()->IsComplete() && - !mark_compact_collector_.marking_deque()->IsEmpty()); + const double remaining_idle_time_in_ms = + AdvanceIncrementalMarking(action.parameter, deadline_in_ms, + IncrementalMarking::IdleStepActions()); if (remaining_idle_time_in_ms > 0.0) { action.additional_work = TryFinalizeIdleIncrementalMarking( remaining_idle_time_in_ms, heap_state.size_of_objects, diff --git a/chromium/v8/src/heap/heap.h b/chromium/v8/src/heap/heap.h index 6fb429ae7d6..4c9e3ad1448 100644 --- a/chromium/v8/src/heap/heap.h +++ b/chromium/v8/src/heap/heap.h @@ -837,6 +837,21 @@ class Heap { // incremental steps. void StartIdleIncrementalMarking(); + // Starts incremental marking assuming incremental marking is currently + // stopped. + void StartIncrementalMarking(int gc_flags, + const GCCallbackFlags gc_callback_flags, + const char* reason = nullptr); + + // Performs incremental marking steps of step_size_in_bytes as long as + // deadline_ins_ms is not reached. step_size_in_bytes can be 0 to compute + // an estimate increment. Returns the remaining time that cannot be used + // for incremental marking anymore because a single step would exceed the + // deadline. + double AdvanceIncrementalMarking( + intptr_t step_size_in_bytes, double deadline_in_ms, + IncrementalMarking::StepActions step_actions); + inline void increment_scan_on_scavenge_pages() { scan_on_scavenge_pages_++; if (FLAG_gc_verbose) { @@ -2267,6 +2282,8 @@ class Heap { bool HasLowYoungGenerationAllocationRate(); bool HasLowOldGenerationAllocationRate(); + double YoungGenerationMutatorUtilization(); + double OldGenerationMutatorUtilization(); void ReduceNewSpaceSize(); diff --git a/chromium/v8/src/heap/incremental-marking.cc b/chromium/v8/src/heap/incremental-marking.cc index 22051eaab2d..58eb0aa4097 100644 --- a/chromium/v8/src/heap/incremental-marking.cc +++ b/chromium/v8/src/heap/incremental-marking.cc @@ -16,22 +16,34 @@ namespace v8 { namespace internal { +IncrementalMarking::StepActions IncrementalMarking::IdleStepActions() { + return StepActions(IncrementalMarking::NO_GC_VIA_STACK_GUARD, + IncrementalMarking::FORCE_MARKING, + IncrementalMarking::DO_NOT_FORCE_COMPLETION); +} + + IncrementalMarking::IncrementalMarking(Heap* heap) : heap_(heap), state_(STOPPED), + is_compacting_(false), steps_count_(0), old_generation_space_available_at_start_of_incremental_(0), old_generation_space_used_at_start_of_incremental_(0), + bytes_rescanned_(0), should_hurry_(false), marking_speed_(0), + bytes_scanned_(0), allocated_(0), + write_barriers_invoked_since_last_step_(0), idle_marking_delay_counter_(0), no_marking_scope_depth_(0), unscanned_bytes_of_large_object_(0), was_activated_(false), weak_closure_was_overapproximated_(false), weak_closure_approximation_rounds_(0), - request_type_(COMPLETE_MARKING) {} + request_type_(COMPLETE_MARKING), + gc_callback_flags_(kNoGCCallbackFlags) {} void IncrementalMarking::RecordWriteSlow(HeapObject* obj, Object** slot, @@ -472,9 +484,12 @@ static void PatchIncrementalMarkingRecordWriteStubs( } -void IncrementalMarking::Start(int mark_compact_flags) { +void IncrementalMarking::Start(int mark_compact_flags, + const GCCallbackFlags gc_callback_flags, + const char* reason) { if (FLAG_trace_incremental_marking) { - PrintF("[IncrementalMarking] Start\n"); + PrintF("[IncrementalMarking] Start (%s)\n", + (reason == nullptr) ? "unknown reason" : reason); } DCHECK(FLAG_incremental_marking); DCHECK(FLAG_incremental_marking_steps); @@ -484,6 +499,7 @@ void IncrementalMarking::Start(int mark_compact_flags) { ResetStepCounters(); + gc_callback_flags_ = gc_callback_flags; was_activated_ = true; if (!heap_->mark_compact_collector()->sweeping_in_progress()) { @@ -826,7 +842,7 @@ void IncrementalMarking::Epilogue() { void IncrementalMarking::OldSpaceStep(intptr_t allocated) { if (IsStopped() && ShouldActivateEvenWithoutIdleNotification()) { - Start(Heap::kNoGCFlags); + Start(Heap::kNoGCFlags, kNoGCCallbackFlags, "old space step"); } else { Step(allocated * kFastMarking / kInitialMarkingSpeed, GC_VIA_STACK_GUARD); } diff --git a/chromium/v8/src/heap/incremental-marking.h b/chromium/v8/src/heap/incremental-marking.h index 8fe341154fa..706e3323270 100644 --- a/chromium/v8/src/heap/incremental-marking.h +++ b/chromium/v8/src/heap/incremental-marking.h @@ -26,6 +26,21 @@ class IncrementalMarking { enum GCRequestType { COMPLETE_MARKING, OVERAPPROXIMATION }; + struct StepActions { + StepActions(CompletionAction complete_action_, + ForceMarkingAction force_marking_, + ForceCompletionAction force_completion_) + : completion_action(complete_action_), + force_marking(force_marking_), + force_completion(force_completion_) {} + + CompletionAction completion_action; + ForceMarkingAction force_marking; + ForceCompletionAction force_completion; + }; + + static StepActions IdleStepActions(); + explicit IncrementalMarking(Heap* heap); static void Initialize(); @@ -67,7 +82,9 @@ class IncrementalMarking { bool WasActivated(); - void Start(int mark_compact_flags); + void Start(int mark_compact_flags, + const GCCallbackFlags gc_callback_flags = kNoGCCallbackFlags, + const char* reason = nullptr); void Stop(); @@ -185,6 +202,8 @@ class IncrementalMarking { Heap* heap() const { return heap_; } + GCCallbackFlags CallbackFlags() const { return gc_callback_flags_; } + private: int64_t SpaceLeftInOldSpace(); @@ -243,6 +262,8 @@ class IncrementalMarking { GCRequestType request_type_; + GCCallbackFlags gc_callback_flags_; + DISALLOW_IMPLICIT_CONSTRUCTORS(IncrementalMarking); }; } diff --git a/chromium/v8/src/messages.h b/chromium/v8/src/messages.h index 4072300bf6a..5c3e867933c 100644 --- a/chromium/v8/src/messages.h +++ b/chromium/v8/src/messages.h @@ -173,6 +173,7 @@ class CallSite { T(ObserveCallbackFrozen, \ "Object.observe cannot deliver to a frozen function object") \ T(ObserveGlobalProxy, "% cannot be called on the global proxy object") \ + T(ObserveAccessChecked, "% cannot be called on access-checked objects") \ T(ObserveInvalidAccept, \ "Third argument to Object.observe must be an array of strings.") \ T(ObserveNonFunction, "Object.% cannot deliver to non-function") \ diff --git a/chromium/v8/src/object-observe.js b/chromium/v8/src/object-observe.js index 56859a1c97b..9c49fd38fb6 100644 --- a/chromium/v8/src/object-observe.js +++ b/chromium/v8/src/object-observe.js @@ -389,6 +389,8 @@ function ObjectObserve(object, callback, acceptList) { throw MakeTypeError(kObserveNonObject, "observe", "observe"); if (%IsJSGlobalProxy(object)) throw MakeTypeError(kObserveGlobalProxy, "observe"); + if (%IsAccessCheckNeeded(object)) + throw MakeTypeError(kObserveAccessChecked, "observe"); if (!IS_SPEC_FUNCTION(callback)) throw MakeTypeError(kObserveNonFunction, "observe"); if (ObjectIsFrozen(callback)) @@ -617,6 +619,8 @@ function ObjectGetNotifier(object) { throw MakeTypeError(kObserveNonObject, "getNotifier", "getNotifier"); if (%IsJSGlobalProxy(object)) throw MakeTypeError(kObserveGlobalProxy, "getNotifier"); + if (%IsAccessCheckNeeded(object)) + throw MakeTypeError(kObserveAccessChecked, "getNotifier"); if (ObjectIsFrozen(object)) return null; diff --git a/chromium/v8/src/runtime/runtime-object.cc b/chromium/v8/src/runtime/runtime-object.cc index da1ec4977be..9536ec0cc40 100644 --- a/chromium/v8/src/runtime/runtime-object.cc +++ b/chromium/v8/src/runtime/runtime-object.cc @@ -1435,5 +1435,13 @@ RUNTIME_FUNCTION(Runtime_DefineSetterPropertyUnchecked) { setter, attrs)); return isolate->heap()->undefined_value(); } + + +RUNTIME_FUNCTION(Runtime_IsAccessCheckNeeded) { + SealHandleScope shs(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_CHECKED(Object, object, 0); + return isolate->heap()->ToBoolean(object->IsAccessCheckNeeded()); +} } // namespace internal } // namespace v8 diff --git a/chromium/v8/src/runtime/runtime.h b/chromium/v8/src/runtime/runtime.h index da1ae40ba06..c4f74e7de11 100644 --- a/chromium/v8/src/runtime/runtime.h +++ b/chromium/v8/src/runtime/runtime.h @@ -483,7 +483,8 @@ namespace internal { F(IsStrong, 1, 1) \ F(ClassOf, 1, 1) \ F(DefineGetterPropertyUnchecked, 4, 1) \ - F(DefineSetterPropertyUnchecked, 4, 1) + F(DefineSetterPropertyUnchecked, 4, 1) \ + F(IsAccessCheckNeeded, 1, 1) #define FOR_EACH_INTRINSIC_OBSERVE(F) \ diff --git a/chromium/v8/src/scanner-character-streams.cc b/chromium/v8/src/scanner-character-streams.cc index 442bc75d6cc..eaaa9bc1f72 100644 --- a/chromium/v8/src/scanner-character-streams.cc +++ b/chromium/v8/src/scanner-character-streams.cc @@ -346,6 +346,7 @@ size_t ExternalStreamingStream::FillBuffer(size_t position) { current_data_length_ = source_stream_->GetMoreData(¤t_data_); current_data_offset_ = 0; bool data_ends = current_data_length_ == 0; + bookmark_data_is_from_current_data_ = false; // A caveat: a data chunk might end with bytes from an incomplete UTF-8 // character (the rest of the bytes will be in the next chunk). @@ -405,6 +406,15 @@ bool ExternalStreamingStream::SetBookmark() { // - buffer_[buffer_cursor_ .. buffer_end_] => bookmark_buffer_ // - current_data_[.._offset_ .. .._length_] => bookmark_data_ // - utf8_split_char_buffer_* => bookmark_utf8_split... + // + // To make sure we don't unnecessarily copy data, we also maintain + // whether bookmark_data_ contains a copy of the current current_data_ + // block. This is done with: + // - bookmark_data_is_from_current_data_ + // - bookmark_data_offset_: offset into bookmark_data_ + // + // Note that bookmark_data_is_from_current_data_ must be maintained + // whenever current_data_ is updated. bookmark_ = pos_; @@ -414,10 +424,21 @@ bool ExternalStreamingStream::SetBookmark() { CopyCharsUnsigned(bookmark_buffer_.start(), buffer_cursor_, buffer_length); size_t data_length = current_data_length_ - current_data_offset_; - bookmark_data_.Dispose(); - bookmark_data_ = Vector<uint8_t>::New(static_cast<int>(data_length)); - CopyBytes(bookmark_data_.start(), current_data_ + current_data_offset_, - data_length); + size_t bookmark_data_length = static_cast<size_t>(bookmark_data_.length()); + if (bookmark_data_is_from_current_data_ && + data_length < bookmark_data_length) { + // Fast case: bookmark_data_ was previously copied from the current + // data block, and we have enough data for this bookmark. + bookmark_data_offset_ = bookmark_data_length - data_length; + } else { + // Slow case: We need to copy current_data_. + bookmark_data_.Dispose(); + bookmark_data_ = Vector<uint8_t>::New(static_cast<int>(data_length)); + CopyBytes(bookmark_data_.start(), current_data_ + current_data_offset_, + data_length); + bookmark_data_is_from_current_data_ = true; + bookmark_data_offset_ = 0; + } bookmark_utf8_split_char_buffer_length_ = utf8_split_char_buffer_length_; for (size_t i = 0; i < utf8_split_char_buffer_length_; i++) { @@ -436,12 +457,14 @@ void ExternalStreamingStream::ResetToBookmark() { // bookmark_data_* => current_data_* // (current_data_ assumes ownership of its memory.) - uint8_t* data = new uint8_t[bookmark_data_.length()]; current_data_offset_ = 0; - current_data_length_ = bookmark_data_.length(); - CopyCharsUnsigned(data, bookmark_data_.begin(), bookmark_data_.length()); + current_data_length_ = bookmark_data_.length() - bookmark_data_offset_; + uint8_t* data = new uint8_t[current_data_length_]; + CopyCharsUnsigned(data, bookmark_data_.begin() + bookmark_data_offset_, + current_data_length_); delete[] current_data_; current_data_ = data; + bookmark_data_is_from_current_data_ = true; // bookmark_buffer_ needs to be copied to buffer_. CopyCharsUnsigned(buffer_, bookmark_buffer_.begin(), @@ -462,6 +485,7 @@ void ExternalStreamingStream::FlushCurrent() { current_data_ = NULL; current_data_length_ = 0; current_data_offset_ = 0; + bookmark_data_is_from_current_data_ = false; } diff --git a/chromium/v8/src/scanner-character-streams.h b/chromium/v8/src/scanner-character-streams.h index 582165710db..f3ee20463af 100644 --- a/chromium/v8/src/scanner-character-streams.h +++ b/chromium/v8/src/scanner-character-streams.h @@ -94,6 +94,8 @@ class ExternalStreamingStream : public BufferedUtf16CharacterStream { current_data_length_(0), utf8_split_char_buffer_length_(0), bookmark_(0), + bookmark_data_is_from_current_data_(false), + bookmark_data_offset_(0), bookmark_utf8_split_char_buffer_length_(0) {} virtual ~ExternalStreamingStream() { @@ -134,6 +136,8 @@ class ExternalStreamingStream : public BufferedUtf16CharacterStream { size_t bookmark_; Vector<uint16_t> bookmark_buffer_; Vector<uint8_t> bookmark_data_; + bool bookmark_data_is_from_current_data_; + size_t bookmark_data_offset_; uint8_t bookmark_utf8_split_char_buffer_[4]; size_t bookmark_utf8_split_char_buffer_length_; }; |