diff --git a/DEPS b/DEPS index 3fac46da..9e6da72 100644 --- a/DEPS +++ b/DEPS
@@ -36,7 +36,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': 'e930459a18ea099859f7d0076802458be00a6b4c', + 'skia_revision': '443023975e335e3630191227dbc21fa72c436af3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -88,7 +88,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '243e3dfbc911dba7d7ad2397aa5d975d6b85a854', + 'catapult_revision': '6cad5da3c39eca66c6dcd2db0b4cb1885fc00556', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/ash/common/wm/overview/scoped_transform_overview_window.cc b/ash/common/wm/overview/scoped_transform_overview_window.cc index ca19743..0816a10c 100644 --- a/ash/common/wm/overview/scoped_transform_overview_window.cc +++ b/ash/common/wm/overview/scoped_transform_overview_window.cc
@@ -180,7 +180,7 @@ class ScopedTransformOverviewWindow::OverviewContentMask : public ui::LayerDelegate { public: - OverviewContentMask(int inset, int radius); + explicit OverviewContentMask(float radius); ~OverviewContentMask() override; ui::Layer* layer() { return &layer_; } @@ -193,16 +193,14 @@ private: ui::Layer layer_; - int inset_; - int radius_; + float radius_; DISALLOW_COPY_AND_ASSIGN(OverviewContentMask); }; ScopedTransformOverviewWindow::OverviewContentMask::OverviewContentMask( - int inset, - int radius) - : layer_(ui::LAYER_TEXTURED), inset_(inset), radius_(radius) { + float radius) + : layer_(ui::LAYER_TEXTURED), radius_(radius) { layer_.set_delegate(this); } @@ -214,7 +212,6 @@ const ui::PaintContext& context) { ui::PaintRecorder recorder(context, layer()->size()); gfx::Rect bounds(layer()->bounds().size()); - bounds.Inset(0, inset_, 0, 0); // Tile a window into an area, rounding the bottom corners. const SkRect rect = gfx::RectToSkRect(bounds); @@ -260,11 +257,6 @@ ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() {} void ScopedTransformOverviewWindow::RestoreWindow() { - if (ash::MaterialDesignController::IsOverviewMaterial()) { - window()->GetLayer()->SetMaskLayer(nullptr); - mask_.reset(); - } - ScopedAnimationSettings animation_settings_list; BeginScopedAnimation(OverviewAnimationType::OVERVIEW_ANIMATION_RESTORE_WINDOW, &animation_settings_list); @@ -288,6 +280,20 @@ } window_->GetWindowState()->set_ignored_by_shelf(ignored_by_shelf_); SetOpacity(original_opacity_); + + if (ash::MaterialDesignController::IsOverviewMaterial()) { + ui::Layer* layer = window()->GetLayer(); + layer->SetMaskLayer(nullptr); + mask_.reset(); + + if (original_window_shape_) { + layer->SetAlphaShape( + base::WrapUnique(new SkRegion(*original_window_shape_.get()))); + } else { + layer->SetAlphaShape(nullptr); + } + window()->SetMasksToBounds(false); + } } void ScopedTransformOverviewWindow::BeginScopedAnimation( @@ -394,14 +400,31 @@ void ScopedTransformOverviewWindow::SetTransform( WmWindow* root_window, const gfx::Transform& transform, - int radius) { + float radius) { DCHECK(overview_started_); - if (ash::MaterialDesignController::IsOverviewMaterial() && !mask_) { - mask_.reset(new OverviewContentMask( - window()->GetIntProperty(WmWindowProperty::TOP_VIEW_INSET), radius)); - mask_->layer()->SetBounds(GetTargetBoundsInScreen()); + if (ash::MaterialDesignController::IsOverviewMaterial() && + &transform != &original_transform_) { + gfx::Rect bounds(GetTargetBoundsInScreen().size()); + mask_.reset(new OverviewContentMask(radius)); + mask_->layer()->SetFillsBoundsOpaquely(false); + mask_->layer()->SetBounds(bounds); window()->GetLayer()->SetMaskLayer(mask_->layer()); + + SkRegion* window_shape = window()->GetLayer()->alpha_shape(); + if (!original_window_shape_ && window_shape) + original_window_shape_.reset(new SkRegion(*window_shape)); + const int inset = + window()->GetIntProperty(WmWindowProperty::TOP_VIEW_INSET); + if (inset > 0) { + bounds.Inset(0, inset, 0, 0); + SkRegion* region = new SkRegion; + region->setRect(RectToSkIRect(bounds)); + if (original_window_shape_) + region->op(*original_window_shape_, SkRegion::kIntersect_Op); + window()->GetLayer()->SetAlphaShape(base::WrapUnique(region)); + window()->SetMasksToBounds(true); + } } gfx::Point target_origin(GetTargetBoundsInScreen().origin());
diff --git a/ash/common/wm/overview/scoped_transform_overview_window.h b/ash/common/wm/overview/scoped_transform_overview_window.h index 7445d76..e593106 100644 --- a/ash/common/wm/overview/scoped_transform_overview_window.h +++ b/ash/common/wm/overview/scoped_transform_overview_window.h
@@ -15,6 +15,8 @@ #include "ui/gfx/geometry/size.h" #include "ui/gfx/transform.h" +class SkRegion; + namespace gfx { class Rect; } @@ -106,7 +108,7 @@ // using rounded corners of |radius|. void SetTransform(WmWindow* root_window, const gfx::Transform& transform, - int radius); + float radius); // Set's the opacity of the managed windows. void SetOpacity(float opacity); @@ -135,6 +137,9 @@ // Mask layer that hides the original window header. std::unique_ptr<OverviewContentMask> mask_; + // Original window shape, if it was set on a window. + std::unique_ptr<SkRegion> original_window_shape_; + // If true, the window was minimized and should be restored if the window // was not selected. bool minimized_;
diff --git a/ash/common/wm/overview/window_grid.cc b/ash/common/wm/overview/window_grid.cc index acc094f..10003074 100644 --- a/ash/common/wm/overview/window_grid.cc +++ b/ash/common/wm/overview/window_grid.cc
@@ -783,6 +783,10 @@ // Immediately finish any active bounds animation. window->StopAnimatingProperty(ui::LayerAnimationElement::BOUNDS); + if (ash::MaterialDesignController::IsOverviewMaterial()) { + PositionWindows(false); + return; + } // Recompute the transform for the window. (*iter)->RecomputeWindowTransforms(); }
diff --git a/ash/common/wm/overview/window_selector_item.cc b/ash/common/wm/overview/window_selector_item.cc index ffa9104..7f58f4a 100644 --- a/ash/common/wm/overview/window_selector_item.cc +++ b/ash/common/wm/overview/window_selector_item.cc
@@ -477,8 +477,7 @@ // which when scaled will yield |kLabelBackgroundRadius|. transform_window_.SetTransform( root_window_, transform, - gfx::ToFlooredInt(kLabelBackgroundRadius / - GetItemScale(target_bounds.size()))); + (kLabelBackgroundRadius / GetItemScale(target_bounds.size()))); transform_window_.set_overview_transform(transform); }
diff --git a/ash/mus/user_window_controller_impl.cc b/ash/mus/user_window_controller_impl.cc index ba64ffb..365acc7 100644 --- a/ash/mus/user_window_controller_impl.cc +++ b/ash/mus/user_window_controller_impl.cc
@@ -197,10 +197,12 @@ user_window_observer_->OnUserWindowObserverAdded(std::move(user_windows)); } -void UserWindowControllerImpl::FocusUserWindow(uint32_t window_id) { +void UserWindowControllerImpl::ActivateUserWindow(uint32_t window_id) { ::ui::Window* window = GetUserWindowById(window_id); - if (window) + if (window) { + window->SetVisible(true); window->SetFocus(); + } } } // namespace mus
diff --git a/ash/mus/user_window_controller_impl.h b/ash/mus/user_window_controller_impl.h index 47bcfe3..c3a17f9 100644 --- a/ash/mus/user_window_controller_impl.h +++ b/ash/mus/user_window_controller_impl.h
@@ -56,7 +56,7 @@ // mojom::UserWindowController: void AddUserWindowObserver(mojom::UserWindowObserverPtr observer) override; - void FocusUserWindow(uint32_t window_id) override; + void ActivateUserWindow(uint32_t window_id) override; RootWindowController* root_controller_; mojom::UserWindowObserverPtr user_window_observer_;
diff --git a/ash/public/interfaces/user_window_controller.mojom b/ash/public/interfaces/user_window_controller.mojom index 2add8cca..0352ff07 100644 --- a/ash/public/interfaces/user_window_controller.mojom +++ b/ash/public/interfaces/user_window_controller.mojom
@@ -34,5 +34,5 @@ // TODO(msw): Add minimization, restoration, opening a chooser view, etc. interface UserWindowController { AddUserWindowObserver(UserWindowObserver observer); - FocusUserWindow(uint32 window_id); + ActivateUserWindow(uint32 window_id); };
diff --git a/ash/sysui/shelf_delegate_mus.cc b/ash/sysui/shelf_delegate_mus.cc index 5a925bd1..773a9ad 100644 --- a/ash/sysui/shelf_delegate_mus.cc +++ b/ash/sysui/shelf_delegate_mus.cc
@@ -94,7 +94,7 @@ return false; } void ExecuteCommand(int command_id, int event_flags) override { - item_delegate_->user_window_controller_->FocusUserWindow(command_id); + item_delegate_->user_window_controller_->ActivateUserWindow(command_id); } private: @@ -111,7 +111,7 @@ return kNewWindowCreated; } if (window_id_to_title_.size() == 1) { - user_window_controller_->FocusUserWindow( + user_window_controller_->ActivateUserWindow( window_id_to_title_.begin()->first); return kExistingWindowActivated; }
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json index 8349b199..bc7d3d0d 100644 --- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json +++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
@@ -570,6 +570,24 @@ "keyCode": [65, 66] } } + }, + { + "command": "toggleSelection", + "sequence": { + "cvoxModifier": true, + "keys": { + "keyCode": [83] + } + } + }, + { + "command": "copy", + "sequence": { + "keys": { + "keyCode": [67], + "ctrlKey": [true] + } + } } ] }
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js index cbc4fa7..db0f689 100644 --- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js +++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js
@@ -888,8 +888,6 @@ return true; } - cvox.ChromeVox.tts.speak(Msgs.getMsg(evt.type).toLowerCase(), - cvox.QueueMode.QUEUE); var text = ''; switch (evt.type) { case 'paste': @@ -900,7 +898,8 @@ text = window.getSelection().toString(); break; } - cvox.ChromeVox.tts.speak(text, cvox.QueueMode.QUEUE); + cvox.ChromeVox.tts.speak( + Msgs.getMsg(evt.type, [text]), cvox.QueueMode.QUEUE); cvox.ChromeVox.navigationManager.clearPageSel(); return true; };
diff --git a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js index 5d8213d..de39262 100644 --- a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js +++ b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
@@ -430,7 +430,7 @@ /** - * @type {chrome.automation.AutomationNode} + * @type {chrome.automation.AutomationRootNode} */ chrome.automation.AutomationNode.prototype.root; @@ -618,5 +618,31 @@ */ chrome.automation.AutomationNode.prototype.activeDescendant; +/** + * @extends {chrome.automation.AutomationNode} + * @constructor + */ +chrome.automation.AutomationRootNode = function() {}; + +/** + * @type {chrome.automation.AutomationNode} + */ +chrome.automation.AutomationRootNode.prototype.anchorObject; + +/** + * @type {number} + */ +chrome.automation.AutomationRootNode.prototype.anchorOffset; + +/** + * @type {chrome.automation.AutomationNode} + */ +chrome.automation.AutomationRootNode.prototype.focusObject; + +/** + * @type {number} + */ +chrome.automation.AutomationRootNode.prototype.focusOffset; + /** @type {function() : !Object} */ chrome.app.getDetails;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js index 453b1d2..3748fd1 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
@@ -796,6 +796,36 @@ cvox.BrailleCaptionsBackground.setActive( !cvox.BrailleCaptionsBackground.isEnabled()); return false; + case 'copy': + var textarea = document.createElement('textarea'); + document.body.appendChild(textarea); + textarea.focus(); + document.execCommand('paste'); + var clipboardContent = textarea.value; + textarea.remove(); + cvox.ChromeVox.tts.speak( + Msgs.getMsg('copy', [clipboardContent]), cvox.QueueMode.FLUSH); + this.pageSel_ = null; + return true; + case 'toggleSelection': + if (!this.pageSel_) { + this.pageSel_ = this.currentRange; + } else { + var root = this.currentRange_.start.node.root; + if (root && root.anchorObject && root.focusObject) { + var sel = new cursors.Range( + new cursors.Cursor(root.anchorObject, root.anchorOffset), + new cursors.Cursor(root.focusObject, root.focusOffset) + ); + var o = new Output() + .format('@end_selection') + .withSpeechAndBraille(sel, sel, Output.EventType.NAVIGATE) + .go(); + } + this.pageSel_ = null; + return false; + } + break; default: return true; } @@ -884,12 +914,64 @@ var prevRange = this.currentRange_; this.setCurrentRange(range); - range.select(); + var o = new Output(); + var selectedRange; + if (this.pageSel_ && + this.pageSel_.isValid() && + range.isValid()) { + // Compute the direction of the endpoints of each range. - var o = new Output().withRichSpeechAndBraille( - range, prevRange, Output.EventType.NAVIGATE) + // Casts are ok because isValid checks node start and end nodes are + // non-null; Closure just doesn't eval enough to see it. + var startDir = + AutomationUtil.getDirection(this.pageSel_.start.node, + /** @type {!AutomationNode} */ (range.start.node)); + var endDir = + AutomationUtil.getDirection(this.pageSel_.end.node, + /** @type {!AutomationNode} */ (range.end.node)); + + // Selection across roots isn't supported. + var pageRootStart = this.pageSel_.start.node.root; + var pageRootEnd = this.pageSel_.end.node.root; + var curRootStart = range.start.node.root; + var curRootEnd = range.end.node.root; + + // Disallow crossing over the start of the page selection and roots. + if (startDir == Dir.BACKWARD || + pageRootStart != pageRootEnd || + pageRootStart != curRootStart || + pageRootEnd != curRootEnd) { + o.format('@end_selection'); + this.pageSel_ = null; + } else { + // Expand or shrink requires different feedback. + var msg; + if (endDir == Dir.FORWARD && + (this.pageSel_.end.node != range.end.node || + this.pageSel_.end.index <= range.end.index)) { + msg = '@selected'; + } else { + msg = '@unselected'; + selectedRange = prevRange; + } + this.pageSel_ = new cursors.Range( + this.pageSel_.start, + range.end + ); + if (this.pageSel_) + this.pageSel_.select(); + } + } else { + range.select(); + } + + o.withRichSpeechAndBraille( + selectedRange || range, prevRange, Output.EventType.NAVIGATE) .withQueueMode(cvox.QueueMode.FLUSH); + if (msg) + o.format(msg); + for (var prop in opt_speechProps) o.format('!' + prop);
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs index 847f58a..d995120 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -967,3 +967,28 @@ .replay(); }); }); + +TEST_F('BackgroundTest', 'Selection', function() { + var mockFeedback = this.createMockFeedback(); + this.runWithLoadedTree(function(root) {/*! + <p>simple</p> + <p>doc</p> + */}, function(root) { + // Fakes a toggleSelection command. + root.addEventListener('textSelectionChanged', function() { + if (root.focusOffset == 3) + ChromeVoxState.instance.onGotCommand('toggleSelection'); + }, true); + + mockFeedback.call(doCmd('toggleSelection')) + .expectSpeech('simple', 'selected') + .call(doCmd('nextCharacter')) + .expectSpeech('i', 'selected') + .call(doCmd('previousCharacter')) + .expectSpeech('i', 'unselected') + .call(doCmd('nextCharacter')) + .call(doCmd('nextCharacter')) + .expectSpeech('End selection', 'sim') + .replay(); + }); +});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js index 5b33f7dc..b855f909 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
@@ -130,25 +130,61 @@ return this.index_; }, + /** - * An index appropriate for making selections. + * A node appropriate for making selections. + * @return {AutomationNode} + * @private + */ + get selectionNode_() { + if (!this.node) + return null; + + if (this.node.role == RoleType.inlineTextBox || + this.index == cursors.NODE_INDEX) + return this.node.parent; + + return this.node; + }, + + /** + * An index appropriate for making selections. If this cursor has a + * cursors.NODE_INDEX index, the selection index is a node offset e.g. the + * index in parent. If not, the index is a character offset. * @return {number} * @private */ get selectionIndex_() { - if (this.index_ == cursors.NODE_INDEX) - return 0; - var adjustedIndex = this.index_; - if (this.node.role == RoleType.inlineTextBox) { + if (adjustedIndex == cursors.NODE_INDEX) + adjustedIndex = 0; + var sibling = this.node.previousSibling; while (sibling) { adjustedIndex += sibling.name.length; sibling = sibling.previousSibling; } - } + // Work around Blink's somewhat unexpected offset calculation which + // requires us to consider all previous siblings of the parenting static + // text. + var parent = this.node.parent; + if (parent.role == RoleType.staticText) { + sibling = parent.previousSibling; + while (sibling) { + if (sibling.name) + adjustedIndex += sibling.name.length; + sibling = sibling.previousSibling; + } + } + } else if (this.index_ == cursors.NODE_INDEX) { + if (this.index_ == cursors.NODE_INDEX) { + // Translate the index into a selection on the parent. + if (this.node.parent) + adjustedIndex = this.node.indexInParent; + } + } return adjustedIndex; }, @@ -546,15 +582,15 @@ * Select the text contained within this range. */ select: function() { - var start = this.start.node; - var end = this.end.node; + var startNode = this.start.selectionNode_; + var endNode = this.end.selectionNode_; - if (!start || !end) + if (!startNode || !endNode) return; // Find the most common root. - var uniqueAncestors = AutomationUtil.getUniqueAncestors(start, end); - var mcr = start.root; + var uniqueAncestors = AutomationUtil.getUniqueAncestors(startNode, endNode); + var mcr = startNode.root; if (uniqueAncestors) { var common = uniqueAncestors.pop().parent; if (common) @@ -564,18 +600,19 @@ if (!mcr || mcr.role == RoleType.desktop) return; - if (mcr === start.root && mcr === end.root) { - start = start.role == RoleType.inlineTextBox ? start.parent : start; - end = end.role == RoleType.inlineTextBox ? end.parent : end; + if (mcr === startNode.root && mcr === endNode.root) { + var startIndex = this.start.selectionIndex_; - if (!start || !end) - return; + // We want to adjust to select the entire node for node offsets; + // otherwise, use the plain character offset. + var endIndex = this.end.index == cursors.NODE_INDEX ? + this.end.selectionIndex_ + 1 : this.end.selectionIndex_; chrome.automation.setDocumentSelection( - { anchorObject: start, - anchorOffset: this.start.selectionIndex_, - focusObject: end, - focusOffset: this.end.selectionIndex_ } + { anchorObject: startNode, + anchorOffset: startIndex, + focusObject: endNode, + focusOffset: endIndex } ); } },
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs index b2d30fc..cd09156 100644 --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -369,9 +369,23 @@ assertEquals('diff ', secondLine.name); var secondLineCursor = new cursors.Cursor(secondLine, -1); - assertEquals(0, secondLineCursor.selectionIndex_); - secondLineCursor = new cursors.Cursor(secondLine, 0); - secondLineCursor.index = 0; + // The selected node moves to the static text node. + assertEquals(secondLineCursor.node.parent, + secondLineCursor.selectionNode_); + // This selects the entire node via a character offset. assertEquals(6, secondLineCursor.selectionIndex_); + + // Index into the characters. + secondLineCursor = new cursors.Cursor(secondLine, 1); + assertEquals(7, secondLineCursor.selectionIndex_); + + // Now, try selecting via node offsets. + var cursor = new cursors.Cursor(root.firstChild, -1); + assertEquals(root, cursor.selectionNode_); + assertEquals(0, cursor.selectionIndex_); + + cursor = new cursors.Cursor(root.firstChild.nextSibling, -1); + assertEquals(root, cursor.selectionNode_); + assertEquals(1, cursor.selectionIndex_); }); });
diff --git a/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2 b/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2 index c2cd99c..9a3f5f3 100644 --- a/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2 +++ b/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2
@@ -16,6 +16,7 @@ "accessibilityPrivate", "bookmarks", "brailleDisplayPrivate", + "clipboardRead", "commands.accessibility", "commandLinePrivate", "experimental",
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd index a2e714a..8cd5d2958 100644 --- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd +++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -1845,13 +1845,13 @@ Start or end selection. </message> <message desc="Spoken when the browser's copy command is invoked." name="IDS_CHROMEVOX_COPY"> - copy. + copy $1. </message> <message desc="Spoken when the browser's cut command is invoked." name="IDS_CHROMEVOX_CUT"> - cut. + cut $1. </message> <message desc="Spoken when the browser's paste command is invoked." name="IDS_CHROMEVOX_PASTE"> - paste. + paste $1. </message> <message desc="Spoken when additional characters are selected in editable text." name="IDS_CHROMEVOX_SELECTED"> selected
diff --git a/chrome/browser/resources/net_internals/quic_view.html b/chrome/browser/resources/net_internals/quic_view.html index 9ee0590..e5cf4bd 100644 --- a/chrome/browser/resources/net_internals/quic_view.html +++ b/chrome/browser/resources/net_internals/quic_view.html
@@ -15,6 +15,7 @@ <li>Idle Connection Timeout In Seconds: <span jscontent="$this.idle_connection_timeout_seconds"></span></li> <li>Disable PreConnect If 0RTT: <span jscontent="$this.disable_preconnect_if_0rtt"></span></li> <li>Disable QUIC On Timeout With Open Streams: <span jscontent="$this.disable_quic_on_timeout_with_open_streams"></span></li> + <li>Race Cert Verification: <span jscontent="!!race_cert_verification"></span></li> <li jsdisplay="$this.disabled_reason && disabled_reason.length > 0">QUIC dynamically disabled: <span jscontent="disabled_reason"></span></li> </ul>
diff --git a/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js b/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js index cc68ec1..7fd7faef 100644 --- a/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js +++ b/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js
@@ -5,8 +5,8 @@ function createTestFunction(expected_message) { return function(tab) { function onDebuggerEvent(debuggee, method, params) { - if (debuggee.tabId == tab.id && method == 'Console.messageAdded') { - if (params.message.text.indexOf(expected_message) > -1) { + if (debuggee.tabId == tab.id && method == 'Runtime.exceptionThrown') { + if (params.details.text.indexOf(expected_message) > -1) { chrome.debugger.onEvent.removeListener(onDebuggerEvent); chrome.test.succeed(); } @@ -16,7 +16,7 @@ chrome.debugger.attach({ tabId: tab.id }, "1.1", function() { // Enabling console provides both stored and new messages via the // Console.messageAdded event. - chrome.debugger.sendCommand({ tabId: tab.id }, "Console.enable"); + chrome.debugger.sendCommand({ tabId: tab.id }, "Runtime.enable"); }); } }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java index ec7d461..a346282c 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
@@ -51,7 +51,8 @@ .put("idle_connection_timeout_seconds", 300) .put("close_sessions_on_ip_change", false) .put("migrate_sessions_on_network_change", true) - .put("migrate_sessions_early", true); + .put("migrate_sessions_early", true) + .put("race_cert_verification", true); JSONObject experimentalOptions = new JSONObject().put("QUIC", quicParams); mBuilder.setExperimentalOptions(experimentalOptions.toString()); mBuilder.setMockCertVerifierForTesting(QuicTestServer.createMockCertVerifier());
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc index fa080d2d..61dc9d4 100644 --- a/components/cronet/url_request_context_config.cc +++ b/components/cronet/url_request_context_config.cc
@@ -53,6 +53,7 @@ const char kQuicMigrateSessionsEarly[] = "migrate_sessions_early"; const char kQuicDisableBidirectionalStreams[] = "quic_disable_bidirectional_streams"; +const char kQuicRaceCertVerification[] = "race_cert_verification"; // AsyncDNS experiment dictionary name. const char kAsyncDnsFieldTrialName[] = "AsyncDNS"; @@ -187,6 +188,13 @@ context_builder->set_quic_disable_bidirectional_streams( quic_disable_bidirectional_streams); } + + bool quic_race_cert_verification = false; + if (quic_args->GetBoolean(kQuicRaceCertVerification, + &quic_race_cert_verification)) { + context_builder->set_quic_race_cert_verification( + quic_race_cert_verification); + } } const base::DictionaryValue* async_dns_args = nullptr;
diff --git a/components/cronet/url_request_context_config_unittest.cc b/components/cronet/url_request_context_config_unittest.cc index a0cef2a..d06522291 100644 --- a/components/cronet/url_request_context_config_unittest.cc +++ b/components/cronet/url_request_context_config_unittest.cc
@@ -46,6 +46,7 @@ "\"packet_loss_threshold\":0.5," "\"idle_connection_timeout_seconds\":300," "\"close_sessions_on_ip_change\":true," + "\"race_cert_verification\":true," "\"connection_options\":\"TIME,TBBR,REJ\"}," "\"AsyncDNS\":{\"enable\":true}}", // Data reduction proxy key. @@ -103,6 +104,9 @@ EXPECT_TRUE(params->quic_close_sessions_on_ip_change); EXPECT_FALSE(params->quic_migrate_sessions_on_network_change); + // Check race_cert_verification. + EXPECT_TRUE(params->quic_race_cert_verification); + // Check AsyncDNS resolver is enabled. EXPECT_TRUE(context->host_resolver()->GetDnsConfigAsValue()); }
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc index d9ab400..5f5d0c8 100644 --- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc +++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -21,9 +21,12 @@ namespace { +// Gathers data from the NavigationHandle assigned to navigations that start +// with the expected URL. class NavigationHandleObserver : public WebContentsObserver { public: - NavigationHandleObserver(WebContents* web_contents, const GURL& expected_url) + NavigationHandleObserver(WebContents* web_contents, + const GURL& expected_start_url) : WebContentsObserver(web_contents), handle_(nullptr), has_committed_(false), @@ -36,10 +39,10 @@ was_redirected_(false), frame_tree_node_id_(-1), page_transition_(ui::PAGE_TRANSITION_LINK), - expected_url_(expected_url) {} + expected_start_url_(expected_start_url) {} void DidStartNavigation(NavigationHandle* navigation_handle) override { - if (handle_ || navigation_handle->GetURL() != expected_url_) + if (handle_ || navigation_handle->GetURL() != expected_start_url_) return; handle_ = navigation_handle; @@ -115,12 +118,13 @@ bool was_redirected_; int frame_tree_node_id_; ui::PageTransition page_transition_; - GURL expected_url_; + GURL expected_start_url_; GURL last_committed_url_; }; // A test NavigationThrottle that will return pre-determined checks and run -// callbacks when the various NavigationThrottle methods are called. +// callbacks when the various NavigationThrottle methods are called. It is +// not instantiated directly but through a TestNavigationThrottleInstaller. class TestNavigationThrottle : public NavigationThrottle { public: TestNavigationThrottle( @@ -169,8 +173,10 @@ base::Closure did_call_will_process_; }; -// Install a TestNavigationThrottle on all requests and allows waiting for -// various NavigationThrottle related events. +// Install a TestNavigationThrottle on all following requests and allows waiting +// for various NavigationThrottle related events. Waiting works only for the +// immediately next navigation. New instances are needed to wait for further +// navigations. class TestNavigationThrottleInstaller : public WebContentsObserver { public: TestNavigationThrottleInstaller( @@ -270,6 +276,22 @@ scoped_refptr<MessageLoopRunner> will_process_loop_runner_; }; +// Records all navigation start URLs from the WebContents. +class NavigationStartUrlRecorder : public WebContentsObserver { + public: + NavigationStartUrlRecorder(WebContents* web_contents) + : WebContentsObserver(web_contents) {} + + void DidStartNavigation(NavigationHandle* navigation_handle) override { + urls_.push_back(navigation_handle->GetURL()); + } + + const std::vector<GURL>& urls() const { return urls_; } + + private: + std::vector<GURL> urls_; +}; + } // namespace class NavigationHandleImplBrowserTest : public ContentBrowserTest { @@ -632,4 +654,67 @@ GURL(embedded_test_server()->GetURL("bar.com", "/title2.html"))); } +// Specialized test that verifies the NavigationHandle gets the HTTPS upgraded +// URL from the very beginning of the navigation. +class NavigationHandleImplHttpsUpgradeBrowserTest + : public NavigationHandleImplBrowserTest { + public: + void CheckHttpsUpgradedIframeNavigation(const GURL& start_url, + const GURL& iframe_secure_url) { + ASSERT_TRUE(start_url.SchemeIs(url::kHttpScheme)); + ASSERT_TRUE(iframe_secure_url.SchemeIs(url::kHttpsScheme)); + + NavigationStartUrlRecorder url_recorder(shell()->web_contents()); + TestNavigationThrottleInstaller installer( + shell()->web_contents(), NavigationThrottle::PROCEED, + NavigationThrottle::PROCEED, NavigationThrottle::PROCEED); + TestNavigationManager navigation_manager(shell()->web_contents(), + iframe_secure_url); + + // Load the page and wait for the frame load with the expected URL. + // Note: if the test times out while waiting then a navigation to + // iframe_secure_url never happened and the expected upgrade may not be + // working. + shell()->LoadURL(start_url); + navigation_manager.WaitForWillStartRequest(); + + // The main frame should have finished navigating while the iframe should + // have just started. + EXPECT_EQ(2, installer.will_start_called()); + EXPECT_EQ(0, installer.will_redirect_called()); + EXPECT_EQ(1, installer.will_process_called()); + + // Check the correct start URLs have been registered. + EXPECT_EQ(iframe_secure_url, url_recorder.urls().back()); + EXPECT_EQ(start_url, url_recorder.urls().front()); + EXPECT_EQ(2ul, url_recorder.urls().size()); + } +}; + +// Tests that the start URL is HTTPS upgraded for a same site navigation. +IN_PROC_BROWSER_TEST_F(NavigationHandleImplHttpsUpgradeBrowserTest, + StartUrlIsHttpsUpgradedSameSite) { + GURL start_url( + embedded_test_server()->GetURL("/https_upgrade_same_site.html")); + + // Builds the expected upgraded same site URL. + GURL::Replacements replace_scheme; + replace_scheme.SetSchemeStr("https"); + GURL cross_site_iframe_secure_url = embedded_test_server() + ->GetURL("/title1.html") + .ReplaceComponents(replace_scheme); + + CheckHttpsUpgradedIframeNavigation(start_url, cross_site_iframe_secure_url); +} + +// Tests that the start URL is HTTPS upgraded for a cross site navigation. +IN_PROC_BROWSER_TEST_F(NavigationHandleImplHttpsUpgradeBrowserTest, + StartUrlIsHttpsUpgradedCrossSite) { + GURL start_url( + embedded_test_server()->GetURL("/https_upgrade_cross_site.html")); + GURL cross_site_iframe_secure_url("https://other.com/title1.html"); + + CheckHttpsUpgradedIframeNavigation(start_url, cross_site_iframe_secure_url); +} + } // namespace content
diff --git a/content/test/data/https_upgrade_cross_site.html b/content/test/data/https_upgrade_cross_site.html new file mode 100644 index 0000000..4591ec1 --- /dev/null +++ b/content/test/data/https_upgrade_cross_site.html
@@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<title>Cross Site HTTPS upgrade</title> + +<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> + +<body> + <iframe src="http://other.com/title1.html"></iframe> +</body> +</html>
diff --git a/content/test/data/https_upgrade_same_site.html b/content/test/data/https_upgrade_same_site.html new file mode 100644 index 0000000..bf1dd6e --- /dev/null +++ b/content/test/data/https_upgrade_same_site.html
@@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<title>Same Site HTTPS upgrade</title> + +<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> + +<body> + <iframe src="/title1.html"></iframe> +</body> +</html>
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc index 06651e96..532f5b1 100644 --- a/net/http/http_network_session.cc +++ b/net/http/http_network_session.cc
@@ -129,6 +129,7 @@ quic_migrate_sessions_early(false), quic_disable_bidirectional_streams(false), quic_force_hol_blocking(false), + quic_race_cert_verification(false), proxy_delegate(NULL), enable_token_binding(false) { quic_supported_versions.push_back(QUIC_VERSION_34); @@ -188,6 +189,7 @@ params.quic_migrate_sessions_on_network_change, params.quic_migrate_sessions_early, params.quic_force_hol_blocking, + params.quic_race_cert_verification, params.quic_connection_options, params.enable_token_binding), spdy_session_pool_(params.host_resolver, @@ -340,6 +342,8 @@ dict->SetString("disabled_reason", quic_stream_factory_.QuicDisabledReasonString()); dict->SetBoolean("force_hol_blocking", params_.quic_force_hol_blocking); + dict->SetBoolean("race_cert_verification", + params_.quic_race_cert_verification); return std::move(dict); }
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index b4a72ea..2a40a0c 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h
@@ -180,6 +180,8 @@ bool quic_disable_bidirectional_streams; // If true, enable force HOL blocking. For measurement purposes. bool quic_force_hol_blocking; + // If true, race cert verification with host resolution. + bool quic_race_cert_verification; ProxyDelegate* proxy_delegate; // Enable support for Token Binding.
diff --git a/net/quic/crypto/proof_verifier.h b/net/quic/crypto/proof_verifier.h index d05e1dd..c551e40 100644 --- a/net/quic/crypto/proof_verifier.h +++ b/net/quic/crypto/proof_verifier.h
@@ -86,6 +86,26 @@ std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details, std::unique_ptr<ProofVerifierCallback> callback) = 0; + + // VerifyCertChain checks that |certs| is a valid chain for |hostname|. On + // success, it returns QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and + // sets |*error_details| to a description of the problem. In either case it + // may set |*details|, which the caller takes ownership of. + // + // |context| specifies an implementation specific struct (which may be nullptr + // for some implementations) that provides useful information for the + // verifier, e.g. logging handles. + // + // This function may also return QUIC_PENDING, in which case the ProofVerifier + // will call back, on the original thread, via |callback| when complete. + // In this case, the ProofVerifier will take ownership of |callback|. + virtual QuicAsyncStatus VerifyCertChain( + const std::string& hostname, + const std::vector<std::string>& certs, + const ProofVerifyContext* context, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* details, + std::unique_ptr<ProofVerifierCallback> callback) = 0; }; } // namespace net
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc index 621eb1f..61eea5a 100644 --- a/net/quic/crypto/proof_verifier_chromium.cc +++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -78,6 +78,16 @@ std::unique_ptr<ProofVerifyDetails>* verify_details, std::unique_ptr<ProofVerifierCallback> callback); + // Starts the certificate chain verification of |certs|. If |QUIC_PENDING| is + // returned, then |callback| will be invoked asynchronously when the + // verification completes. + QuicAsyncStatus VerifyCertChain( + const std::string& hostname, + const std::vector<std::string>& certs, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback); + private: enum State { STATE_NONE, @@ -85,6 +95,19 @@ STATE_VERIFY_CERT_COMPLETE, }; + // Convert |certs| to |cert_|(X509Certificate). Returns true if successful. + bool GetX509Certificate(const vector<string>& certs, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details); + + // Start the cert verification. + QuicAsyncStatus VerifyCert( + const string& hostname, + const uint16_t port, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback); + int DoLoop(int last_io_result); void OnIOComplete(int result); int DoVerifyCert(int result); @@ -125,6 +148,9 @@ // passed to CertVerifier::Verify. int cert_verify_flags_; + // If set to true, enforces policy checking in DoVerifyCertComplete(). + bool enforce_policy_checking_; + State next_state_; base::TimeTicks start_time_; @@ -148,6 +174,7 @@ transport_security_state_(transport_security_state), cert_transparency_verifier_(cert_transparency_verifier), cert_verify_flags_(cert_verify_flags), + enforce_policy_checking_(true), next_state_(STATE_NONE), start_time_(base::TimeTicks::Now()), net_log_(net_log) { @@ -195,27 +222,9 @@ verify_details_.reset(new ProofVerifyDetailsChromium); - if (certs.empty()) { - *error_details = "Failed to create certificate chain. Certs are empty."; - DLOG(WARNING) << *error_details; - verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; - *verify_details = std::move(verify_details_); + // Converts |certs| to |cert_|. + if (!GetX509Certificate(certs, error_details, verify_details)) return QUIC_FAILURE; - } - - // Convert certs to X509Certificate. - vector<StringPiece> cert_pieces(certs.size()); - for (unsigned i = 0; i < certs.size(); i++) { - cert_pieces[i] = base::StringPiece(certs[i]); - } - cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces); - if (!cert_.get()) { - *error_details = "Failed to create certificate chain"; - DLOG(WARNING) << *error_details; - verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; - *verify_details = std::move(verify_details_); - return QUIC_FAILURE; - } if (!cert_sct.empty()) { // Note that this is a completely synchronous operation: The CT Log Verifier @@ -237,6 +246,75 @@ return QUIC_FAILURE; } + DCHECK(enforce_policy_checking_); + return VerifyCert(hostname, port, error_details, verify_details, + std::move(callback)); +} + +QuicAsyncStatus ProofVerifierChromium::Job::VerifyCertChain( + const string& hostname, + const vector<string>& certs, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) { + DCHECK(error_details); + DCHECK(verify_details); + DCHECK(callback); + + error_details->clear(); + + if (STATE_NONE != next_state_) { + *error_details = "Certificate is already set and VerifyCertChain has begun"; + DLOG(DFATAL) << *error_details; + return QUIC_FAILURE; + } + + verify_details_.reset(new ProofVerifyDetailsChromium); + + // Converts |certs| to |cert_|. + if (!GetX509Certificate(certs, error_details, verify_details)) + return QUIC_FAILURE; + + enforce_policy_checking_ = false; + // |port| is not needed because |enforce_policy_checking_| is false. + return VerifyCert(hostname, /*port=*/0, error_details, verify_details, + std::move(callback)); +} + +bool ProofVerifierChromium::Job::GetX509Certificate( + const vector<string>& certs, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details) { + if (certs.empty()) { + *error_details = "Failed to create certificate chain. Certs are empty."; + DLOG(WARNING) << *error_details; + verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; + *verify_details = std::move(verify_details_); + return false; + } + + // Convert certs to X509Certificate. + vector<StringPiece> cert_pieces(certs.size()); + for (unsigned i = 0; i < certs.size(); i++) { + cert_pieces[i] = base::StringPiece(certs[i]); + } + cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces); + if (!cert_.get()) { + *error_details = "Failed to create certificate chain"; + DLOG(WARNING) << *error_details; + verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; + *verify_details = std::move(verify_details_); + return false; + } + return true; +} + +QuicAsyncStatus ProofVerifierChromium::Job::VerifyCert( + const string& hostname, + const uint16_t port, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) { hostname_ = hostname; port_ = port; @@ -315,7 +393,8 @@ // If the connection was good, check HPKP and CT status simultaneously, // but prefer to treat the HPKP error as more serious, if there was one. - if ((result == OK || + if (enforce_policy_checking_ && + (result == OK || (IsCertificateError(result) && IsCertStatusMinorError(cert_status)))) { if ((cert_verify_result.cert_status & CERT_STATUS_IS_EV)) { ct::EVPolicyCompliance ev_policy_compliance = @@ -505,9 +584,32 @@ QuicAsyncStatus status = job->VerifyProof( hostname, port, server_config, quic_version, chlo_hash, certs, cert_sct, signature, error_details, verify_details, std::move(callback)); - if (status == QUIC_PENDING) { + if (status == QUIC_PENDING) active_jobs_.insert(job.release()); + return status; +} + +QuicAsyncStatus ProofVerifierChromium::VerifyCertChain( + const std::string& hostname, + const std::vector<std::string>& certs, + const ProofVerifyContext* verify_context, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) { + if (!verify_context) { + *error_details = "Missing context"; + return QUIC_FAILURE; } + const ProofVerifyContextChromium* chromium_context = + reinterpret_cast<const ProofVerifyContextChromium*>(verify_context); + std::unique_ptr<Job> job( + new Job(this, cert_verifier_, ct_policy_enforcer_, + transport_security_state_, cert_transparency_verifier_, + chromium_context->cert_verify_flags, chromium_context->net_log)); + QuicAsyncStatus status = job->VerifyCertChain( + hostname, certs, error_details, verify_details, std::move(callback)); + if (status == QUIC_PENDING) + active_jobs_.insert(job.release()); return status; }
diff --git a/net/quic/crypto/proof_verifier_chromium.h b/net/quic/crypto/proof_verifier_chromium.h index b15751c..ee9ddab 100644 --- a/net/quic/crypto/proof_verifier_chromium.h +++ b/net/quic/crypto/proof_verifier_chromium.h
@@ -85,6 +85,13 @@ std::string* error_details, std::unique_ptr<ProofVerifyDetails>* verify_details, std::unique_ptr<ProofVerifierCallback> callback) override; + QuicAsyncStatus VerifyCertChain( + const std::string& hostname, + const std::vector<std::string>& certs, + const ProofVerifyContext* verify_context, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) override; private: class Job;
diff --git a/net/quic/crypto/proof_verifier_chromium_test.cc b/net/quic/crypto/proof_verifier_chromium_test.cc index fff1bf6d..47d2637 100644 --- a/net/quic/crypto/proof_verifier_chromium_test.cc +++ b/net/quic/crypto/proof_verifier_chromium_test.cc
@@ -587,5 +587,34 @@ CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED); } +// Tests that the VerifyCertChain verifies certificates. +TEST_F(ProofVerifierChromiumTest, VerifyCertChain) { + scoped_refptr<X509Certificate> test_cert = GetTestServerCertificate(); + ASSERT_TRUE(test_cert); + + CertVerifyResult dummy_result; + dummy_result.verified_cert = test_cert; + dummy_result.cert_status = 0; + + MockCertVerifier dummy_verifier; + dummy_verifier.AddResultForCert(test_cert.get(), dummy_result, OK); + + ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, + &transport_security_state_, + ct_verifier_.get()); + + std::unique_ptr<DummyProofVerifierCallback> callback( + new DummyProofVerifierCallback); + QuicAsyncStatus status = proof_verifier.VerifyCertChain( + kTestHostname, certs_, verify_context_.get(), &error_details_, &details_, + std::move(callback)); + ASSERT_EQ(QUIC_SUCCESS, status); + + ASSERT_TRUE(details_.get()); + ProofVerifyDetailsChromium* verify_details = + static_cast<ProofVerifyDetailsChromium*>(details_.get()); + EXPECT_EQ(0u, verify_details->cert_verify_result.cert_status); +} + } // namespace test } // namespace net
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index 91e442d..e3aa922 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc
@@ -34,6 +34,7 @@ #include "net/http/bidirectional_stream_impl.h" #include "net/quic/bidirectional_stream_quic_impl.h" #include "net/quic/crypto/channel_id_chromium.h" +#include "net/quic/crypto/proof_verifier.h" #include "net/quic/crypto/proof_verifier_chromium.h" #include "net/quic/crypto/properties_based_quic_server_info.h" #include "net/quic/crypto/quic_random.h" @@ -167,6 +168,88 @@ } // namespace +// Responsible for verifying the certificates saved in +// QuicCryptoClientConfig, and for notifying any associated requests when +// complete. Results from cert verification are ignored. +class QuicStreamFactory::CertVerifierJob { + public: + // ProofVerifierCallbackImpl is passed as the callback method to + // VerifyCertChain. The ProofVerifier calls this class with the result of cert + // verification when verification is performed asynchronously. + class ProofVerifierCallbackImpl : public ProofVerifierCallback { + public: + explicit ProofVerifierCallbackImpl(CertVerifierJob* job) : job_(job) {} + + ~ProofVerifierCallbackImpl() override {} + + void Run(bool ok, + const std::string& error_details, + std::unique_ptr<ProofVerifyDetails>* details) override { + if (job_ == nullptr) + return; + job_->verify_callback_ = nullptr; + job_->OnComplete(); + } + + void Cancel() { job_ = nullptr; } + + private: + CertVerifierJob* job_; + }; + + CertVerifierJob(const QuicServerId& server_id, + int cert_verify_flags, + const BoundNetLog& net_log) + : server_id_(server_id), + verify_callback_(nullptr), + verify_context_(base::WrapUnique( + new ProofVerifyContextChromium(cert_verify_flags, net_log))), + net_log_(net_log), + weak_factory_(this) {} + + ~CertVerifierJob() { + if (verify_callback_) + verify_callback_->Cancel(); + } + + // Starts verification of certs cached in the |crypto_config|. + QuicAsyncStatus Run(QuicCryptoClientConfig* crypto_config, + const CompletionCallback& callback) { + QuicCryptoClientConfig::CachedState* cached = + crypto_config->LookupOrCreate(server_id_); + ProofVerifierCallbackImpl* verify_callback = + new ProofVerifierCallbackImpl(this); + QuicAsyncStatus status = crypto_config->proof_verifier()->VerifyCertChain( + server_id_.host(), cached->certs(), verify_context_.get(), + &verify_error_details_, &verify_details_, + std::unique_ptr<ProofVerifierCallback>(verify_callback)); + if (status == QUIC_PENDING) { + verify_callback_ = verify_callback; + callback_ = callback; + } + return status; + } + + void OnComplete() { + if (!callback_.is_null()) + callback_.Run(OK); + } + + const QuicServerId& server_id() const { return server_id_; } + + private: + QuicServerId server_id_; + ProofVerifierCallbackImpl* verify_callback_; + std::unique_ptr<ProofVerifyContext> verify_context_; + std::unique_ptr<ProofVerifyDetails> verify_details_; + std::string verify_error_details_; + const BoundNetLog net_log_; + CompletionCallback callback_; + base::WeakPtrFactory<CertVerifierJob> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(CertVerifierJob); +}; + // Responsible for creating a new QUIC session to the specified server, and // for notifying any associated requests when complete. class QuicStreamFactory::Job { @@ -364,9 +447,8 @@ int QuicStreamFactory::Job::DoResolveHost() { // Start loading the data now, and wait for it after we resolve the host. - if (server_info_) { + if (server_info_) server_info_->Start(); - } io_state_ = STATE_RESOLVE_HOST_COMPLETE; dns_resolution_start_time_ = base::TimeTicks::Now(); @@ -388,9 +470,8 @@ // Inform the factory of this resolution, which will set up // a session alias, if possible. - if (factory_->OnResolution(key_, address_list_)) { + if (factory_->OnResolution(key_, address_list_)) return OK; - } if (server_info_) io_state_ = STATE_LOAD_SERVER_INFO; @@ -469,14 +550,12 @@ return rv; } - if (!session_->connection()->connected()) { + if (!session_->connection()->connected()) return ERR_CONNECTION_CLOSED; - } session_->StartReading(); - if (!session_->connection()->connected()) { + if (!session_->connection()->connected()) return ERR_QUIC_PROTOCOL_ERROR; - } bool require_confirmation = factory_->require_confirmation() || was_alternative_service_recently_broken_; @@ -485,8 +564,9 @@ base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr())); if (!session_->connection()->connected() && - session_->error() == QUIC_PROOF_INVALID) + session_->error() == QUIC_PROOF_INVALID) { return ERR_QUIC_HANDSHAKE_FAILED; + } return rv; } @@ -503,9 +583,8 @@ int QuicStreamFactory::Job::DoConnectComplete(int rv) { if (session_ && session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) { num_sent_client_hellos_ += session_->GetNumSentClientHellos(); - if (num_sent_client_hellos_ >= QuicCryptoClientStream::kMaxClientHellos) { + if (num_sent_client_hellos_ >= QuicCryptoClientStream::kMaxClientHellos) return ERR_QUIC_HANDSHAKE_FAILED; - } // The handshake was rejected statelessly, so create another connection // to resume the handshake. io_state_ = STATE_CONNECT; @@ -633,6 +712,7 @@ bool migrate_sessions_on_network_change, bool migrate_sessions_early, bool force_hol_blocking, + bool race_cert_verification, const QuicTagVector& connection_options, bool enable_token_binding) : require_confirmation_(true), @@ -687,6 +767,7 @@ migrate_sessions_early_(migrate_sessions_early && migrate_sessions_on_network_change_), force_hol_blocking_(force_hol_blocking), + race_cert_verification_(race_cert_verification), port_seed_(random_generator_->RandUint64()), check_persisted_supports_quic_(true), has_initialized_data_(false), @@ -754,6 +835,8 @@ STLDeleteElements(&(active_jobs_[server_id])); active_jobs_.erase(server_id); } + while (!active_cert_verifier_jobs_.empty()) + active_cert_verifier_jobs_.erase(active_cert_verifier_jobs_.begin()); if (ssl_config_service_.get()) ssl_config_service_->RemoveObserver(this); if (migrate_sessions_on_network_change_) { @@ -885,11 +968,12 @@ // don't wait for Cache thread to load the data for that server. load_from_disk_cache = false; } - if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) { + if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) quic_server_info = quic_server_info_factory_->GetForServer(server_id); - } } + ignore_result(StartCertVerifyJob(server_id, cert_verify_flags, net_log)); + QuicSessionKey key(destination, server_id); std::unique_ptr<Job> job( new Job(this, host_resolver_, key, WasQuicRecentlyBroken(server_id), @@ -950,9 +1034,8 @@ const AddressList& address_list) { const QuicServerId& server_id(key.server_id()); DCHECK(!HasActiveSession(server_id)); - if (disable_connection_pooling_) { + if (disable_connection_pooling_) return false; - } for (const IPEndPoint& address : address_list) { if (!ContainsKey(ip_aliases_, address)) continue; @@ -1022,6 +1105,10 @@ job_requests_map_.erase(server_id); } +void QuicStreamFactory::OnCertVerifyJobComplete(CertVerifierJob* job, int rv) { + active_cert_verifier_jobs_.erase(job->server_id()); +} + std::unique_ptr<QuicHttpStream> QuicStreamFactory::CreateFromSession( QuicChromiumClientSession* session) { return std::unique_ptr<QuicHttpStream>( @@ -1137,9 +1224,8 @@ DCHECK_EQ(session, active_sessions_[server_id]); // Track sessions which have recently gone away so that we can disable // port suggestions. - if (session->goaway_received()) { + if (session->goaway_received()) gone_away_aliases_.insert(*it); - } active_sessions_.erase(server_id); ProcessGoingAwaySession(session, server_id, true); @@ -1148,9 +1234,8 @@ if (!aliases.empty()) { const IPEndPoint peer_address = session->connection()->peer_address(); ip_aliases_[peer_address].erase(session); - if (ip_aliases_[peer_address].empty()) { + if (ip_aliases_[peer_address].empty()) ip_aliases_.erase(peer_address); - } } session_aliases_.erase(session); } @@ -1255,9 +1340,8 @@ QuicChromiumClientSession* session) { const AliasSet& aliases = session_aliases_[session]; - if (aliases.empty()) { + if (aliases.empty()) return; - } for (const QuicSessionKey& key : aliases) { const QuicServerId& server_id = key.server_id(); @@ -1269,9 +1353,8 @@ const IPEndPoint peer_address = session->connection()->peer_address(); ip_aliases_[peer_address].erase(session); - if (ip_aliases_[peer_address].empty()) { + if (ip_aliases_[peer_address].empty()) ip_aliases_.erase(peer_address); - } QuicSessionKey key = *aliases.begin(); session_aliases_.erase(session); Job* job = new Job(this, host_resolver_, session, key); @@ -1367,9 +1450,8 @@ NetworkChangeNotifier::NetworkList network_list; NetworkChangeNotifier::GetConnectedNetworks(&network_list); for (NetworkHandle new_network : network_list) { - if (new_network != old_network) { + if (new_network != old_network) return new_network; - } } return NetworkChangeNotifier::kInvalidNetworkHandle; } @@ -1557,6 +1639,11 @@ return ContainsKey(active_jobs_, server_id); } +bool QuicStreamFactory::HasActiveCertVerifierJob( + const QuicServerId& server_id) const { + return ContainsKey(active_cert_verifier_jobs_, server_id); +} + int QuicStreamFactory::ConfigureSocket(DatagramClientSocket* socket, IPEndPoint addr, NetworkHandle network) { @@ -1645,15 +1732,13 @@ // Passing in kInvalidNetworkHandle binds socket to default network. int rv = ConfigureSocket(socket.get(), addr, NetworkChangeNotifier::kInvalidNetworkHandle); - if (rv != OK) { + if (rv != OK) return rv; - } - if (enable_port_selection) { + if (enable_port_selection) DCHECK_LE(1u, port_suggester->call_count()); - } else { + else DCHECK_EQ(0u, port_suggester->call_count()); - } if (!helper_.get()) { helper_.reset( @@ -1767,6 +1852,29 @@ return cached->IsEmpty(); } +QuicAsyncStatus QuicStreamFactory::StartCertVerifyJob( + const QuicServerId& server_id, + int cert_verify_flags, + const BoundNetLog& net_log) { + if (!race_cert_verification_) + return QUIC_FAILURE; + QuicCryptoClientConfig::CachedState* cached = + crypto_config_.LookupOrCreate(server_id); + if (!cached || cached->certs().empty() || + HasActiveCertVerifierJob(server_id)) { + return QUIC_FAILURE; + } + std::unique_ptr<CertVerifierJob> cert_verifier_job( + new CertVerifierJob(server_id, cert_verify_flags, net_log)); + QuicAsyncStatus status = cert_verifier_job->Run( + &crypto_config_, + base::Bind(&QuicStreamFactory::OnCertVerifyJobComplete, + base::Unretained(this), cert_verifier_job.get())); + if (status == QUIC_PENDING) + active_cert_verifier_jobs_[server_id] = std::move(cert_verifier_job); + return status; +} + void QuicStreamFactory::InitializeCachedStateInCryptoConfig( const QuicServerId& server_id, const std::unique_ptr<QuicServerInfo>& server_info,
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h index fbb91ab..7fcb960 100644 --- a/net/quic/quic_stream_factory.h +++ b/net/quic/quic_stream_factory.h
@@ -193,6 +193,7 @@ bool migrate_sessions_on_network_change, bool migrate_sessions_early, bool force_hol_blocking, + bool race_cert_verification, const QuicTagVector& connection_options, bool enable_token_binding); ~QuicStreamFactory() override; @@ -365,6 +366,7 @@ private: class Job; + class CertVerifierJob; friend class test::QuicStreamFactoryPeer; FRIEND_TEST_ALL_PREFIXES(HttpStreamFactoryTest, QuicLossyProxyMarkedAsBad); @@ -382,6 +384,8 @@ typedef std::map<QuicServerId, RequestSet> ServerIDRequestsMap; typedef std::deque<enum QuicChromiumClientSession::QuicDisabledReason> DisabledReasonsQueue; + typedef std::map<QuicServerId, std::unique_ptr<CertVerifierJob>> + CertVerifierJobMap; enum FactoryStatus { OPEN, // New streams may be created. @@ -401,8 +405,10 @@ bool OnResolution(const QuicSessionKey& key, const AddressList& address_list); void OnJobComplete(Job* job, int rv); + void OnCertVerifyJobComplete(CertVerifierJob* job, int rv); bool HasActiveSession(const QuicServerId& server_id) const; bool HasActiveJob(const QuicServerId& server_id) const; + bool HasActiveCertVerifierJob(const QuicServerId& server_id) const; int CreateSession(const QuicSessionKey& key, int cert_verify_flags, std::unique_ptr<QuicServerInfo> quic_server_info, @@ -424,6 +430,13 @@ bool CryptoConfigCacheIsEmpty(const QuicServerId& server_id); + // Starts an asynchronous job for cert verification if + // |race_cert_verification_| is enabled and if there are cached certs for the + // given |server_id|. + QuicAsyncStatus StartCertVerifyJob(const QuicServerId& server_id, + int cert_verify_flags, + const BoundNetLog& net_log); + // Initializes the cached state associated with |server_id| in // |crypto_config_| with the information in |server_info|. Populates // |connection_id| with the next server designated connection id, @@ -491,6 +504,8 @@ ServerIDRequestsMap job_requests_map_; RequestMap active_requests_; + CertVerifierJobMap active_cert_verifier_jobs_; + QuicVersionVector supported_versions_; // Determine if we should consistently select a client UDP port. If false, @@ -576,6 +591,9 @@ // If set, force HOL blocking. For measurement purposes. const bool force_hol_blocking_; + // Set if cert verification is to be raced with host resolution. + bool race_cert_verification_; + // Each profile will (probably) have a unique port_seed_ value. This value // is used to help seed a pseudo-random number generator (PortSuggester) so // that we consistently (within this profile) suggest the same ephemeral
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index cb159de8..a536b22 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc
@@ -321,16 +321,16 @@ idle_connection_timeout_seconds_(kIdleConnectionTimeoutSeconds), migrate_sessions_on_network_change_(false), migrate_sessions_early_(false), - force_hol_blocking_(false) { + force_hol_blocking_(false), + race_cert_verification_(false) { clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); } ~QuicStreamFactoryTestBase() { // If |factory_| was initialized, then it took over ownership of |clock_|. // If |factory_| was not initialized, then |clock_| needs to be destroyed. - if (!factory_) { + if (!factory_) delete clock_; - } } void Initialize() { @@ -353,8 +353,8 @@ close_sessions_on_ip_change_, disable_quic_on_timeout_with_open_streams_, idle_connection_timeout_seconds_, migrate_sessions_on_network_change_, - migrate_sessions_early_, force_hol_blocking_, QuicTagVector(), - /*enable_token_binding*/ false)); + migrate_sessions_early_, force_hol_blocking_, race_cert_verification_, + QuicTagVector(), /*enable_token_binding*/ false)); factory_->set_require_confirmation(false); EXPECT_FALSE(factory_->has_quic_server_info_factory()); factory_->set_quic_server_info_factory(new MockQuicServerInfoFactory()); @@ -379,6 +379,11 @@ return QuicStreamFactoryPeer::HasActiveSession(factory_.get(), server_id); } + bool HasActiveCertVerifierJob(const QuicServerId& server_id) { + return QuicStreamFactoryPeer::HasActiveCertVerifierJob(factory_.get(), + server_id); + } + QuicChromiumClientSession* GetActiveSession( const HostPortPair& host_port_pair) { QuicServerId server_id(host_port_pair, PRIVACY_MODE_DISABLED); @@ -547,6 +552,7 @@ bool migrate_sessions_on_network_change_; bool migrate_sessions_early_; bool force_hol_blocking_; + bool race_cert_verification_; }; class QuicStreamFactoryTest : public QuicStreamFactoryTestBase, @@ -4136,6 +4142,64 @@ EXPECT_EQ(test_cert2, cached2->certs()[0]); } +TEST_P(QuicStreamFactoryTest, StartCertVerifyJob) { + Initialize(); + + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)}; + SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + + // Save current state of |race_cert_verification|. + bool race_cert_verification = + QuicStreamFactoryPeer::GetRaceCertVerification(factory_.get()); + + // Load server config. + HostPortPair host_port_pair("test.example.com", kDefaultServerPort); + QuicServerId quic_server_id(host_port_pair_, privacy_mode_); + QuicStreamFactoryPeer::CacheDummyServerConfig(factory_.get(), quic_server_id); + + QuicStreamFactoryPeer::SetRaceCertVerification(factory_.get(), true); + + // Start CertVerifyJob. + QuicAsyncStatus status = QuicStreamFactoryPeer::StartCertVerifyJob( + factory_.get(), quic_server_id, /*cert_verify_flags=*/0, net_log_); + EXPECT_NE(QUIC_FAILURE, status); + + if (status == QUIC_PENDING) { + // Verify CertVerifierJob has started. + EXPECT_TRUE(HasActiveCertVerifierJob(quic_server_id)); + + while (HasActiveCertVerifierJob(quic_server_id)) { + base::RunLoop().RunUntilIdle(); + } + } + // Verify CertVerifierJob has finished. + EXPECT_FALSE(HasActiveCertVerifierJob(quic_server_id)); + + // Start a QUIC request. + QuicStreamRequest request(factory_.get()); + EXPECT_EQ(ERR_IO_PENDING, + request.Request(host_port_pair_, privacy_mode_, + /*cert_verify_flags=*/0, url_, "GET", net_log_, + callback_.callback())); + + EXPECT_EQ(OK, callback_.WaitForResult()); + + std::unique_ptr<QuicHttpStream> stream = request.CreateStream(); + EXPECT_TRUE(stream.get()); + + // Restore |race_cert_verification|. + QuicStreamFactoryPeer::SetRaceCertVerification(factory_.get(), + race_cert_verification); + + EXPECT_TRUE(socket_data.AllReadDataConsumed()); + EXPECT_TRUE(socket_data.AllWriteDataConsumed()); + + // Verify there are no outstanding CertVerifierJobs after request has + // finished. + EXPECT_FALSE(HasActiveCertVerifierJob(quic_server_id)); +} + TEST_P(QuicStreamFactoryTest, QuicDoingZeroRTT) { Initialize();
diff --git a/net/quic/test_tools/quic_stream_factory_peer.cc b/net/quic/test_tools/quic_stream_factory_peer.cc index d891cfe..ec7034a4 100644 --- a/net/quic/test_tools/quic_stream_factory_peer.cc +++ b/net/quic/test_tools/quic_stream_factory_peer.cc
@@ -7,11 +7,14 @@ #include <string> #include <vector> +#include "net/cert/x509_certificate.h" #include "net/quic/crypto/quic_crypto_client_config.h" #include "net/quic/quic_chromium_client_session.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_http_stream.h" #include "net/quic/quic_stream_factory.h" +#include "net/test/cert_test_util.h" +#include "net/test/test_data_directory.h" using std::string; using std::vector; @@ -33,6 +36,12 @@ return factory->HasActiveSession(server_id); } +bool QuicStreamFactoryPeer::HasActiveCertVerifierJob( + QuicStreamFactory* factory, + const QuicServerId& server_id) { + return factory->HasActiveCertVerifierJob(server_id); +} + QuicChromiumClientSession* QuicStreamFactoryPeer::GetActiveSession( QuicStreamFactory* factory, const QuicServerId& server_id) { @@ -82,6 +91,25 @@ factory->delay_tcp_race_ = delay_tcp_race; } +bool QuicStreamFactoryPeer::GetRaceCertVerification( + QuicStreamFactory* factory) { + return factory->race_cert_verification_; +} + +void QuicStreamFactoryPeer::SetRaceCertVerification( + QuicStreamFactory* factory, + bool race_cert_verification) { + factory->race_cert_verification_ = race_cert_verification; +} + +QuicAsyncStatus QuicStreamFactoryPeer::StartCertVerifyJob( + QuicStreamFactory* factory, + const QuicServerId& server_id, + int cert_verify_flags, + const BoundNetLog& net_log) { + return factory->StartCertVerifyJob(server_id, cert_verify_flags, net_log); +} + void QuicStreamFactoryPeer::SetYieldAfterPackets(QuicStreamFactory* factory, int yield_after_packets) { factory->yield_after_packets_ = yield_after_packets; @@ -149,9 +177,15 @@ string server_config(reinterpret_cast<const char*>(&scfg), sizeof(scfg)); string source_address_token("test_source_address_token"); string signature("test_signature"); - string test_cert("test_cert"); + vector<string> certs; - certs.push_back(test_cert); + // Load a certificate that is valid for *.example.org + scoped_refptr<X509Certificate> cert( + ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem")); + DCHECK(cert); + std::string der_bytes; + DCHECK(X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_bytes)); + certs.push_back(der_bytes); QuicCryptoClientConfig* crypto_config = &factory->crypto_config_; QuicCryptoClientConfig::CachedState* cached =
diff --git a/net/quic/test_tools/quic_stream_factory_peer.h b/net/quic/test_tools/quic_stream_factory_peer.h index bf18a18..385ebe3d 100644 --- a/net/quic/test_tools/quic_stream_factory_peer.h +++ b/net/quic/test_tools/quic_stream_factory_peer.h
@@ -12,6 +12,7 @@ #include "base/task_runner.h" #include "net/base/host_port_pair.h" #include "net/base/privacy_mode.h" +#include "net/log/net_log.h" #include "net/quic/quic_protocol.h" #include "net/quic/quic_server_id.h" #include "net/quic/quic_time.h" @@ -36,6 +37,9 @@ static bool HasActiveSession(QuicStreamFactory* factory, const QuicServerId& server_id); + static bool HasActiveCertVerifierJob(QuicStreamFactory* factory, + const QuicServerId& server_id); + static QuicChromiumClientSession* GetActiveSession( QuicStreamFactory* factory, const QuicServerId& server_id); @@ -59,6 +63,16 @@ static void SetDelayTcpRace(QuicStreamFactory* factory, bool delay_tcp_race); + static bool GetRaceCertVerification(QuicStreamFactory* factory); + + static void SetRaceCertVerification(QuicStreamFactory* factory, + bool race_cert_verification); + + static QuicAsyncStatus StartCertVerifyJob(QuicStreamFactory* factory, + const QuicServerId& server_id, + int cert_verify_flags, + const BoundNetLog& net_log); + static void SetYieldAfterPackets(QuicStreamFactory* factory, int yield_after_packets);
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc index ad5748d..0e80d61d 100644 --- a/net/tools/quic/test_tools/quic_test_client.cc +++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -83,6 +83,16 @@ return QUIC_SUCCESS; } + QuicAsyncStatus VerifyCertChain( + const std::string& hostname, + const std::vector<std::string>& certs, + const ProofVerifyContext* verify_context, + std::string* error_details, + std::unique_ptr<ProofVerifyDetails>* verify_details, + std::unique_ptr<ProofVerifierCallback> callback) override { + return QUIC_SUCCESS; + } + const string& common_name() const { return common_name_; } const string& cert_sct() const { return cert_sct_; }
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc index 3f93b3e..8eded65 100644 --- a/net/url_request/url_request_context_builder.cc +++ b/net/url_request/url_request_context_builder.cc
@@ -193,7 +193,8 @@ quic_close_sessions_on_ip_change(false), quic_migrate_sessions_on_network_change(false), quic_migrate_sessions_early(false), - quic_disable_bidirectional_streams(false) {} + quic_disable_bidirectional_streams(false), + quic_race_cert_verification(false) {} URLRequestContextBuilder::HttpNetworkSessionParams::~HttpNetworkSessionParams() {} @@ -441,6 +442,8 @@ http_network_session_params_.quic_migrate_sessions_early; network_session_params.quic_disable_bidirectional_streams = http_network_session_params_.quic_disable_bidirectional_streams; + network_session_params.quic_race_cert_verification = + http_network_session_params_.quic_race_cert_verification; if (proxy_delegate_) { network_session_params.proxy_delegate = proxy_delegate_.get(); storage->set_proxy_delegate(std::move(proxy_delegate_));
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h index ed76c00..58f0516a 100644 --- a/net/url_request/url_request_context_builder.h +++ b/net/url_request/url_request_context_builder.h
@@ -105,6 +105,7 @@ bool quic_migrate_sessions_on_network_change; bool quic_migrate_sessions_early; bool quic_disable_bidirectional_streams; + bool quic_race_cert_verification; }; URLRequestContextBuilder(); @@ -283,6 +284,11 @@ quic_disable_bidirectional_streams; } + void set_quic_race_cert_verification(bool quic_race_cert_verification) { + http_network_session_params_.quic_race_cert_verification = + quic_race_cert_verification; + } + void set_throttling_enabled(bool throttling_enabled) { throttling_enabled_ = throttling_enabled; }
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter index d5580351..c6ff6ef 100644 --- a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter +++ b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
@@ -2,6 +2,7 @@ -IFrameZoomBrowserTest.RedirectToPageWithSubframeZoomsCorrectly -IFrameZoomBrowserTest.SubframesDontZoomIndependently -NavigationControllerBrowserTest.BackTwiceToIframeWithContent +-NavigationHandleImplHttpsUpgradeBrowserTest.* -RenderFrameHostManagerTest.RestoreSubframeFileAccessForHistoryNavigation -RequestDataResourceDispatcherHostBrowserTest.* -ServiceWorker*
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 61b8d73..a9dbff4 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1384,3 +1384,4 @@ # Minor image differences after Skia GPU batching change. crbug.com/625909 virtual/gpu-rasterization/fast/images/color-profile-group.html [ NeedsRebaseline ] crbug.com/625909 virtual/gpu/fast/canvas/image-object-in-canvas.html [ NeedsRebaseline ] +crbug.com/626823 virtual/gpu-rasterization/fast/images/color-profile-iframe.html [ NeedsManualRebaseline ]
diff --git a/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value-expected.html b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value-expected.html new file mode 100644 index 0000000..e528d75 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value-expected.html
@@ -0,0 +1,2 @@ +<!DOCTYPE html> +<p>crbug.com/625604: Don't display fallback content when value attribute is empty rather than null. </p>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value.html b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value.html new file mode 100644 index 0000000..c471250 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value.html
@@ -0,0 +1,6 @@ +<!DOCTYPE html> +<p>crbug.com/625604: Don't display fallback content when value attribute is empty rather than null. </p> +<input value="" type="image" style="background-image: url('data:image/gif;base64,R0lGODdhAgACAIABAAAAAP///ywAAAAAAgACAAACA0QCBQA7'); display: block;"> +<input value="" type="image" style="display: block;"> + +
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column-expected.txt new file mode 100644 index 0000000..0713d4f --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column-expected.txt
@@ -0,0 +1,43 @@ +CONSOLE MESSAGE: line 1: 239 +CONSOLE MESSAGE: line 2: 239 +{ + column : 9 + level : log + line : 1 + source : console-api + stack : { + callFrames : [ + [0] : { + columnNumber : 9 + functionName : + lineNumber : 1 + scriptId : 0 + url : + } + ] + } + text : 239 + type : log + url : +} +{ + column : 3 + level : log + line : 2 + source : console-api + stack : { + callFrames : [ + [0] : { + columnNumber : 3 + functionName : + lineNumber : 2 + scriptId : 0 + url : + } + ] + } + text : 239 + type : log + url : +} +
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column.html b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column.html new file mode 100644 index 0000000..53bef3dc --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column.html
@@ -0,0 +1,41 @@ +<html> +<head> +<script type="text/javascript" src="../../http/tests/inspector-protocol/inspector-protocol-test.js"></script> +<script> + +function test() +{ + InspectorTest.sendCommand("Console.enable", {}); + + addConsoleMessagePromise("console.log(239)") + .then(dumpMessage) + .then(() => addConsoleMessagePromise("var l = console.log;\n l(239)")) + .then(dumpMessage) + .then(() => InspectorTest.completeTest()); + + function addConsoleMessagePromise(expression) + { + var cb; + var p = new Promise((resolver) => cb = resolver); + InspectorTest.eventHandler["Console.messageAdded"] = (messageObject) => cb(messageObject); + InspectorTest.sendCommand("Runtime.evaluate", { expression: expression }); + return p; + } + + function dumpMessage(messageObject) + { + var msg = messageObject.params.message; + delete msg.executionContextId; + delete msg.parameters; + delete msg.timestamp; + for (var frame of msg.stack.callFrames) + frame.scriptId = 0; + InspectorTest.logObject(msg); + } +} + +</script> +</head> +<body onload="runTest()"> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html b/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html index 88a3050..4a8f0f1 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html +++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html
@@ -43,7 +43,7 @@ { var workerId = messageObject["params"]["workerId"]; InspectorTest.log("Worker created"); - sendCommandToWorker("Console.enable", {}, workerId); + sendCommandToWorker("Runtime.enable", {}, workerId); if (!--waitForWorkers) InspectorTest.sendCommandOrDie("Runtime.evaluate", { expression: "worker1.postMessage(239);worker2.postMessage(42);" }); } @@ -53,11 +53,9 @@ InspectorTest.eventHandler["Worker.dispatchMessageFromWorker"] = function(messageObject) { var message = JSON.parse(messageObject["params"]["message"]); - if (message["method"] === "Console.messageAdded") { - var callFrames = message.params.message.stack.callFrames; - if (callFrames.length < 2 || callFrames[1].functionName !== "boo1") - return; - InspectorTest.log(message.params.message.stack.callFrames.length > 0 ? "Message with stack trace received." : "[FAIL] Message contains empty stack trace"); + if (message["method"] === "Runtime.exceptionThrown") { + var callFrames = message.params.details.stack ? message.params.details.stack.callFrames : []; + InspectorTest.log(callFrames.length > 0 ? "Message with stack trace received." : "[FAIL] Message contains empty stack trace"); messageReceived = true; if (messageReceived && workerTerminated) InspectorTest.completeTest();
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript-expected.txt new file mode 100644 index 0000000..06a7cb8b --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript-expected.txt
@@ -0,0 +1,24 @@ +{ + exceptionDetails : { + columnNumber : 2 + lineNumber : 1 + scriptId : 0 + text : Uncaught SyntaxError: Unexpected end of input + url : foo1.js + } +} +{ + scriptId : 0 +} +{ +} +{ + exceptionDetails : { + columnNumber : 13 + lineNumber : 0 + scriptId : 0 + text : Uncaught SyntaxError: Unexpected identifier + url : foo4.js + } +} +
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript.html b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript.html new file mode 100644 index 0000000..699e55b --- /dev/null +++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript.html
@@ -0,0 +1,45 @@ +<html> +<head> +<script type="text/javascript" src="../../http/tests/inspector-protocol/inspector-protocol-test.js"></script> +<script> +function test() +{ + var executionContextId; + + InspectorTest.sendCommand("Runtime.enable", {}); + InspectorTest.eventHandler["Runtime.executionContextCreated"] = function(messageObject) + { + executionContextId = messageObject.params.context.id; + + compileScriptPromise("\n (", false, "foo1.js") + .then(dumpResult) + .then(() => compileScriptPromise("239", true, "foo2.js")) + .then(dumpResult) + .then(() => compileScriptPromise("239", false, "foo3.js")) + .then(dumpResult) + .then(() => compileScriptPromise("testfunction f()\n{\n return 0;\n}\n", false, "foo4.js")) + .then(dumpResult) + .then(() => InspectorTest.completeTest()); + } + + function dumpResult(messageObject) + { + if (messageObject.result.exceptionDetails) + messageObject.result.exceptionDetails.scriptId = 0; + if (messageObject.result.scriptId) + messageObject.result.scriptId = 0; + InspectorTest.logObject(messageObject.result); + } + + function compileScriptPromise(expression, persistScript, sourceURL) + { + var cb; + var p = new Promise((resolver) => cb = resolver); + InspectorTest.sendCommand("Runtime.compileScript", { expression: expression, sourceURL: sourceURL, persistScript: persistScript, executionContextId: executionContextId }, cb); + return p; + } +} +</script> +</head> +<body onLoad="runTest();"></body> +</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt index b5fbe8f7..1758b56 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt
@@ -1,8 +1,6 @@ CONSOLE ERROR: line 12: Uncaught (in promise) Error: Handled error CONSOLE ERROR: line 12: Uncaught (in promise) Error: Handled error CONSOLE ERROR: line 12: Uncaught (in promise) Error: Handled error -CONSOLE MESSAGE: Handler added to rejected promise -CONSOLE MESSAGE: Handler added to rejected promise Tests that console revokes lazily handled promise rejections. Creating promise
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt index 8e84e0eb..7bcabaa 100644 --- a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt
@@ -1,5 +1,4 @@ CONSOLE ERROR: line 1: Uncaught (in promise) Error: Handled error -CONSOLE MESSAGE: Handler added to rejected promise Tests that console revokes lazily handled promise rejections. Creating worker with promise
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt index 593cc8c5..6c599e61 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt
@@ -1,4 +1,4 @@ Verifies proactive javascript compilation. -0:14 [Error] Uncaught SyntaxError: Unexpected identifier +0:13 [Error] Uncaught SyntaxError: Unexpected identifier
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt index 64ac2c0..878de58c 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt
@@ -12,7 +12,7 @@ Running script exceptionDetails: Uncaught ReferenceError: c is not defined - line: 1, column: 15 + line: 0, column: 15 exceptionDetails stack trace: url: test.js function: @@ -22,6 +22,6 @@ Compiling script exceptionDetails: Uncaught SyntaxError: Unexpected token } - line: 1, column: 0 + line: 0, column: 0 no stack trace attached to exceptionDetails
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html index 1378028..b1f7ca4 100644 --- a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html +++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html
@@ -9,7 +9,7 @@ { InspectorTest.addResult("exceptionDetails:") InspectorTest.addResult(" " + exceptionDetails.text); - InspectorTest.addResult(" line: " + exceptionDetails.line + ", column: " + exceptionDetails.column); + InspectorTest.addResult(" line: " + exceptionDetails.lineNumber + ", column: " + exceptionDetails.columnNumber); var stack = exceptionDetails.stack ? exceptionDetails.stack.callFrames : null; if (!stack) { @@ -19,7 +19,7 @@ for (var i = 0; i < stack.length && i < 100; ++i) { InspectorTest.addResult(" url: " + stack[i].url); InspectorTest.addResult(" function: " + stack[i].functionName); - InspectorTest.addResult(" line: " + stack[i].lineNumber); + InspectorTest.addResult(" line: " + stack[i].lineNumber); } } }
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp index 179c4229..9b9530c9 100644 --- a/third_party/WebKit/Source/core/dom/Document.cpp +++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -175,6 +175,7 @@ #include "core/inspector/InspectorInstrumentation.h" #include "core/inspector/InspectorTraceEvents.h" #include "core/inspector/InstanceCounters.h" +#include "core/inspector/MainThreadDebugger.h" #include "core/layout/HitTestResult.h" #include "core/layout/LayoutPart.h" #include "core/layout/LayoutView.h" @@ -2890,10 +2891,9 @@ return domWindow(); } -void Document::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location) +void Document::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location) { - ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location)); - addConsoleMessage(consoleMessage); + MainThreadDebugger::instance()->exceptionThrown(m_frame.get(), errorMessage, std::move(location)); } void Document::setURL(const KURL& url)
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h index e0bc025..44e65c6b 100644 --- a/third_party/WebKit/Source/core/dom/Document.h +++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -951,7 +951,7 @@ void cancelIdleCallback(int id); EventTarget* errorEventTarget() final; - void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) final; + void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) final; void initDNSPrefetch();
diff --git a/third_party/WebKit/Source/core/dom/ExecutionContext.cpp b/third_party/WebKit/Source/core/dom/ExecutionContext.cpp index 4251bcfb..c2e51cd 100644 --- a/third_party/WebKit/Source/core/dom/ExecutionContext.cpp +++ b/third_party/WebKit/Source/core/dom/ExecutionContext.cpp
@@ -153,14 +153,14 @@ // First report the original exception and only then all the nested ones. if (!dispatchErrorEvent(errorEvent, corsStatus)) - logExceptionToConsole(errorEvent->messageForConsole(), errorEvent->location()->clone()); + exceptionThrown(errorEvent->messageForConsole(), errorEvent->location()->clone()); if (!m_pendingExceptions) return; for (size_t i = 0; i < m_pendingExceptions->size(); i++) { PendingException* e = m_pendingExceptions->at(i).get(); - logExceptionToConsole(e->m_errorMessage, std::move(e->m_location)); + exceptionThrown(e->m_errorMessage, std::move(e->m_location)); } m_pendingExceptions.reset(); }
diff --git a/third_party/WebKit/Source/core/dom/ExecutionContext.h b/third_party/WebKit/Source/core/dom/ExecutionContext.h index b13a279..8655591 100644 --- a/third_party/WebKit/Source/core/dom/ExecutionContext.h +++ b/third_party/WebKit/Source/core/dom/ExecutionContext.h
@@ -107,7 +107,7 @@ void reportException(ErrorEvent*, AccessControlStatus); virtual void addConsoleMessage(ConsoleMessage*) = 0; - virtual void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) = 0; + virtual void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) = 0; PublicURLManager& publicURLManager();
diff --git a/third_party/WebKit/Source/core/fetch/FetchContext.cpp b/third_party/WebKit/Source/core/fetch/FetchContext.cpp index 207af0f..ca03599 100644 --- a/third_party/WebKit/Source/core/fetch/FetchContext.cpp +++ b/third_party/WebKit/Source/core/fetch/FetchContext.cpp
@@ -109,7 +109,7 @@ { } -void FetchContext::upgradeInsecureRequest(FetchRequest&) +void FetchContext::upgradeInsecureRequest(ResourceRequest&) { }
diff --git a/third_party/WebKit/Source/core/fetch/FetchContext.h b/third_party/WebKit/Source/core/fetch/FetchContext.h index d4b5455..7a8400f 100644 --- a/third_party/WebKit/Source/core/fetch/FetchContext.h +++ b/third_party/WebKit/Source/core/fetch/FetchContext.h
@@ -104,7 +104,7 @@ virtual void sendImagePing(const KURL&); virtual void addConsoleMessage(const String&) const; virtual SecurityOrigin* getSecurityOrigin() const { return nullptr; } - virtual void upgradeInsecureRequest(FetchRequest&); + virtual void upgradeInsecureRequest(ResourceRequest&); virtual void addClientHintsIfNecessary(FetchRequest&); virtual void addCSPHeaderIfNecessary(Resource::Type, FetchRequest&);
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp index 31e8389..125574198 100644 --- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp +++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
@@ -410,7 +410,8 @@ { ASSERT(request.options().synchronousPolicy == RequestAsynchronously || factory.type() == Resource::Raw || factory.type() == Resource::XSLStyleSheet); - context().upgradeInsecureRequest(request); + if (request.resourceRequest().httpHeaderField("Upgrade-Insecure-Requests") != AtomicString("1")) + context().upgradeInsecureRequest(request.mutableResourceRequest()); context().addClientHintsIfNecessary(request); context().addCSPHeaderIfNecessary(factory.type(), request);
diff --git a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp index 46d5ea4..24ed78e2 100644 --- a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
@@ -824,7 +824,7 @@ alt = fastGetAttribute(titleAttr); if (alt.isNull()) alt = fastGetAttribute(valueAttr); - if (alt.isEmpty()) + if (alt.isNull()) alt = locale().queryString(WebLocalizedString::InputElementAltText); return alt; }
diff --git a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp index 687ba7ea..e8c6b9c 100644 --- a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp +++ b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
@@ -123,6 +123,11 @@ debugger()->contextDestroyed(scriptState->context()); } +void MainThreadDebugger::exceptionThrown(LocalFrame* frame, const String& errorMessage, std::unique_ptr<SourceLocation> location) +{ + debugger()->exceptionThrown(contextGroupId(frame), errorMessage, location->url(), location->lineNumber(), location->columnNumber(), location->cloneStackTrace(), location->scriptId()); +} + int MainThreadDebugger::contextGroupId(LocalFrame* frame) { LocalFrame* localFrameRoot = frame->localFrameRoot();
diff --git a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h index f2af18b..9c649e4 100644 --- a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h +++ b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h
@@ -43,6 +43,7 @@ class LocalFrame; class SecurityOrigin; +class SourceLocation; class CORE_EXPORT MainThreadDebugger final : public ThreadDebugger { WTF_MAKE_NONCOPYABLE(MainThreadDebugger); @@ -64,10 +65,12 @@ InspectorTaskRunner* taskRunner() const { return m_taskRunner.get(); } bool isWorker() override { return false; } void setClientMessageLoop(std::unique_ptr<ClientMessageLoop>); + // TODO(dgozman): by making this method virtual, we can move many methods to ThreadDebugger and avoid some duplication. Should be careful about performance. int contextGroupId(LocalFrame*); void didClearContextsForFrame(LocalFrame*); void contextCreated(ScriptState*, LocalFrame*, SecurityOrigin*); void contextWillBeDestroyed(ScriptState*); + void exceptionThrown(LocalFrame*, const String& errorMessage, std::unique_ptr<SourceLocation>); void installAdditionalCommandLineAPI(v8::Local<v8::Context>, v8::Local<v8::Object>) override;
diff --git a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp index 80129d3..9aaf4a3 100644 --- a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp +++ b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp
@@ -71,6 +71,11 @@ debugger()->contextDestroyed(context); } +void WorkerThreadDebugger::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location) +{ + debugger()->exceptionThrown(workerContextGroupId, errorMessage, location->url(), location->lineNumber(), location->columnNumber(), location->cloneStackTrace(), location->scriptId()); +} + int WorkerThreadDebugger::contextGroupId() { return workerContextGroupId;
diff --git a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h index b95f354..ba96fd9 100644 --- a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h +++ b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h
@@ -31,13 +31,15 @@ #ifndef WorkerThreadDebugger_h #define WorkerThreadDebugger_h +#include "core/CoreExport.h" #include "core/inspector/ThreadDebugger.h" namespace blink { +class SourceLocation; class WorkerThread; -class WorkerThreadDebugger final : public ThreadDebugger { +class CORE_EXPORT WorkerThreadDebugger final : public ThreadDebugger { WTF_MAKE_NONCOPYABLE(WorkerThreadDebugger); public: explicit WorkerThreadDebugger(WorkerThread*, v8::Isolate*); @@ -48,6 +50,7 @@ int contextGroupId(); void contextCreated(v8::Local<v8::Context>); void contextWillBeDestroyed(v8::Local<v8::Context>); + void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>); // V8DebuggerClient implementation. void runMessageLoopOnPause(int contextGroupId) override;
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp index d200c0cec..e219e446 100644 --- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp +++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -624,6 +624,11 @@ return true; } +void DocumentLoader::upgradeInsecureRequest() +{ + fetcher()->context().upgradeInsecureRequest(m_request); +} + void DocumentLoader::startLoadingMainResource() { timing().markNavigationStart();
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.h b/third_party/WebKit/Source/core/loader/DocumentLoader.h index 9bf58f7f..a8b4216 100644 --- a/third_party/WebKit/Source/core/loader/DocumentLoader.h +++ b/third_party/WebKit/Source/core/loader/DocumentLoader.h
@@ -113,6 +113,8 @@ NavigationType getNavigationType() const { return m_navigationType; } void setNavigationType(NavigationType navigationType) { m_navigationType = navigationType; } + void upgradeInsecureRequest(); + void startLoadingMainResource(); void acceptDataFromThreadedReceiver(const char* data, int dataLength, int encodedDataLength);
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp index d73172a9..7838ecc 100644 --- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp +++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -676,14 +676,20 @@ return m_document ? m_document->getSecurityOrigin() : nullptr; } -void FrameFetchContext::upgradeInsecureRequest(FetchRequest& fetchRequest) +void FrameFetchContext::upgradeInsecureRequest(ResourceRequest& resourceRequest) { - KURL url = fetchRequest.resourceRequest().url(); - // Tack an 'Upgrade-Insecure-Requests' header to outgoing navigational requests, as described in // https://w3c.github.io/webappsec/specs/upgrade/#feature-detect - if (fetchRequest.resourceRequest().frameType() != WebURLRequest::FrameTypeNone) - fetchRequest.mutableResourceRequest().addHTTPHeaderField("Upgrade-Insecure-Requests", "1"); + if (resourceRequest.frameType() != WebURLRequest::FrameTypeNone) { + + // Early return if the request has already been upgraded. + if (resourceRequest.httpHeaderField("Upgrade-Insecure-Requests") == AtomicString("1")) + return; + + resourceRequest.addHTTPHeaderField("Upgrade-Insecure-Requests", "1"); + } + + KURL url = resourceRequest.url(); // If we don't yet have an |m_document| (because we're loading an iframe, for instance), check the FrameLoader's policy. WebInsecureRequestPolicy relevantPolicy = m_document ? m_document->getInsecureRequestPolicy() : frame()->loader().getInsecureRequestPolicy(); @@ -695,17 +701,15 @@ // 1. Are for subresources (including nested frames). // 2. Are form submissions. // 3. Whose hosts are contained in the document's InsecureNavigationSet. - const ResourceRequest& request = fetchRequest.resourceRequest(); - if (request.frameType() == WebURLRequest::FrameTypeNone - || request.frameType() == WebURLRequest::FrameTypeNested - || request.requestContext() == WebURLRequest::RequestContextForm - || (!url.host().isNull() && relevantNavigationSet->contains(url.host().impl()->hash()))) - { + if (resourceRequest.frameType() == WebURLRequest::FrameTypeNone + || resourceRequest.frameType() == WebURLRequest::FrameTypeNested + || resourceRequest.requestContext() == WebURLRequest::RequestContextForm + || (!url.host().isNull() && relevantNavigationSet->contains(url.host().impl()->hash()))) { UseCounter::count(m_document, UseCounter::UpgradeInsecureRequestsUpgradedRequest); url.setProtocol("https"); if (url.port() == 80) url.setPort(443); - fetchRequest.mutableResourceRequest().setURL(url); + resourceRequest.setURL(url); } } }
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.h b/third_party/WebKit/Source/core/loader/FrameFetchContext.h index e04fd81..281f18f 100644 --- a/third_party/WebKit/Source/core/loader/FrameFetchContext.h +++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.h
@@ -99,7 +99,7 @@ void sendImagePing(const KURL&) override; void addConsoleMessage(const String&) const override; SecurityOrigin* getSecurityOrigin() const override; - void upgradeInsecureRequest(FetchRequest&) override; + void upgradeInsecureRequest(ResourceRequest&) override; void addClientHintsIfNecessary(FetchRequest&) override; void addCSPHeaderIfNecessary(Resource::Type, FetchRequest&) override;
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp index 8e8aa81b..c9e1ebff 100644 --- a/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp +++ b/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp
@@ -186,7 +186,7 @@ fetchRequest.mutableResourceRequest().setRequestContext(requestContext); fetchRequest.mutableResourceRequest().setFrameType(frameType); - fetchContext->upgradeInsecureRequest(fetchRequest); + fetchContext->upgradeInsecureRequest(fetchRequest.mutableResourceRequest()); EXPECT_STREQ(expectedURL.getString().utf8().data(), fetchRequest.resourceRequest().url().getString().utf8().data()); EXPECT_EQ(expectedURL.protocol(), fetchRequest.resourceRequest().url().protocol()); @@ -204,10 +204,16 @@ fetchRequest.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextScript); fetchRequest.mutableResourceRequest().setFrameType(frameType); - fetchContext->upgradeInsecureRequest(fetchRequest); + fetchContext->upgradeInsecureRequest(fetchRequest.mutableResourceRequest()); EXPECT_STREQ(shouldPrefer ? "1" : "", fetchRequest.resourceRequest().httpHeaderField(HTTPNames::Upgrade_Insecure_Requests).utf8().data()); + + // Calling upgradeInsecureRequest more than once shouldn't affect the header. + if (shouldPrefer) { + fetchContext->upgradeInsecureRequest(fetchRequest.mutableResourceRequest()); + EXPECT_STREQ("1", fetchRequest.resourceRequest().httpHeaderField(HTTPNames::Upgrade_Insecure_Requests).utf8().data()); + } } RefPtr<SecurityOrigin> exampleOrigin;
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp index 71242fc..cf11fc8a 100644 --- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp +++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -1451,6 +1451,7 @@ if (m_provisionalDocumentLoader->isClientRedirect()) m_provisionalDocumentLoader->appendRedirect(m_frame->document()->url()); m_provisionalDocumentLoader->appendRedirect(m_provisionalDocumentLoader->request().url()); + m_provisionalDocumentLoader->upgradeInsecureRequest(); double triggeringEventTime = frameLoadRequest.triggeringEvent() ? frameLoadRequest.triggeringEvent()->platformTimeStamp() : 0; client()->dispatchDidStartProvisionalLoad(triggeringEventTime); ASSERT(m_provisionalDocumentLoader);
diff --git a/third_party/WebKit/Source/core/testing/NullExecutionContext.h b/third_party/WebKit/Source/core/testing/NullExecutionContext.h index fb09789..379de81 100644 --- a/third_party/WebKit/Source/core/testing/NullExecutionContext.h +++ b/third_party/WebKit/Source/core/testing/NullExecutionContext.h
@@ -38,7 +38,7 @@ DOMTimerCoordinator* timers() override { return nullptr; } void addConsoleMessage(ConsoleMessage*) override { } - void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override { } + void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override { } void setIsSecureContext(bool); bool isSecureContext(String& errorMessage, const SecureContextCheck = StandardSecureContextCheck) const override;
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp index 6d858ae8..1d605348 100644 --- a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp
@@ -7,6 +7,7 @@ #include "bindings/core/v8/ScriptSourceCode.h" #include "bindings/core/v8/WorkerOrWorkletScriptController.h" #include "core/frame/FrameConsole.h" +#include "core/inspector/MainThreadDebugger.h" namespace blink { @@ -35,4 +36,9 @@ frame()->console().addMessage(consoleMessage); } +void MainThreadWorkletGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location) +{ + MainThreadDebugger::instance()->exceptionThrown(frame(), errorMessage, std::move(location)); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h index 14d4478..eb966b54 100644 --- a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h
@@ -27,6 +27,7 @@ using LocalFrameLifecycleObserver::frame; void addConsoleMessage(ConsoleMessage*) final; + void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) final; DEFINE_INLINE_VIRTUAL_TRACE() {
diff --git a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp index a43edb0..ecbb0fee 100644 --- a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp
@@ -34,6 +34,7 @@ #include "core/events/MessageEvent.h" #include "core/frame/LocalDOMWindow.h" #include "core/inspector/ConsoleMessage.h" +#include "core/inspector/WorkerThreadDebugger.h" #include "core/origin_trials/OriginTrialContext.h" #include "core/workers/SharedWorkerThread.h" #include "core/workers/WorkerClients.h" @@ -83,11 +84,11 @@ return static_cast<SharedWorkerThread*>(Base::thread()); } -void SharedWorkerGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location) +void SharedWorkerGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location) { - WorkerGlobalScope::logExceptionToConsole(errorMessage, location->clone()); - ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location)); - addMessageToWorkerConsole(consoleMessage); + WorkerGlobalScope::exceptionThrown(errorMessage, location->clone()); + if (WorkerThreadDebugger* debugger = WorkerThreadDebugger::from(thread()->isolate())) + debugger->exceptionThrown(errorMessage, std::move(location)); } DEFINE_TRACE(SharedWorkerGlobalScope)
diff --git a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h index 4879f9a..8df3b94f 100644 --- a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h
@@ -65,7 +65,7 @@ private: SharedWorkerGlobalScope(const String& name, const KURL&, const String& userAgent, SharedWorkerThread*, std::unique_ptr<SecurityOrigin::PrivilegeData>, WorkerClients*); - void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override; + void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override; String m_name; };
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp index d20f8ba..0567dfa7 100644 --- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
@@ -267,7 +267,7 @@ return this; } -void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location) +void WorkerGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location) { thread()->workerReportingProxy().reportException(errorMessage, std::move(location)); } @@ -355,7 +355,8 @@ void WorkerGlobalScope::exceptionUnhandled(const String& errorMessage, std::unique_ptr<SourceLocation> location) { - addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location))); + if (WorkerThreadDebugger* debugger = WorkerThreadDebugger::from(thread()->isolate())) + debugger->exceptionThrown(errorMessage, std::move(location)); } bool WorkerGlobalScope::isSecureContext(String& errorMessage, const SecureContextCheck privilegeContextCheck) const
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h index 76538ca1..d1de075 100644 --- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
@@ -154,7 +154,7 @@ WorkerGlobalScope(const KURL&, const String& userAgent, WorkerThread*, double timeOrigin, std::unique_ptr<SecurityOrigin::PrivilegeData>, WorkerClients*); void applyContentSecurityPolicyFromVector(const Vector<CSPHeaderAndType>& headers); - void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override; + void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override; void addMessageToWorkerConsole(ConsoleMessage*); void setV8CacheOptions(V8CacheOptions v8CacheOptions) { m_v8CacheOptions = v8CacheOptions; }
diff --git a/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h b/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h index 2271421..5ceb933 100644 --- a/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h +++ b/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h
@@ -164,7 +164,7 @@ return EventTargetNames::DedicatedWorkerGlobalScope; } - void logExceptionToConsole(const String&, std::unique_ptr<SourceLocation>) override + void exceptionThrown(const String&, std::unique_ptr<SourceLocation>) override { }
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp index 9a5d5da2..632f875 100644 --- a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp +++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp
@@ -69,12 +69,6 @@ InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText); } -void WorkletGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location) -{ - ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location)); - addConsoleMessage(consoleMessage); -} - KURL WorkletGlobalScope::virtualCompleteURL(const String& url) const { // Always return a null URL when passed a null string.
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h index 4e475c93..b8ec117 100644 --- a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h +++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h
@@ -55,7 +55,6 @@ } void reportBlockedScriptExecutionToInspector(const String& directiveText) final; - void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) final; DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js index 79457d97..0d134561 100644 --- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js +++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
@@ -808,7 +808,7 @@ if (!wasThrown) message = new WebInspector.ConsoleMessage(result.target(), WebInspector.ConsoleMessage.MessageSource.JS, level, "", WebInspector.ConsoleMessage.MessageType.Result, undefined, undefined, undefined, undefined, [result]); else - message = new WebInspector.ConsoleMessage(result.target(), WebInspector.ConsoleMessage.MessageSource.JS, level, exceptionDetails.text, WebInspector.ConsoleMessage.MessageType.Result, exceptionDetails.url, exceptionDetails.line, exceptionDetails.column, undefined, [WebInspector.UIString("Uncaught"), result], exceptionDetails.stack, undefined, undefined, exceptionDetails.scriptId); + message = new WebInspector.ConsoleMessage(result.target(), WebInspector.ConsoleMessage.MessageSource.JS, level, exceptionDetails.text, WebInspector.ConsoleMessage.MessageType.Result, exceptionDetails.url, exceptionDetails.lineNumber + 1, exceptionDetails.columnNumber + 1, undefined, [WebInspector.UIString("Uncaught"), result], exceptionDetails.stack, undefined, undefined, exceptionDetails.scriptId); message.setOriginatingMessage(originatingConsoleMessage); result.target().consoleModel.addMessage(message); },
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js index 523ecabc..44f865d7 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
@@ -40,7 +40,7 @@ /** @type {!Array.<!WebInspector.ConsoleMessage>} */ this._messages = []; /** @type {!Map<number, !WebInspector.ConsoleMessage>} */ - this._messageById = new Map(); + this._messageByExceptionId = new Map(); this._warnings = 0; this._errors = 0; this._revokedErrors = 0; @@ -79,20 +79,20 @@ if (this._isBlacklisted(msg)) return; - if (msg.level === WebInspector.ConsoleMessage.MessageLevel.RevokedError && msg._relatedMessageId) { - var relatedMessage = this._messageById.get(msg._relatedMessageId); - if (!relatedMessage) + if (msg.level === WebInspector.ConsoleMessage.MessageLevel.RevokedError && msg._revokedExceptionId) { + var exceptionMessage = this._messageByExceptionId.get(msg._revokedExceptionId); + if (!exceptionMessage) return; this._errors--; this._revokedErrors++; - relatedMessage.level = WebInspector.ConsoleMessage.MessageLevel.RevokedError; - this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageUpdated, relatedMessage); + exceptionMessage.level = WebInspector.ConsoleMessage.MessageLevel.RevokedError; + this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageUpdated, exceptionMessage); return; } this._messages.push(msg); - if (msg._messageId) - this._messageById.set(msg._messageId, msg); + if (msg._exceptionId) + this._messageByExceptionId.set(msg._exceptionId, msg); this._incrementErrorWarningCount(msg); this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, msg); }, @@ -148,7 +148,7 @@ _messagesCleared: function() { this._messages = []; - this._messageById.clear(); + this._messageByExceptionId.clear(); this._errors = 0; this._revokedErrors = 0; this._warnings = 0; @@ -244,10 +244,8 @@ * @param {number=} timestamp * @param {!RuntimeAgent.ExecutionContextId=} executionContextId * @param {?string=} scriptId - * @param {number=} messageId - * @param {number=} relatedMessageId */ -WebInspector.ConsoleMessage = function(target, source, level, messageText, type, url, line, column, requestId, parameters, stackTrace, timestamp, executionContextId, scriptId, messageId, relatedMessageId) +WebInspector.ConsoleMessage = function(target, source, level, messageText, type, url, line, column, requestId, parameters, stackTrace, timestamp, executionContextId, scriptId) { this._target = target; this.source = source; @@ -266,8 +264,6 @@ this.timestamp = timestamp || Date.now(); this.executionContextId = executionContextId || 0; this.scriptId = scriptId || null; - this._messageId = messageId || 0; - this._relatedMessageId = relatedMessageId || 0; var networkLog = target && WebInspector.NetworkLog.fromTarget(target); this.request = (requestId && networkLog) ? networkLog.requestForId(requestId) : null; @@ -311,6 +307,22 @@ }, /** + * @param {number} exceptionId + */ + setExceptionId: function(exceptionId) + { + this._exceptionId = exceptionId; + }, + + /** + * @param {number} revokedExceptionId + */ + setRevokedExceptionId: function(revokedExceptionId) + { + this._revokedExceptionId = revokedExceptionId; + }, + + /** * @return {?WebInspector.ConsoleMessage} */ originatingMessage: function() @@ -354,9 +366,9 @@ if (!msg) return false; - if (this._messageId || msg._messageId) + if (this._exceptionId || msg._exceptionId) return false; - if (this._relatedMessageId || msg._relatedMessageId) + if (this._revokedExceptionId || msg._revokedExceptionId) return false; if (!this._isEqualStackTraces(this.stackTrace, msg.stackTrace)) @@ -458,7 +470,7 @@ Warning: "warning", Error: "error", Debug: "debug", - RevokedError: "revokedError" + RevokedError: "revokedError" // This is frontend-only level, used to put exceptions to console. }; /** @@ -502,9 +514,7 @@ payload.stack, payload.timestamp * 1000, // Convert to ms. payload.executionContextId, - payload.scriptId, - payload.messageId, - payload.relatedMessageId); + payload.scriptId); this._console.addMessage(consoleMessage); },
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js index c83a4fa..4013efa2 100644 --- a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js +++ b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
@@ -339,6 +339,62 @@ /** * @override + * @param {number} exceptionId + * @param {number} timestamp + * @param {!RuntimeAgent.ExceptionDetails} details + * @param {!RuntimeAgent.RemoteObject=} exception + * @param {number=} executionContextId + */ + exceptionThrown: function(exceptionId, timestamp, details, exception, executionContextId) + { + var consoleMessage = new WebInspector.ConsoleMessage( + this._runtimeModel.target(), + WebInspector.ConsoleMessage.MessageSource.JS, + WebInspector.ConsoleMessage.MessageLevel.Error, + details.text, + undefined, + details.url, + typeof details.lineNumber === "undefined" ? undefined : details.lineNumber + 1, + typeof details.columnNumber === "undefined" ? undefined : details.columnNumber + 1, + undefined, + exception ? ["Uncaught (in promise)", exception] : undefined, + details.stack, + timestamp, + executionContextId, + details.scriptId); + consoleMessage.setExceptionId(exceptionId); + this._runtimeModel.target().consoleModel.addMessage(consoleMessage); + }, + + /** + * @override + * @param {number} timestamp + * @param {string} message + * @param {number} exceptionId + */ + exceptionRevoked: function(timestamp, message, exceptionId) + { + var consoleMessage = new WebInspector.ConsoleMessage( + this._runtimeModel.target(), + WebInspector.ConsoleMessage.MessageSource.JS, + WebInspector.ConsoleMessage.MessageLevel.RevokedError, + message, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + timestamp, + undefined, + undefined); + consoleMessage.setRevokedExceptionId(exceptionId); + this._runtimeModel.target().consoleModel.addMessage(consoleMessage); + }, + + /** + * @override * @param {!RuntimeAgent.RemoteObject} payload * @param {!Object=} hints */
diff --git a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js index 9c350e3..cbd9c613 100644 --- a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js +++ b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
@@ -313,8 +313,8 @@ exceptionDetails.text, undefined, sourceURL, - exceptionDetails.line, - exceptionDetails.column, + exceptionDetails.lineNumber + 1, + exceptionDetails.columnNumber + 1, undefined, undefined, exceptionDetails.stack);
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js index 48c38108..c3f8b49 100644 --- a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js +++ b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js
@@ -69,9 +69,9 @@ this.scheduleCompile(); return; } - if (!exceptionDetails) + if (!exceptionDetails || !exceptionDetails.hasOwnProperty("lineNumber")) return; - this._sourceFrame.uiSourceCode().addLineMessage(WebInspector.UISourceCode.Message.Level.Error, exceptionDetails.text, exceptionDetails.line - 1, exceptionDetails.column + 1); + this._sourceFrame.uiSourceCode().addLineMessage(WebInspector.UISourceCode.Message.Level.Error, exceptionDetails.text, /** @type {number} */(exceptionDetails.lineNumber), exceptionDetails.columnNumber); this._compilationFinishedForTest(); } },
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp index 50780b6..f0c7f372 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp +++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp
@@ -42,6 +42,7 @@ #include "core/fetch/ResourceLoaderOptions.h" #include "core/inspector/ConsoleMessage.h" #include "core/inspector/WorkerInspectorController.h" +#include "core/inspector/WorkerThreadDebugger.h" #include "core/loader/ThreadableLoader.h" #include "core/origin_trials/OriginTrialContext.h" #include "core/workers/WorkerClients.h" @@ -212,11 +213,11 @@ return ServiceWorkerScriptCachedMetadataHandler::create(this, scriptURL, metaData); } -void ServiceWorkerGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location) +void ServiceWorkerGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location) { - WorkerGlobalScope::logExceptionToConsole(errorMessage, location->clone()); - ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location)); - addMessageToWorkerConsole(consoleMessage); + WorkerGlobalScope::exceptionThrown(errorMessage, location->clone()); + if (WorkerThreadDebugger* debugger = WorkerThreadDebugger::from(thread()->isolate())) + debugger->exceptionThrown(errorMessage, std::move(location)); } void ServiceWorkerGlobalScope::scriptLoaded(size_t scriptSize, size_t cachedMetadataSize)
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h index 7abf63fb..3921a13 100644 --- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h +++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h
@@ -96,7 +96,7 @@ ServiceWorkerGlobalScope(const KURL&, const String& userAgent, ServiceWorkerThread*, double timeOrigin, std::unique_ptr<SecurityOrigin::PrivilegeData>, WorkerClients*); void importScripts(const Vector<String>& urls, ExceptionState&) override; CachedMetadataHandler* createWorkerScriptCachedMetadataHandler(const KURL& scriptURL, const Vector<char>* metaData) override; - void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override; + void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override; void scriptLoaded(size_t scriptSize, size_t cachedMetadataSize) override; Member<ServiceWorkerClients> m_clients;
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp index 7f85e9e..5e552c2b 100644 --- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp +++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -345,14 +345,27 @@ public: PropertyTreeManager(cc::PropertyTrees& propertyTrees, cc::Layer* rootLayer) : m_propertyTrees(propertyTrees) - , m_rootLayer(rootLayer) {} + , m_rootLayer(rootLayer) +#if DCHECK_IS_ON() + , m_isFirstEffectEver(true) +#endif + { + m_effectStack.append(BlinkEffectAndCcIdPair{nullptr, kSecondaryRootNodeId}); + } int compositorIdForTransformNode(const TransformPaintPropertyNode*); int compositorIdForClipNode(const ClipPaintPropertyNode*); + int switchToEffectNode(const EffectPaintPropertyNode& nextEffect); + int compositorIdForCurrentEffectNode() const { return m_effectStack.last().id; } private: + void buildEffectNodesRecursively(const EffectPaintPropertyNode* nextEffect); + cc::TransformTree& transformTree() { return m_propertyTrees.transform_tree; } cc::ClipTree& clipTree() { return m_propertyTrees.clip_tree; } + cc::EffectTree& effectTree() { return m_propertyTrees.effect_tree; } + + const EffectPaintPropertyNode* currentEffectNode() const { return m_effectStack.last().effect; } // Property trees which should be updated by the manager. cc::PropertyTrees& m_propertyTrees; @@ -364,6 +377,17 @@ // Maps from Blink-side property tree nodes to cc property node indices. HashMap<const TransformPaintPropertyNode*, int> m_transformNodeMap; HashMap<const ClipPaintPropertyNode*, int> m_clipNodeMap; + + struct BlinkEffectAndCcIdPair { + const EffectPaintPropertyNode* effect; + int id; + }; + Vector<BlinkEffectAndCcIdPair> m_effectStack; + +#if DCHECK_IS_ON() + HashSet<const EffectPaintPropertyNode*> m_effectNodesConverted; + bool m_isFirstEffectEver; +#endif }; int PropertyTreeManager::compositorIdForTransformNode(const TransformPaintPropertyNode* transformNode) @@ -442,6 +466,91 @@ return id; } +unsigned depth(const EffectPaintPropertyNode* node) +{ + unsigned result = 0; + for (; node; node = node->parent()) + result++; + return result; +} + +const EffectPaintPropertyNode* lowestCommonAncestor(const EffectPaintPropertyNode* nodeA, const EffectPaintPropertyNode* nodeB) +{ + // Optimized common case. + if (nodeA == nodeB) + return nodeA; + + unsigned depthA = depth(nodeA), depthB = depth(nodeB); + while (depthA > depthB) { + nodeA = nodeA->parent(); + depthA--; + } + while (depthB > depthA) { + nodeB = nodeB->parent(); + depthB--; + } + DCHECK_EQ(depthA, depthB); + while (nodeA != nodeB) { + nodeA = nodeA->parent(); + nodeB = nodeB->parent(); + } + return nodeA; +} + +int PropertyTreeManager::switchToEffectNode(const EffectPaintPropertyNode& nextEffect) +{ + const EffectPaintPropertyNode* ancestor = lowestCommonAncestor(currentEffectNode(), &nextEffect); + while (currentEffectNode() != ancestor) + m_effectStack.removeLast(); + +#if DCHECK_IS_ON() + DCHECK(m_isFirstEffectEver || currentEffectNode()) << "Malformed effect tree. Nodes in the same property tree should have common root."; + m_isFirstEffectEver = false; +#endif + buildEffectNodesRecursively(&nextEffect); + + return compositorIdForCurrentEffectNode(); +} + +void PropertyTreeManager::buildEffectNodesRecursively(const EffectPaintPropertyNode* nextEffect) +{ + if (nextEffect == currentEffectNode()) + return; + DCHECK(nextEffect); + + buildEffectNodesRecursively(nextEffect->parent()); + DCHECK_EQ(nextEffect->parent(), currentEffectNode()); + +#if DCHECK_IS_ON() + DCHECK(!m_effectNodesConverted.contains(nextEffect)) << "Malformed paint artifact. Paint chunks under the same effect should be contiguous."; + m_effectNodesConverted.add(nextEffect); +#endif + + // We currently create dummy layers to host effect nodes and corresponding render surface. + // This should be removed once cc implements better support for freestanding property trees. + scoped_refptr<cc::Layer> dummyLayer = cc::Layer::Create(); + m_rootLayer->AddChild(dummyLayer); + + // Also cc assumes a clip node is always created by a layer that creates render surface. + cc::ClipNode& dummyClip = *clipTree().Node(clipTree().Insert(cc::ClipNode(), kSecondaryRootNodeId)); + dummyClip.owner_id = dummyLayer->id(); + dummyClip.transform_id = kRealRootNodeId; + dummyClip.target_transform_id = kRealRootNodeId; + + cc::EffectNode& effectNode = *effectTree().Node(effectTree().Insert(cc::EffectNode(), compositorIdForCurrentEffectNode())); + effectNode.owner_id = dummyLayer->id(); + effectNode.clip_id = dummyClip.id; + effectNode.has_render_surface = true; + effectNode.opacity = nextEffect->opacity(); + m_effectStack.append(BlinkEffectAndCcIdPair{nextEffect, effectNode.id}); + + dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber); + dummyLayer->SetTransformTreeIndex(kSecondaryRootNodeId); + dummyLayer->SetClipTreeIndex(dummyClip.id); + dummyLayer->SetEffectTreeIndex(effectNode.id); + dummyLayer->SetScrollTreeIndex(kRealRootNodeId); +} + } // namespace void PaintArtifactCompositor::updateInLayerListMode(const PaintArtifact& paintArtifact) @@ -465,6 +574,7 @@ int transformId = propertyTreeManager.compositorIdForTransformNode(paintChunk.properties.transform.get()); int clipId = propertyTreeManager.compositorIdForClipNode(paintChunk.properties.clip.get()); + int effectId = propertyTreeManager.switchToEffectNode(*paintChunk.properties.effect.get()); layer->set_offset_to_transform_parent(layerOffset); @@ -472,7 +582,7 @@ layer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber); layer->SetTransformTreeIndex(transformId); layer->SetClipTreeIndex(clipId); - layer->SetEffectTreeIndex(kSecondaryRootNodeId); + layer->SetEffectTreeIndex(effectId); layer->SetScrollTreeIndex(kRealRootNodeId); if (m_extraDataForTestingEnabled)
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp index 4640c277..f51287bf 100644 --- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp +++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -9,9 +9,11 @@ #include "cc/layers/layer.h" #include "cc/test/fake_output_surface.h" #include "cc/trees/clip_node.h" +#include "cc/trees/effect_node.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_settings.h" #include "platform/RuntimeEnabledFeatures.h" +#include "platform/graphics/paint/EffectPaintPropertyNode.h" #include "platform/graphics/paint/PaintArtifact.h" #include "platform/testing/PictureMatchers.h" #include "platform/testing/TestPaintArtifact.h" @@ -33,6 +35,12 @@ return transform; } +EffectPaintPropertyNode* dummyRootEffect() +{ + DEFINE_STATIC_REF(EffectPaintPropertyNode, node, EffectPaintPropertyNode::create(1.0)); + return node; +} + class PaintArtifactCompositorTest : public ::testing::Test { protected: void SetUp() override @@ -97,11 +105,11 @@ TransformationMatrix().rotate(90), FloatPoint3D(100, 100, 0)); TestPaintArtifact artifact; - artifact.chunk(transform, nullptr, nullptr) + artifact.chunk(transform, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); - artifact.chunk(nullptr, nullptr, nullptr) + artifact.chunk(nullptr, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 100, 100), Color::gray); - artifact.chunk(transform, nullptr, nullptr) + artifact.chunk(transform, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(100, 100, 200, 100), Color::black); update(artifact.build()); @@ -139,9 +147,9 @@ TransformationMatrix().translate(5, 5), FloatPoint3D(), transform1); TestPaintArtifact artifact; - artifact.chunk(transform1, nullptr, nullptr) + artifact.chunk(transform1, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 300, 200), Color::white); - artifact.chunk(transform2, nullptr, nullptr) + artifact.chunk(transform2, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 300, 200), Color::black); update(artifact.build()); @@ -223,13 +231,13 @@ nullptr, FloatRoundedRect(200, 200, 700, 100), clip1); TestPaintArtifact artifact; - artifact.chunk(nullptr, clip1, nullptr) + artifact.chunk(nullptr, clip1, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::white); - artifact.chunk(nullptr, clip2, nullptr) + artifact.chunk(nullptr, clip2, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::lightGray); - artifact.chunk(nullptr, clip1, nullptr) + artifact.chunk(nullptr, clip1, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::darkGray); - artifact.chunk(nullptr, clip2, nullptr) + artifact.chunk(nullptr, clip2, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::black); update(artifact.build()); @@ -449,11 +457,11 @@ TransformationMatrix().rotate(90), FloatPoint3D(100, 100, 0)); TestPaintArtifact artifact; - artifact.chunk(transform, nullptr, nullptr) + artifact.chunk(transform, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); - artifact.chunk(nullptr, nullptr, nullptr) + artifact.chunk(nullptr, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 100, 100), Color::gray); - artifact.chunk(transform, nullptr, nullptr) + artifact.chunk(transform, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(100, 100, 200, 100), Color::black); update(artifact.build()); @@ -491,9 +499,9 @@ TransformationMatrix().translate(5, 5), FloatPoint3D(), transform1); TestPaintArtifact artifact; - artifact.chunk(transform1, nullptr, nullptr) + artifact.chunk(transform1, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 300, 200), Color::white); - artifact.chunk(transform2, nullptr, nullptr) + artifact.chunk(transform2, nullptr, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 300, 200), Color::black); update(artifact.build()); @@ -549,13 +557,13 @@ nullptr, FloatRoundedRect(200, 200, 700, 100), clip1); TestPaintArtifact artifact; - artifact.chunk(nullptr, clip1, nullptr) + artifact.chunk(nullptr, clip1, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::white); - artifact.chunk(nullptr, clip2, nullptr) + artifact.chunk(nullptr, clip2, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::lightGray); - artifact.chunk(nullptr, clip1, nullptr) + artifact.chunk(nullptr, clip1, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::darkGray); - artifact.chunk(nullptr, clip2, nullptr) + artifact.chunk(nullptr, clip2, dummyRootEffect()) .rectDrawing(FloatRect(300, 350, 100, 100), Color::black); update(artifact.build()); @@ -605,7 +613,7 @@ } TestPaintArtifact artifact; - artifact.chunk(nullptr, clips.last(), nullptr) + artifact.chunk(nullptr, clips.last(), dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 200, 200), Color::white); update(artifact.build()); @@ -637,9 +645,9 @@ nullptr, FloatRoundedRect(400, 0, 400, 600), commonClip); TestPaintArtifact artifact; - artifact.chunk(nullptr, clip1, nullptr) + artifact.chunk(nullptr, clip1, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 640, 480), Color::white); - artifact.chunk(nullptr, clip2, nullptr) + artifact.chunk(nullptr, clip2, dummyRootEffect()) .rectDrawing(FloatRect(0, 0, 640, 480), Color::black); update(artifact.build()); @@ -685,5 +693,47 @@ EXPECT_EQ(translation(50, 100), layer->screen_space_transform()); } +TEST_F(PaintArtifactCompositorTestWithPropertyTrees, EffectTreeConversion) +{ + RefPtr<EffectPaintPropertyNode> effect1 = EffectPaintPropertyNode::create(0.5, dummyRootEffect()); + RefPtr<EffectPaintPropertyNode> effect2 = EffectPaintPropertyNode::create(0.3, effect1.get()); + RefPtr<EffectPaintPropertyNode> effect3 = EffectPaintPropertyNode::create(0.2, dummyRootEffect()); + + TestPaintArtifact artifact; + artifact.chunk(nullptr, nullptr, effect2.get()) + .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); + artifact.chunk(nullptr, nullptr, effect1.get()) + .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); + artifact.chunk(nullptr, nullptr, effect3.get()) + .rectDrawing(FloatRect(0, 0, 100, 100), Color::white); + update(artifact.build()); + + ASSERT_EQ(3u, contentLayerCount()); + + const cc::EffectTree& effectTree = propertyTrees().effect_tree; + // Node #0 reserved for null; #1 for root render surface; #2 for dummyRootEffect, + // plus 3 nodes for those created by this test. + ASSERT_EQ(6u, effectTree.size()); + + const cc::EffectNode& convertedDummyRootEffect = *effectTree.Node(2); + EXPECT_EQ(1, convertedDummyRootEffect.parent_id); + + const cc::EffectNode& convertedEffect1 = *effectTree.Node(3); + EXPECT_EQ(convertedDummyRootEffect.id, convertedEffect1.parent_id); + EXPECT_FLOAT_EQ(0.5, convertedEffect1.opacity); + + const cc::EffectNode& convertedEffect2 = *effectTree.Node(4); + EXPECT_EQ(convertedEffect1.id, convertedEffect2.parent_id); + EXPECT_FLOAT_EQ(0.3, convertedEffect2.opacity); + + const cc::EffectNode& convertedEffect3 = *effectTree.Node(5); + EXPECT_EQ(convertedDummyRootEffect.id, convertedEffect3.parent_id); + EXPECT_FLOAT_EQ(0.2, convertedEffect3.opacity); + + EXPECT_EQ(convertedEffect2.id, contentLayerAt(0)->effect_tree_index()); + EXPECT_EQ(convertedEffect1.id, contentLayerAt(1)->effect_tree_index()); + EXPECT_EQ(convertedEffect3.id, contentLayerAt(2)->effect_tree_index()); +} + } // namespace } // namespace blink
diff --git a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp index 330a8473..6594dbe 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp
@@ -314,10 +314,10 @@ v8::Maybe<int> lineNumber = message->GetLineNumber(m_context->context()); if (lineNumber.IsJust()) - exceptionDetailsObject->setLine(lineNumber.FromJust()); + exceptionDetailsObject->setLineNumber(lineNumber.FromJust() - 1); v8::Maybe<int> columnNumber = message->GetStartColumn(m_context->context()); if (columnNumber.IsJust()) - exceptionDetailsObject->setColumn(columnNumber.FromJust()); + exceptionDetailsObject->setColumnNumber(columnNumber.FromJust()); v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace(); if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp index 310e57d..55eff2c4 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp
@@ -95,13 +95,16 @@ m_frontend.messageAdded(std::move(expired)); m_frontend.flush(); } - for (const auto& message : storage->messages()) - reportMessage(message.get(), false); + for (const auto& message : storage->messages()) { + if (message->origin() == V8MessageOrigin::kConsole) + reportMessage(message.get(), false); + } } void V8ConsoleAgentImpl::reportMessage(V8ConsoleMessage* message, bool generatePreview) { - m_frontend.messageAdded(message->buildInspectorObject(m_session, generatePreview)); + DCHECK_EQ(V8MessageOrigin::kConsole, message->origin()); + message->reportToFrontend(&m_frontend, m_session, generatePreview); m_frontend.flush(); }
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp index 3fcdf36..e4c9083 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp
@@ -8,6 +8,7 @@ #include "platform/v8_inspector/V8ConsoleAgentImpl.h" #include "platform/v8_inspector/V8DebuggerImpl.h" #include "platform/v8_inspector/V8InspectorSessionImpl.h" +#include "platform/v8_inspector/V8RuntimeAgentImpl.h" #include "platform/v8_inspector/V8StackTraceImpl.h" #include "platform/v8_inspector/V8StringUtil.h" #include "platform/v8_inspector/public/V8DebuggerClient.h" @@ -61,7 +62,6 @@ case WarningMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::Warning; case ErrorMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::Error; case InfoMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::Info; - case RevokedErrorMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::RevokedError; } return protocol::Console::ConsoleMessage::LevelEnum::Log; } @@ -198,7 +198,7 @@ } // namespace V8ConsoleMessage::V8ConsoleMessage( - double timestampMS, + double timestamp, MessageSource source, MessageLevel level, const String16& message, @@ -208,7 +208,8 @@ std::unique_ptr<V8StackTrace> stackTrace, int scriptId, const String16& requestIdentifier) - : m_timestamp(timestampMS / 1000.0) + : m_origin(V8MessageOrigin::kConsole) + , m_timestamp(timestamp) , m_source(source) , m_level(level) , m_message(message) @@ -220,8 +221,8 @@ , m_requestIdentifier(requestIdentifier) , m_contextId(0) , m_type(LogMessageType) - , m_messageId(0) - , m_relatedMessageId(0) + , m_exceptionId(0) + , m_revokedExceptionId(0) { } @@ -229,14 +230,15 @@ { } -std::unique_ptr<protocol::Console::ConsoleMessage> V8ConsoleMessage::buildInspectorObject(V8InspectorSessionImpl* session, bool generatePreview) const +void V8ConsoleMessage::reportToFrontend(protocol::Console::Frontend* frontend, V8InspectorSessionImpl* session, bool generatePreview) const { + DCHECK_EQ(V8MessageOrigin::kConsole, m_origin); std::unique_ptr<protocol::Console::ConsoleMessage> result = protocol::Console::ConsoleMessage::create() .setSource(messageSourceValue(m_source)) .setLevel(messageLevelValue(m_level)) .setText(m_message) - .setTimestamp(m_timestamp) + .setTimestamp(m_timestamp / 1000) // TODO(dgozman): migrate this to milliseconds. .build(); result->setType(messageTypeValue(m_type)); result->setLine(static_cast<int>(m_lineNumber)); @@ -248,23 +250,21 @@ result->setNetworkRequestId(m_requestIdentifier); if (m_contextId) result->setExecutionContextId(m_contextId); - appendArguments(result.get(), session, generatePreview); + std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> args = wrapArguments(session, generatePreview); + if (args) + result->setParameters(std::move(args)); if (m_stackTrace) result->setStack(m_stackTrace->buildInspectorObject()); - if (m_messageId) - result->setMessageId(m_messageId); - if (m_relatedMessageId) - result->setRelatedMessageId(m_relatedMessageId); - return result; + frontend->messageAdded(std::move(result)); } -void V8ConsoleMessage::appendArguments(protocol::Console::ConsoleMessage* result, V8InspectorSessionImpl* session, bool generatePreview) const +std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> V8ConsoleMessage::wrapArguments(V8InspectorSessionImpl* session, bool generatePreview) const { if (!m_arguments.size() || !m_contextId) - return; + return nullptr; InspectedContext* inspectedContext = session->debugger()->getContext(session->contextGroupId(), m_contextId); if (!inspectedContext) - return; + return nullptr; v8::Isolate* isolate = inspectedContext->isolate(); v8::HandleScope handles(isolate); @@ -289,8 +289,57 @@ args->addItem(std::move(wrapped)); } } - if (args) - result->setParameters(std::move(args)); + return args; +} + +void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend, V8InspectorSessionImpl* session, bool generatePreview) const +{ + if (m_origin == V8MessageOrigin::kException) { + // TODO(dgozman): unify with InjectedScript::createExceptionDetails. + std::unique_ptr<protocol::Runtime::ExceptionDetails> details = protocol::Runtime::ExceptionDetails::create().setText(m_message).build(); + details->setUrl(m_url); + if (m_lineNumber) + details->setLineNumber(static_cast<int>(m_lineNumber) - 1); + if (m_columnNumber) + details->setColumnNumber(static_cast<int>(m_columnNumber) - 1); + if (m_scriptId) + details->setScriptId(String::number(m_scriptId)); + if (m_stackTrace) + details->setStack(m_stackTrace->buildInspectorObject()); + + std::unique_ptr<protocol::Runtime::RemoteObject> exception = wrapException(session, generatePreview); + + if (exception) + frontend->exceptionThrown(m_exceptionId, m_timestamp, std::move(details), std::move(exception), m_contextId); + else + frontend->exceptionThrown(m_exceptionId, m_timestamp, std::move(details)); + return; + } + if (m_origin == V8MessageOrigin::kRevokedException) { + frontend->exceptionRevoked(m_timestamp, m_message, m_revokedExceptionId); + return; + } + NOTREACHED(); +} + +std::unique_ptr<protocol::Runtime::RemoteObject> V8ConsoleMessage::wrapException(V8InspectorSessionImpl* session, bool generatePreview) const +{ + if (!m_arguments.size() || !m_contextId) + return nullptr; + DCHECK_EQ(1u, m_arguments.size()); + InspectedContext* inspectedContext = session->debugger()->getContext(session->contextGroupId(), m_contextId); + if (!inspectedContext) + return nullptr; + + v8::Isolate* isolate = inspectedContext->isolate(); + v8::HandleScope handles(isolate); + // TODO(dgozman): should we use different object group? + return session->wrapObject(inspectedContext->context(), m_arguments[0]->Get(isolate), "console", generatePreview); +} + +V8MessageOrigin V8ConsoleMessage::origin() const +{ + return m_origin; } unsigned V8ConsoleMessage::argumentCount() const @@ -304,7 +353,7 @@ } // static -std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(double timestampMS, MessageType type, MessageLevel level, const String16& messageText, std::vector<v8::Local<v8::Value>>* arguments, std::unique_ptr<V8StackTrace> stackTrace, InspectedContext* context) +std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(double timestamp, MessageType type, MessageLevel level, const String16& messageText, std::vector<v8::Local<v8::Value>>* arguments, std::unique_ptr<V8StackTrace> stackTrace, InspectedContext* context) { String16 url; unsigned lineNumber = 0; @@ -325,7 +374,7 @@ actualMessage = V8ValueStringBuilder::toString(messageArguments.at(0)->Get(context->isolate()), context->isolate()); } - std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestampMS, ConsoleAPIMessageSource, level, actualMessage, url, lineNumber, columnNumber, std::move(stackTrace), 0 /* scriptId */, String16() /* requestIdentifier */)); + std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestamp, ConsoleAPIMessageSource, level, actualMessage, url, lineNumber, columnNumber, std::move(stackTrace), 0 /* scriptId */, String16() /* requestIdentifier */)); message->m_type = type; if (messageArguments.size()) { message->m_contextId = context->contextId(); @@ -336,6 +385,28 @@ return message; } +// static +std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException(double timestamp, const String16& messageText, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId, v8::Isolate* isolate, int contextId, v8::Local<v8::Value> exception, unsigned exceptionId) +{ + std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestamp, JSMessageSource, ErrorMessageLevel, messageText, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, String16() /* requestIdentifier */)); + message->m_exceptionId = exceptionId; + message->m_origin = V8MessageOrigin::kException; + if (contextId && !exception.IsEmpty()) { + message->m_contextId = contextId; + message->m_arguments.push_back(wrapUnique(new v8::Global<v8::Value>(isolate, exception))); + } + return message; +} + +// static +std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException(double timestamp, const String16& messageText, unsigned revokedExceptionId) +{ + std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestamp, JSMessageSource, ErrorMessageLevel, messageText, String16(), 0, 0, nullptr, 0, String16())); + message->m_origin = V8MessageOrigin::kRevokedException; + message->m_revokedExceptionId = revokedExceptionId; + return message; +} + void V8ConsoleMessage::contextDestroyed(int contextId) { if (contextId != m_contextId) @@ -347,25 +418,6 @@ m_arguments.swap(empty); } -void V8ConsoleMessage::assignId(unsigned id) -{ - m_messageId = id; -} - -void V8ConsoleMessage::assignRelatedId(unsigned id) -{ - m_relatedMessageId = id; -} - -void V8ConsoleMessage::addArguments(v8::Isolate* isolate, int contextId, std::vector<v8::Local<v8::Value>>* arguments) -{ - if (!arguments || !contextId) - return; - m_contextId = contextId; - for (size_t i = 0; i < arguments->size(); ++i) - m_arguments.push_back(wrapUnique(new v8::Global<v8::Value>(isolate, arguments->at(i)))); -} - // ------------------------ V8ConsoleMessageStorage ---------------------------- V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8DebuggerImpl* debugger, int contextGroupId) @@ -386,8 +438,12 @@ clear(); V8InspectorSessionImpl* session = m_debugger->sessionForContextGroup(m_contextGroupId); - if (session) - session->consoleAgent()->messageAdded(message.get()); + if (session) { + if (message->origin() == V8MessageOrigin::kConsole) + session->consoleAgent()->messageAdded(message.get()); + else + session->runtimeAgent()->exceptionMessageAdded(message.get()); + } DCHECK(m_messages.size() <= maxConsoleMessageCount); if (m_messages.size() == maxConsoleMessageCount) {
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h index 2f35957..9cdf132 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h +++ b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h
@@ -8,6 +8,7 @@ #include "platform/inspector_protocol/Collections.h" #include "platform/inspector_protocol/String16.h" #include "platform/v8_inspector/protocol/Console.h" +#include "platform/v8_inspector/protocol/Runtime.h" #include "platform/v8_inspector/public/V8ConsoleTypes.h" #include "platform/v8_inspector/public/V8StackTrace.h" #include <deque> @@ -20,10 +21,12 @@ class V8InspectorSessionImpl; class V8StackTrace; +enum class V8MessageOrigin { kConsole, kException, kRevokedException }; + class V8ConsoleMessage { public: V8ConsoleMessage( - double timestampMS, + double timestamp, MessageSource, MessageLevel, const String16& message, @@ -36,7 +39,7 @@ ~V8ConsoleMessage(); static std::unique_ptr<V8ConsoleMessage> createForConsoleAPI( - double timestampMS, + double timestamp, MessageType, MessageLevel, const String16& message, @@ -44,18 +47,37 @@ std::unique_ptr<V8StackTrace>, InspectedContext*); - std::unique_ptr<protocol::Console::ConsoleMessage> buildInspectorObject(V8InspectorSessionImpl*, bool generatePreview) const; + static std::unique_ptr<V8ConsoleMessage> createForException( + double timestamp, + const String16& message, + const String16& url, + unsigned lineNumber, + unsigned columnNumber, + std::unique_ptr<V8StackTrace>, + int scriptId, + v8::Isolate*, + int contextId, + v8::Local<v8::Value> exception, + unsigned exceptionId); + + static std::unique_ptr<V8ConsoleMessage> createForRevokedException( + double timestamp, + const String16& message, + unsigned revokedExceptionId); + + V8MessageOrigin origin() const; + void reportToFrontend(protocol::Console::Frontend*, V8InspectorSessionImpl*, bool generatePreview) const; + void reportToFrontend(protocol::Runtime::Frontend*, V8InspectorSessionImpl*, bool generatePreview) const; unsigned argumentCount() const; MessageType type() const; void contextDestroyed(int contextId); - void assignId(unsigned); - void assignRelatedId(unsigned); - void addArguments(v8::Isolate*, int contextId, std::vector<v8::Local<v8::Value>>*); private: using Arguments = std::vector<std::unique_ptr<v8::Global<v8::Value>>>; - void appendArguments(protocol::Console::ConsoleMessage*, V8InspectorSessionImpl*, bool generatePreview) const; + std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> wrapArguments(V8InspectorSessionImpl*, bool generatePreview) const; + std::unique_ptr<protocol::Runtime::RemoteObject> wrapException(V8InspectorSessionImpl*, bool generatePreview) const; + V8MessageOrigin m_origin; double m_timestamp; MessageSource m_source; MessageLevel m_level; @@ -68,8 +90,8 @@ String16 m_requestIdentifier; int m_contextId; MessageType m_type; - unsigned m_messageId; - unsigned m_relatedMessageId; + unsigned m_exceptionId; + unsigned m_revokedExceptionId; Arguments m_arguments; };
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp index e945af4..a0b0639 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp
@@ -85,7 +85,7 @@ , m_client(client) , m_capturingStackTracesCount(0) , m_muteConsoleCount(0) - , m_lastConsoleMessageId(0) + , m_lastExceptionId(0) , m_enabledAgentsCount(0) , m_breakpointsActivated(true) , m_runningNestedMessageLoop(false) @@ -1029,7 +1029,17 @@ ensureConsoleMessageStorage(contextGroupId)->addMessage(V8ConsoleMessage::createForConsoleAPI(m_client->currentTimeMS(), LogMessageType, LogMessageLevel, message, arguments.size() ? &arguments : nullptr, captureStackTrace(false), inspectedContext)); } -unsigned V8DebuggerImpl::promiseRejected(v8::Local<v8::Context> context, const String16& errorMessage, v8::Local<v8::Value> reason, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId) +void V8DebuggerImpl::exceptionThrown(int contextGroupId, const String16& errorMessage, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId) +{ + if (m_muteConsoleCount || !contextGroupId) + return; + m_client->messageAddedToConsole(contextGroupId, JSMessageSource, ErrorMessageLevel, errorMessage, url, lineNumber, columnNumber, stackTrace.get()); + unsigned exceptionId = ++m_lastExceptionId; + std::unique_ptr<V8ConsoleMessage> consoleMessage = V8ConsoleMessage::createForException(m_client->currentTimeMS(), errorMessage, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, m_isolate, 0, v8::Local<v8::Value>(), exceptionId); + ensureConsoleMessageStorage(contextGroupId)->addMessage(std::move(consoleMessage)); +} + +unsigned V8DebuggerImpl::promiseRejected(v8::Local<v8::Context> context, const String16& errorMessage, v8::Local<v8::Value> exception, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId) { if (m_muteConsoleCount) return 0; @@ -1045,17 +1055,10 @@ message = message.substring(0, 8) + " (in promise)" + message.substring(8); m_client->messageAddedToConsole(contextGroupId, JSMessageSource, ErrorMessageLevel, message, url, lineNumber, columnNumber, stackTrace.get()); - std::unique_ptr<V8ConsoleMessage> consoleMessage = wrapUnique(new V8ConsoleMessage(m_client->currentTimeMS(), JSMessageSource, ErrorMessageLevel, message, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, String16())); - unsigned id = ++m_lastConsoleMessageId; - consoleMessage->assignId(id); - - std::vector<v8::Local<v8::Value>> arguments; - arguments.push_back(toV8String(m_isolate, defaultMessage)); - arguments.push_back(reason); - consoleMessage->addArguments(m_isolate, contextId(context), &arguments); - + unsigned exceptionId = ++m_lastExceptionId; + std::unique_ptr<V8ConsoleMessage> consoleMessage = V8ConsoleMessage::createForException(m_client->currentTimeMS(), message, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, m_isolate, contextId(context), exception, exceptionId); ensureConsoleMessageStorage(contextGroupId)->addMessage(std::move(consoleMessage)); - return id; + return exceptionId; } void V8DebuggerImpl::promiseRejectionRevoked(v8::Local<v8::Context> context, unsigned promiseRejectionId) @@ -1066,10 +1069,7 @@ if (!contextGroupId) return; - const String16 message = "Handler added to rejected promise"; - m_client->messageAddedToConsole(contextGroupId, JSMessageSource, RevokedErrorMessageLevel, message, String16(), 0, 0, nullptr); - std::unique_ptr<V8ConsoleMessage> consoleMessage = wrapUnique(new V8ConsoleMessage(m_client->currentTimeMS(), JSMessageSource, RevokedErrorMessageLevel, message, String16(), 0, 0, nullptr, 0, String16())); - consoleMessage->assignRelatedId(promiseRejectionId); + std::unique_ptr<V8ConsoleMessage> consoleMessage = V8ConsoleMessage::createForRevokedException(m_client->currentTimeMS(), "Handler added to rejected promise", promiseRejectionId); ensureConsoleMessageStorage(contextGroupId)->addMessage(std::move(consoleMessage)); }
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h index 1ce6d5b..a4596af 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h +++ b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h
@@ -131,7 +131,8 @@ void idleFinished() override; bool addConsoleMessage(int contextGroupId, MessageSource, MessageLevel, const String16& message, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId, const String16& requestIdentifier) override; void logToConsole(v8::Local<v8::Context>, const String16& message, v8::Local<v8::Value> arg1, v8::Local<v8::Value> arg2) override; - unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> reason, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) override; + void exceptionThrown(int contextGroupId, const String16& errorMessage, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) override; + unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> exception, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) override; void promiseRejectionRevoked(v8::Local<v8::Context>, unsigned promiseRejectionId) override; void consoleMessagesCount(int contextGroupId, unsigned* total, unsigned* withArguments) override; std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>) override; @@ -186,7 +187,7 @@ ConsoleStorageMap m_consoleStorageMap; int m_capturingStackTracesCount; int m_muteConsoleCount; - unsigned m_lastConsoleMessageId; + unsigned m_lastExceptionId; int m_enabledAgentsCount; bool m_breakpointsActivated; v8::Global<v8::Object> m_debuggerScript;
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp index d32cd3f..86601c1 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp +++ b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp
@@ -34,6 +34,7 @@ #include "platform/v8_inspector/InjectedScript.h" #include "platform/v8_inspector/InspectedContext.h" #include "platform/v8_inspector/RemoteObjectId.h" +#include "platform/v8_inspector/V8ConsoleMessage.h" #include "platform/v8_inspector/V8DebuggerImpl.h" #include "platform/v8_inspector/V8InspectorSessionImpl.h" #include "platform/v8_inspector/V8StringUtil.h" @@ -365,8 +366,13 @@ m_session->changeInstrumentationCounter(+1); m_enabled = true; m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); - v8::HandleScope handles(m_debugger->isolate()); + m_session->debugger()->enableStackCapturingIfNeeded(); m_session->reportAllContexts(this); + V8ConsoleMessageStorage* storage = m_session->debugger()->ensureConsoleMessageStorage(m_session->contextGroupId()); + for (const auto& message : storage->messages()) { + if (message->origin() == V8MessageOrigin::kException || message->origin() == V8MessageOrigin::kRevokedException) + reportMessage(message.get(), false); + } } void V8RuntimeAgentImpl::disable(ErrorString* errorString) @@ -375,6 +381,7 @@ return; m_enabled = false; m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); + m_session->debugger()->disableStackCapturingIfNeeded(); m_session->discardInjectedScripts(); reset(); m_session->changeInstrumentationCounter(-1); @@ -420,4 +427,17 @@ m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints)); } +void V8RuntimeAgentImpl::exceptionMessageAdded(V8ConsoleMessage* message) +{ + if (m_enabled) + reportMessage(message, true); +} + +void V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message, bool generatePreview) +{ + DCHECK(message->origin() == V8MessageOrigin::kException || message->origin() == V8MessageOrigin::kRevokedException); + message->reportToFrontend(&m_frontend, m_session, generatePreview); + m_frontend.flush(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h index 7eb2b135..b8bfceb3 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h +++ b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h
@@ -42,6 +42,7 @@ class InjectedScript; class InspectedContext; class RemoteObjectIdBase; +class V8ConsoleMessage; class V8DebuggerImpl; class V8InspectorSessionImpl; @@ -115,8 +116,11 @@ void reportExecutionContextCreated(InspectedContext*); void reportExecutionContextDestroyed(InspectedContext*); void inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, std::unique_ptr<protocol::DictionaryValue> hints); + void exceptionMessageAdded(V8ConsoleMessage*); private: + void reportMessage(V8ConsoleMessage*, bool generatePreview); + V8InspectorSessionImpl* m_session; protocol::DictionaryValue* m_state; protocol::Runtime::Frontend m_frontend;
diff --git a/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json b/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json index 5a72748d..d01f37c 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json +++ b/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json
@@ -133,13 +133,14 @@ { "id": "ExceptionDetails", "type": "object", + "hidden": true, "description": "Detailed information on exception (or error) that was thrown during script compilation or execution.", "properties": [ { "name": "text", "type": "string", "description": "Exception text." }, { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." }, - { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." }, - { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." }, + { "name": "lineNumber", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message. (0-based)." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message. (0-based)." }, { "name": "stack", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." } ] }, @@ -308,6 +309,28 @@ "description": "Issued when all executionContexts were cleared in browser" }, { + "name": "exceptionThrown", + "description": "Issued when exception was thrown and unhandled.", + "parameters": [ + { "name": "exceptionId", "type": "integer", "description": "Exception id." }, + { "name": "timestamp", "type": "number", "description": "Number of milliseconds since epoch. TODO(dgozman): unify with Console.Timestamp" }, + { "name": "details", "$ref": "ExceptionDetails" }, + { "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." } + ], + "hidden": true + }, + { + "name": "exceptionRevoked", + "description": "Issued when unhandled exception was revoked.", + "parameters": [ + { "name": "timestamp", "type": "number", "description": "Number of milliseconds since epoch. TODO(dgozman): unify with Console.Timestamp" }, + { "name": "message", "type": "string", "description": "Message describing why exception was revoked." }, + { "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in <code>exceptionUnhandled</code>." } + ], + "hidden": true + }, + { "name": "inspectRequested", "parameters": [ { "name": "object", "$ref": "RemoteObject" }, @@ -719,7 +742,7 @@ "description": "Console message.", "properties": [ { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation"], "description": "Message source." }, - { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info", "revokedError"], "description": "Message severity." }, + { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." }, { "name": "text", "type": "string", "description": "Message text." }, { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Console message type." }, { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." }, @@ -731,9 +754,7 @@ { "name": "stack", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." }, { "name": "networkRequestId", "type": "string", "optional": true, "description": "Identifier of the network request associated with this message." }, { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp, when this message was fired.", "hidden": true }, - { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Identifier of the context where this message was created", "hidden": true }, - { "name": "messageId", "type": "integer", "hidden": true, "optional": true, "description": "Message id." }, - { "name": "relatedMessageId", "type": "integer", "hidden": true, "optional": true, "description": "Related message id." } + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Identifier of the context where this message was created", "hidden": true } ] } ],
diff --git a/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h b/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h index 82f1a2b..75f26fe2 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h +++ b/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h
@@ -25,10 +25,10 @@ LogMessageLevel = 1, InfoMessageLevel = 5, WarningMessageLevel = 2, - ErrorMessageLevel = 3, - RevokedErrorMessageLevel = 6 + ErrorMessageLevel = 3 }; +// TODO(dgozman): move from public to private. enum MessageType { LogMessageType = 1, DirMessageType,
diff --git a/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h b/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h index 5ac5e66..c0ebce29 100644 --- a/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h +++ b/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h
@@ -43,7 +43,9 @@ virtual bool addConsoleMessage(int contextGroupId, MessageSource, MessageLevel, const String16& message, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId, const String16& requestIdentifier) = 0; // TODO(dgozman): can we remove this method? virtual void logToConsole(v8::Local<v8::Context>, const String16& message, v8::Local<v8::Value> arg1, v8::Local<v8::Value> arg2) = 0; - virtual unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> reason, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) = 0; + // TODO(dgozman): can we pass exception object? + virtual void exceptionThrown(int contextGroupId, const String16& errorMessage, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) = 0; + virtual unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> exception, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) = 0; virtual void promiseRejectionRevoked(v8::Local<v8::Context>, unsigned promiseRejectionId) = 0; virtual void consoleMessagesCount(int contextGroupId, unsigned* total, unsigned* withArguments) = 0; // TODO(dgozman): remove mute methods.
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp index c8a165a..a4a1f40 100644 --- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp +++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -766,7 +766,6 @@ break; // Unsupported values. case WebConsoleMessage::LevelInfo: - case WebConsoleMessage::LevelRevokedError: break; }
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py index 4fe23f3..5db74a0 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py
@@ -159,3 +159,6 @@ def unexpected_mismatch_results(self): return self._filter_tests(lambda r: r.has_mismatch_result() and not r.did_run_as_expected()) + + def didnt_run_as_expected_results(self): + return self._filter_tests(lambda r: not r.did_run_as_expected())
diff --git a/third_party/WebKit/public/web/WebConsoleMessage.h b/third_party/WebKit/public/web/WebConsoleMessage.h index d0a109c..2c2c062 100644 --- a/third_party/WebKit/public/web/WebConsoleMessage.h +++ b/third_party/WebKit/public/web/WebConsoleMessage.h
@@ -42,7 +42,6 @@ LevelInfo = 5, LevelWarning = 2, LevelError = 3, - LevelRevokedError = 6, LevelLast = LevelInfo };