diff --git a/DEPS b/DEPS index 083144e3..c33d18f 100644 --- a/DEPS +++ b/DEPS
@@ -43,7 +43,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'eae618137190cfe34670243e10b5f1d7fe586b2a', + 'v8_revision': '951fd121057e09553b46c1e067bad9bd4f831c75', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other.
diff --git a/chrome/VERSION b/chrome/VERSION index 11036d83..d9d715a 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=49 MINOR=0 -BUILD=2605 +BUILD=2606 PATCH=0
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index d501e18..557b200 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2269,8 +2269,21 @@ if (!prefs->GetBoolean(prefs::kWebKitJavascriptEnabled)) web_prefs->javascript_enabled = false; - if (!prefs->GetBoolean(prefs::kWebKitWebSecurityEnabled)) + + // Only allow disabling web security via the command-line flag if the user + // has specified a distinct profile directory. This still enables tests to + // disable web security by setting the pref directly. + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (!prefs->GetBoolean(prefs::kWebKitWebSecurityEnabled)) { web_prefs->web_security_enabled = false; + } else if (!web_prefs->web_security_enabled && + command_line->HasSwitch(switches::kDisableWebSecurity) && + !command_line->HasSwitch(switches::kUserDataDir)) { + LOG(ERROR) << "Web security may only be disabled if '--user-data-dir' is " + "also specified."; + web_prefs->web_security_enabled = true; + } + if (!prefs->GetBoolean(prefs::kWebKitPluginsEnabled)) web_prefs->plugins_enabled = false; web_prefs->loads_images_automatically =
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc index b9ea7ad..10e9520 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -1689,11 +1689,14 @@ break; // Nothing to do for DETACH_ABOVE_OR_BELOW. } - // To account for the extra vertical on restored windows that is absent on - // maximized windows, add an additional vertical offset extracted from the tab - // strip. - if (source->GetWidget()->IsMaximized()) - new_bounds.Offset(0, -source->kNewTabButtonVerticalOffset); + // Account for the extra space above the tabstrip on restored windows versus + // maximized windows. + if (source->GetWidget()->IsMaximized()) { + const auto* frame_view = static_cast<BrowserNonClientFrameView*>( + source->GetWidget()->non_client_view()->frame_view()); + new_bounds.Offset( + 0, frame_view->GetTopInset(false) - frame_view->GetTopInset(true)); + } return new_bounds; }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index a1751b1..0328f7f5 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -123,6 +123,9 @@ const int kPinnedToNonPinnedOffset = 3; #endif +// The vertical offset of the tab strip button. +const int kNewTabButtonVerticalOffset = 7; + // Returns the size of the new tab button, not including any bounds extension to // enlarge the clickable area. gfx::Size GetNewTabButtonSize() { @@ -449,7 +452,7 @@ if (ui::MaterialDesignController::IsModeMaterial()) { SkPath border; const float scale = GetWidget()->GetCompositor()->device_scale_factor(); - GetBorderPath(TabStrip::kNewTabButtonVerticalOffset * scale, scale, + GetBorderPath(kNewTabButtonVerticalOffset * scale, scale, tab_strip_->SizeTabButtonToTopOfTabStrip(), &border); mask->addPath(border, SkMatrix::MakeScale(1 / scale)); } else if (tab_strip_->SizeTabButtonToTopOfTabStrip()) { @@ -461,7 +464,7 @@ mask->addRect(RectToSkRect(button_bounds)); } else { SkScalar w = SkIntToScalar(width()); - SkScalar v_offset = SkIntToScalar(TabStrip::kNewTabButtonVerticalOffset); + SkScalar v_offset = SkIntToScalar(kNewTabButtonVerticalOffset); // These values are defined by the shape of the new tab image. Should that // image ever change, these values will need to be updated. They're so @@ -572,8 +575,7 @@ // no flip. x += size.width(); } - canvas->TileImageInt(*background, x, - TabStrip::kNewTabButtonVerticalOffset + offset_y, + canvas->TileImageInt(*background, x, kNewTabButtonVerticalOffset + offset_y, x_scale, 1.0f, 0, 0, size.width(), size.height()); // For non-MD, adjust the alpha of the fill to match that of inactive tabs @@ -659,9 +661,6 @@ /////////////////////////////////////////////////////////////////////////////// // TabStrip, public: -// static -const int TabStrip::kNewTabButtonVerticalOffset = 7; - TabStrip::TabStrip(TabStripController* controller) : controller_(controller), newtab_button_(NULL),
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h index aa09764..76a741c8c 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.h +++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -58,10 +58,6 @@ public views::ViewTargeterDelegate, public TabController { public: - // The vertical offset of the tab strip button. This offset applies only to - // restored windows. - static const int kNewTabButtonVerticalOffset; - explicit TabStrip(TabStripController* controller); ~TabStrip() override;
diff --git a/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc b/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc index 7b92e33e..7aeb5f1 100644 --- a/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc +++ b/chrome/browser/ui/webui/options/sync_setup_handler_unittest.cc
@@ -300,8 +300,6 @@ #if !defined(OS_CHROMEOS) TEST_F(SyncSetupHandlerFirstSigninTest, DisplayBasicLogin) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); // Ensure that the user is not signed in before calling |HandleStartSignin()|. @@ -324,8 +322,6 @@ TEST_F(SyncSetupHandlerTest, ShowSyncSetupWhenNotSignedIn) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); handler_->HandleShowSetupUI(NULL); @@ -359,8 +355,6 @@ // it is displaying the spinner to the user. TEST_F(SyncSetupHandlerTest, DisplayConfigureWithBackendDisabledAndCancel) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -385,8 +379,6 @@ TEST_F(SyncSetupHandlerTest, DisplayConfigureWithBackendDisabledAndSyncStartupCompleted) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -436,8 +428,6 @@ TEST_F(SyncSetupHandlerTest, DisplayConfigureWithBackendDisabledAndCancelAfterSigninSuccess) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -462,8 +452,6 @@ TEST_F(SyncSetupHandlerTest, DisplayConfigureWithBackendDisabledAndSigninFailed) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -496,8 +484,6 @@ TEST_F(SyncSetupHandlerNonCrosTest, HandleGaiaAuthFailure) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasUnrecoverableError()) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) @@ -511,8 +497,6 @@ // TODO(kochi): We need equivalent tests for ChromeOS. TEST_F(SyncSetupHandlerNonCrosTest, UnrecoverableErrorInitializingSync) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); // Open the web UI. @@ -523,8 +507,6 @@ TEST_F(SyncSetupHandlerNonCrosTest, GaiaErrorInitializingSync) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); // Open the web UI. @@ -774,8 +756,6 @@ SigninErrorControllerFactory::GetForProfile(profile_.get())); provider.SetAuthError(kTestUser, error_); EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, IsPassphraseRequired()) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc index 930005d3..3be38c6b 100644 --- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -304,8 +304,6 @@ #if !defined(OS_CHROMEOS) TEST_F(PeopleHandlerFirstSigninTest, DisplayBasicLogin) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); // Ensure that the user is not signed in before calling |HandleStartSignin()|. @@ -328,8 +326,6 @@ TEST_F(PeopleHandlerTest, ShowSyncSetupWhenNotSignedIn) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); handler_->HandleShowSetupUI(NULL); @@ -363,8 +359,6 @@ // it is displaying the spinner to the user. TEST_F(PeopleHandlerTest, DisplayConfigureWithBackendDisabledAndCancel) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -389,8 +383,6 @@ TEST_F(PeopleHandlerTest, DisplayConfigureWithBackendDisabledAndSyncStartupCompleted) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -440,8 +432,6 @@ TEST_F(PeopleHandlerTest, DisplayConfigureWithBackendDisabledAndCancelAfterSigninSuccess) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -466,8 +456,6 @@ TEST_F(PeopleHandlerTest, DisplayConfigureWithBackendDisabledAndSigninFailed) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); error_ = GoogleServiceAuthError::AuthErrorNone(); @@ -500,8 +488,6 @@ TEST_F(PeopleHandlerNonCrosTest, HandleGaiaAuthFailure) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasUnrecoverableError()) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) @@ -515,8 +501,6 @@ // TODO(kochi): We need equivalent tests for ChromeOS. TEST_F(PeopleHandlerNonCrosTest, UnrecoverableErrorInitializingSync) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); // Open the web UI. @@ -527,8 +511,6 @@ TEST_F(PeopleHandlerNonCrosTest, GaiaErrorInitializingSync) { EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, HasSyncSetupCompleted()) .WillRepeatedly(Return(false)); // Open the web UI. @@ -778,8 +760,6 @@ SigninErrorControllerFactory::GetForProfile(profile_.get())); provider.SetAuthError(kTestUser, error_); EXPECT_CALL(*mock_pss_, CanSyncStart()).WillRepeatedly(Return(true)); - EXPECT_CALL(*mock_pss_, IsOAuthRefreshTokenAvailable()) - .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_pss_, IsPassphraseRequired()) .WillRepeatedly(Return(false)); EXPECT_CALL(*mock_pss_, IsUsingSecondaryPassphrase())
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 6d73f09c..d5e408a 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -7771.0.0 \ No newline at end of file +7774.0.0 \ No newline at end of file
diff --git a/components/browser_sync/browser/profile_sync_service.cc b/components/browser_sync/browser/profile_sync_service.cc index 4dd43afd5..66f572df 100644 --- a/components/browser_sync/browser/profile_sync_service.cc +++ b/components/browser_sync/browser/profile_sync_service.cc
@@ -253,14 +253,6 @@ return IsSyncAllowed() && IsSyncRequested() && IsSignedIn(); } -bool ProfileSyncService::IsOAuthRefreshTokenAvailable() { - if (!oauth2_token_service_) - return false; - - return oauth2_token_service_->RefreshTokenIsAvailable( - signin_->GetAccountIdToUse()); -} - void ProfileSyncService::Initialize() { sync_client_->Initialize(this);
diff --git a/components/browser_sync/browser/profile_sync_service.h b/components/browser_sync/browser/profile_sync_service.h index 39b22b4..3ab8909 100644 --- a/components/browser_sync/browser/profile_sync_service.h +++ b/components/browser_sync/browser/profile_sync_service.h
@@ -344,10 +344,6 @@ void RegisterAuthNotifications(); void UnregisterAuthNotifications(); - // Return whether OAuth2 refresh token is loaded and available for the backend - // to start up. Virtual to enable mocking in tests. - virtual bool IsOAuthRefreshTokenAvailable(); - // Returns the SyncableService for syncer::SESSIONS. virtual syncer::SyncableService* GetSessionsSyncableService();
diff --git a/components/browser_sync/browser/profile_sync_service_mock.h b/components/browser_sync/browser/profile_sync_service_mock.h index c610b87..b05fec4 100644 --- a/components/browser_sync/browser/profile_sync_service_mock.h +++ b/components/browser_sync/browser/profile_sync_service_mock.h
@@ -86,7 +86,6 @@ MOCK_CONST_METHOD0(CanSyncStart, bool()); MOCK_CONST_METHOD0(IsManaged, bool()); - MOCK_METHOD0(IsOAuthRefreshTokenAvailable, bool()); MOCK_CONST_METHOD0(IsPassphraseRequired, bool()); MOCK_CONST_METHOD0(IsPassphraseRequiredForDecryption, bool());
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8BindingDesign.md b/third_party/WebKit/Source/bindings/core/v8/V8BindingDesign.md new file mode 100644 index 0000000..e02e97c --- /dev/null +++ b/third_party/WebKit/Source/bindings/core/v8/V8BindingDesign.md
@@ -0,0 +1,259 @@ +# Design of V8 bindings + +This document explains key concepts in the V8 binding architecture +except the lifetime management of DOM wrappers. +See [V8GCController.md](V8GCController.md) to learn the lifetime management. + +[TOC] + +## Isolate + +An isolate is a concept of a thread in V8. +Isolates and threads are in 1:1 relationship. +One isolate is associated with the main thread. +One isolate is associated with one worker thread. + +## Context + +A context is a concept of a global variable scope in V8. +Roughly speaking, one window object corresponds to one context. +For example, `<iframe>` has a window object different from a window object +of its parent frame. So the context of the `<iframe>` is different from +the context of the parent frame. Since these contexts create their own +global variable scopes, global variables and prototype chains of the `<iframe>` +are isolated from the ones of the parent frame. + +Here is an example: + +```html +// main.html +<html><body> +<iframe src="iframe.html"></iframe> +<script> +var foo = 1234; +String.prototype.substr = + function (position, length) { // Hijacks String.prototype.substr + console.log(length); + return "hijacked"; + }; +</script> +</body></html> + +// iframe.html +<script> +console.log(foo); // undefined +var bar = "aaaa".substr(0, 2); // Nothing is logged. +console.log(bar); // "aa" +</script> +``` + +In summary, each frame has a window object. +Each window object has a context. +Each context has its own global variable scope and prototype chains. + +## Entered context and current context + +A relationship between isolates and contexts is interesting. +One isolate has to execute JavaScripts in multiple frames, +each of which has its own context. This means that the context associated +with the isolate changes over time. In other words, the relationship between +isolates and contexts is 1:N over the lifetime of the isolate. + +Here we have a concept of an entered context and a current context. +To understand the difference, you need to understand two kinds of +runtime stacks. + +The first stack is a stack of JavaScript functions. +This stack is managed by V8. When one function calls another function, +the callee function is pushed onto the stack. When that function returns, +the function is popped from the stack and the control returns to the caller +function that is now on the top of the stack. Each function has +an associated context. We call the context of the function +that is currently running (i.e., the context of the function that is on the top +of the stack) a current context. + +Here is an example: + +```html +// main.html +<html><body> +<iframe src="iframe.html"></iframe> +<script> +var iframe = document.querySelector("iframe"); +iframe.onload = function () { + iframe.contentWindow.func(); +} +</script> +</body></html> + +// iframe.html +<script> +function func() { + ...; +} +</script> +``` + +In the above example, at the point when func() is running, +the current context is the context of the `<iframe>`. + +There is a second stack that operates on a much coarser granularity. +This stack is managed by V8 binding (not by V8). +When V8 binding invokes JavaScript, V8 binding enters a context +and pushes the context onto the stack. +The JavaScript starts running on the context. When the JavaScript finishes +and the control returns back to V8 binding, V8 binding pops the context +from the stack. Given that the control between V8 binding and V8 can be nested +(i.e., V8 binding invokes JavaScript, which calls into V8 binding, +which invokes another JavaScript etc), these contexts form a stack. +The pushing and popping are done by calling v8::Context::Enter() and +v8::Context::Exit() (or v8::Context::Scope). We call the most recently entered +context an entered context. + +In the above example, at the point when func() is running, +the entered context is the context of the main frame +(not the context of `<iframe>`). + +The entered context is a concept to implement the +[entry settings object](https://html.spec.whatwg.org/multipage/webappapis.html#entry-settings-object) +of the HTML spec. The current context is a concept to implement the +[incumbent settings object](https://html.spec.whatwg.org/multipage/webappapis.html#incumbent-settings-object) +of the HTML spec. + +In summary, the entered context is a context from which the current JavaScript +execution was started. The current context is a context of +the JavaScript function that is currently running. + +## World + +A world is a concept to sandbox DOM wrappers among content scripts of +Chrome extensions. There are three kinds of worlds: a main world, +an isolated world and a worker world. +A main world is a world where a normal JavaScript downloaded from the web +is executed. +An isolated world is a world where a content script of a Chrome extension. +An isolate of the main thread has 1 main world and N isolated worlds. +An isolate of a worker thread has 1 worker world and 0 isolated world. +[This diagram](https://drive.google.com/file/d/0B1obCOyvTnPKQmJEWkVtOEN2TmM/view?usp=sharing) +will be helpful to understand the relationship. + +All worlds in one isolate share underlying C++ DOM objects, +but each world has its own DOM wrappers. That way the worlds in one isolate +can operate on the same C++ DOM object without sharing any DOM wrapper +among the worlds. + +Also each world has its own context. +This means that each world has its own global variable scope and +prototype chains. + +As a result of the sandboxing, the worlds in one isolate cannot share +any DOM wrappers or contexts but can share underlying C++ DOM objects. +The fact that no DOM wrappers or contexts are shared means that no JavaScript +objects are shared among the worlds. That way we guarantee the security model +that Chrome extensions doesn't share any JavaScript objects while sharing +the underlying C++ DOM objects. This sandbox allows the Chrome extensions to run +untrusted JavaScripts on a shared DOM structure. + +(Note: An isolated world is a concept of V8 binding, +whereas an isolate and a context are a concept of V8. +V8 does not know what isolated worlds are in an isolate.) + +In summary, an isolate of the main thread consists of 1 main world +and N isolated worlds. An isolate of a worker thread consists of +1 worker world and 0 isolated world. All worlds in one isolate share the +underlying C++ DOM objects, but each world has its own DOM wrappers. +Each world has its own context and thus has its own global variable scope +and prototype chains. + +## A relationship between isolates, contexts, worlds and frames + +Let's wrap up the relationship between isolates, contexts, worlds and frames. + +* As a requirement of the DOM side, one HTML page has N frames. +Each frame has its own context. + +* As a requirement of the JavaScript side, one isolate has M worlds. +Each world has its own context. + +As a result, when we execute the main thread where N frames and M worlds +are involved, there exists N * M contexts. In other words, one context is +created for each pair of (frame, world). +[This diagram](https://drive.google.com/file/d/0B1obCOyvTnPKSERSMmpRVjVKQWc/view?usp=sharing) +will be helpful to understand the relationship. + +The main thread can have only one current context at one time, +but the main thread can have the N * M contexts over its lifetime. +For example, when the main thread is operating on a frame X using a JavaScript +in a world Y, the current context is set to a context for the pair of (X, Y). +The current context of the main thread changes over its lifetime. + +On the other hand, a worker thread has 0 frame and 1 world. +Thus a worker thread has only 1 context. +The current context of the worker thread never changes. + +## DOM wrappers and worlds + +For compatibility reasons (although this is not speced), +we need to make sure that the same DOM wrapper is returned to JavaScript +as long as the underlying C++ DOM object is alive. +We should not return different DOM wrappers for the same C++ DOM object. + +Here is an example: + +```html +var div = document.createElement("div"); +div.foo = 1234; // expando +var p = document.createElement("p"); +p.appendChild(div); +div = null; +gc(); +console.log(p.firstChild.foo); // This should be 1234, not undefined +``` + +To accomplish the semantics that the same DOM wrapper is returned to JavaScript +as long as the underlying C++ DOM object is alive, we need a mapping +from the C++ DOM objects to the DOM wrappers. +In addition, we need to sandbox DOM wrappers in each world. +To meet the requirements, we make each world hold a DOM wrapper storage +that stores a mapping from the C++ DOM objects to the DOM wrappers in that world. + +As a result, we have multiple DOM wrapper storages in one isolate. +The mapping of the main world is written in ScriptWrappable. +If ScriptWrappable::m_wrapper has a non-empty value, it is a DOM wrapper of +the C++ DOM object of the main world. +The mapping of other worlds are written in DOMWrapperMap. + +## DOM wrappers and contexts + +When you create a new DOM wrapper, you need to choose a correct context +on which the DOM wrapper is created. If you create a new DOM wrapper in a +wrong context, you will end up with leaking JavaScript objects to other +contexts, which is very likely to cause security issues. + +Here is an example: + +```html +// main.html +<html><body> +<iframe src="iframe.html"></iframe> +<script> +var iframe = document.querySelector("iframe"); +iframe; // The wrapper of the iframe should be created in the context of the main frame. +iframe.contentDocument; // The wrapper of the document should be created in the context of the iframe. +iframe.contentDocument.addEventListener("click", + function (event) { // The wrapper of the event should be created in the context of the iframe. + event.target; + }); +</script> +</body></html> + +// iframe.html +<script> +</script> +``` + +To make sure that a DOM wrapper is created in a correct context, you need to +make sure that the current context must be set to the correct context +whenever you call toV8(). If you're not sure what context to use, +ask haraken@chromium.org. +
diff --git a/third_party/WebKit/Source/wtf/Vector.h b/third_party/WebKit/Source/wtf/Vector.h index b2f295435..46ada79 100644 --- a/third_party/WebKit/Source/wtf/Vector.h +++ b/third_party/WebKit/Source/wtf/Vector.h
@@ -127,16 +127,16 @@ template <typename T> struct VectorMover<false, T> { - static void move(const T* src, const T* srcEnd, T* dst) + static void move(T* src, T* srcEnd, T* dst) { while (src != srcEnd) { - new (NotNull, dst) T(*src); + new (NotNull, dst) T(std::move(*src)); src->~T(); ++dst; ++src; } } - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + static void moveOverlapping(T* src, T* srcEnd, T* dst) { if (src > dst) { move(src, srcEnd, dst); @@ -145,7 +145,7 @@ while (src != srcEnd) { --srcEnd; --dstEnd; - new (NotNull, dstEnd) T(*srcEnd); + new (NotNull, dstEnd) T(std::move(*srcEnd)); srcEnd->~T(); } } @@ -267,12 +267,12 @@ VectorInitializer<VectorTraits<T>::canInitializeWithMemset, T>::initialize(begin, end); } - static void move(const T* src, const T* srcEnd, T* dst) + static void move(T* src, T* srcEnd, T* dst) { VectorMover<VectorTraits<T>::canMoveWithMemcpy, T>::move(src, srcEnd, dst); } - static void moveOverlapping(const T* src, const T* srcEnd, T* dst) + static void moveOverlapping(T* src, T* srcEnd, T* dst) { VectorMover<VectorTraits<T>::canMoveWithMemcpy, T>::moveOverlapping(src, srcEnd, dst); } @@ -745,16 +745,16 @@ void clear() { shrinkCapacity(0); } template <typename U> void append(const U*, size_t); - template <typename U> void append(const U&); - template <typename U> void uncheckedAppend(const U& val); + template <typename U> void append(U&&); + template <typename U> void uncheckedAppend(U&& val); template <typename U, size_t otherCapacity, typename V> void appendVector(const Vector<U, otherCapacity, V>&); template <typename U> void insert(size_t position, const U*, size_t); - template <typename U> void insert(size_t position, const U&); + template <typename U> void insert(size_t position, U&&); template <typename U, size_t c, typename V> void insert(size_t position, const Vector<U, c, V>&); template <typename U> void prepend(const U*, size_t); - template <typename U> void prepend(const U&); + template <typename U> void prepend(U&&); template <typename U, size_t c, typename V> void prepend(const Vector<U, c, V>&); void remove(size_t position); @@ -795,10 +795,15 @@ private: void expandCapacity(size_t newMinCapacity); - const T* expandCapacity(size_t newMinCapacity, const T*); + T* expandCapacity(size_t newMinCapacity, T*); + T* expandCapacity(size_t newMinCapacity, const T* data) + { + return expandCapacity(newMinCapacity, const_cast<T*>(data)); + } + template <typename U> U* expandCapacity(size_t newMinCapacity, U*); void shrinkCapacity(size_t newCapacity); - template <typename U> void appendSlowCase(const U&); + template <typename U> void appendSlowCase(U&&); using Base::m_size; using Base::buffer; @@ -978,7 +983,7 @@ } template <typename T, size_t inlineCapacity, typename Allocator> -const T* Vector<T, inlineCapacity, Allocator>::expandCapacity(size_t newMinCapacity, const T* ptr) +T* Vector<T, inlineCapacity, Allocator>::expandCapacity(size_t newMinCapacity, T* ptr) { if (ptr < begin() || ptr >= end()) { expandCapacity(newMinCapacity); @@ -1139,31 +1144,31 @@ template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> -ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::append(const U& val) +ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::append(U&& val) { ASSERT(Allocator::isAllocationAllowed()); if (LIKELY(size() != capacity())) { ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1); - new (NotNull, end()) T(val); + new (NotNull, end()) T(std::forward<U>(val)); ++m_size; return; } - appendSlowCase(val); + appendSlowCase(std::forward<U>(val)); } template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> -NEVER_INLINE void Vector<T, inlineCapacity, Allocator>::appendSlowCase(const U& val) +NEVER_INLINE void Vector<T, inlineCapacity, Allocator>::appendSlowCase(U&& val) { ASSERT(size() == capacity()); - const U* ptr = &val; + typename std::remove_reference<U>::type* ptr = &val; ptr = expandCapacity(size() + 1, ptr); ASSERT(begin()); ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1); - new (NotNull, end()) T(*ptr); + new (NotNull, end()) T(std::forward<U>(*ptr)); ++m_size; } @@ -1172,16 +1177,15 @@ template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> -ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::uncheckedAppend(const U& val) +ALWAYS_INLINE void Vector<T, inlineCapacity, Allocator>::uncheckedAppend(U&& val) { #ifdef ANNOTATE_CONTIGUOUS_CONTAINER // Vectors in ASAN builds don't have inlineCapacity. - append(val); + append(std::forward<U>(val)); #else ASSERT(size() < capacity()); ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1); - const U* ptr = &val; - new (NotNull, end()) T(*ptr); + new (NotNull, end()) T(std::forward<U>(val)); ++m_size; #endif } @@ -1214,11 +1218,11 @@ template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> -inline void Vector<T, inlineCapacity, Allocator>::insert(size_t position, const U& val) +inline void Vector<T, inlineCapacity, Allocator>::insert(size_t position, U&& val) { ASSERT(Allocator::isAllocationAllowed()); RELEASE_ASSERT(position <= size()); - const U* data = &val; + typename std::remove_reference<U>::type* data = &val; if (size() == capacity()) { data = expandCapacity(size() + 1, data); ASSERT(begin()); @@ -1226,7 +1230,7 @@ ANNOTATE_CHANGE_SIZE(begin(), capacity(), m_size, m_size + 1); T* spot = begin() + position; TypeOperations::moveOverlapping(spot, end(), spot + 1); - new (NotNull, spot) T(*data); + new (NotNull, spot) T(std::forward<U>(*data)); ++m_size; } @@ -1246,9 +1250,9 @@ template <typename T, size_t inlineCapacity, typename Allocator> template <typename U> -inline void Vector<T, inlineCapacity, Allocator>::prepend(const U& val) +inline void Vector<T, inlineCapacity, Allocator>::prepend(U&& val) { - insert(0, val); + insert(0, std::forward<U>(val)); } template <typename T, size_t inlineCapacity, typename Allocator>
diff --git a/third_party/WebKit/Source/wtf/VectorTest.cpp b/third_party/WebKit/Source/wtf/VectorTest.cpp index 2ede057..d06b47b 100644 --- a/third_party/WebKit/Source/wtf/VectorTest.cpp +++ b/third_party/WebKit/Source/wtf/VectorTest.cpp
@@ -189,7 +189,6 @@ for (index = 0; index < vector.size(); index++) { OwnPtr<DestructCounter>& refCounter = vector[index]; EXPECT_EQ(index, static_cast<size_t>(refCounter->get())); - index++; } EXPECT_EQ(0, destructNumber); @@ -228,6 +227,71 @@ EXPECT_EQ(count, static_cast<size_t>(destructNumber)); } +class MoveOnly { +public: + explicit MoveOnly(int i = 0) + : m_i(i) + { } + + MoveOnly(MoveOnly&& other) + : m_i(other.m_i) + { + other.m_i = 0; + } + + MoveOnly& operator=(MoveOnly&& other) + { + if (this != &other) { + m_i = other.m_i; + other.m_i = 0; + } + return *this; + } + + int value() const { return m_i; } + +private: + WTF_MAKE_NONCOPYABLE(MoveOnly); + int m_i; +}; + +TEST(VectorTest, MoveOnlyType) +{ + WTF::Vector<MoveOnly> vector; + vector.append(MoveOnly(1)); + vector.append(MoveOnly(2)); + EXPECT_EQ(2u, vector.size()); + + ASSERT_EQ(1, vector.first().value()); + ASSERT_EQ(2, vector.last().value()); + + vector.remove(0); + EXPECT_EQ(2, vector[0].value()); + EXPECT_EQ(1u, vector.size()); + + MoveOnly moveOnly(std::move(vector[0])); + vector.remove(0); + ASSERT_EQ(2, moveOnly.value()); + ASSERT_EQ(0u, vector.size()); + + size_t count = vector.capacity() + 1; + for (size_t i = 0; i < count; i++) + vector.append(MoveOnly(i + 1)); // +1 to distinguish from default-constructed. + + // Reallocation did not affect the vector's content. + EXPECT_EQ(count, vector.size()); + for (size_t i = 0; i < vector.size(); i++) + EXPECT_EQ(static_cast<int>(i + 1), vector[i].value()); + + WTF::Vector<MoveOnly> otherVector; + vector.swap(otherVector); + EXPECT_EQ(count, otherVector.size()); + EXPECT_EQ(0u, vector.size()); + + vector = std::move(otherVector); + EXPECT_EQ(count, vector.size()); +} + // WrappedInt class will fail if it was memmoved or memcpyed. static HashSet<void*> constructedWrappedInts; class WrappedInt { @@ -380,6 +444,21 @@ compare<WTF::String>(); } +TEST(VectorTest, AppendFirst) +{ + Vector<WTF::String> vector; + vector.append("string"); + // Test passes if it does not crash (reallocation did not make + // the input reference stale). + size_t limit = vector.capacity() + 1; + for (size_t i = 0; i < limit; i++) + vector.append(vector.first()); + + limit = vector.capacity() + 1; + for (size_t i = 0; i < limit; i++) + vector.append(const_cast<const WTF::String&>(vector.first())); +} + } // anonymous namespace } // namespace WTF