diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 90ceb32fc..5f8e810f 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -2418,7 +2418,7 @@
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-018.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-019.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-020.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-021.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-021.html [ Crash Failure ]
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-022.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-023.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-shapes/shape-outside/shape-image/shape-image-025.html [ Failure ]
@@ -2496,7 +2496,7 @@
 crbug.com/591099 external/wpt/css/css-shapes/spec-examples/shape-outside-019.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-tables/height-distribution/percentage-sizing-of-table-cell-children.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-tables/html5-table-formatting-fixed-layout-1.html [ Crash Pass ]
-crbug.com/591099 external/wpt/css/css-tables/table-model-fixup-2.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-tables/table-model-fixup-2.html [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-tables/visibility-collapse-rowcol-001.html [ Crash Pass ]
 crbug.com/591099 external/wpt/css/css-tables/visibility-collapse-rowspan-crash.html [ Crash Pass ]
 crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-color-001.xht [ Crash ]
@@ -2651,7 +2651,7 @@
 crbug.com/591099 external/wpt/editing/run/formatblock.html [ Timeout ]
 crbug.com/591099 external/wpt/editing/run/forwarddelete.html [ Timeout ]
 crbug.com/591099 external/wpt/editing/run/hilitecolor.html [ Timeout ]
-crbug.com/591099 external/wpt/editing/run/indent.html [ Timeout ]
+crbug.com/591099 external/wpt/editing/run/indent.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/editing/run/inserthorizontalrule.html [ Crash Pass Timeout ]
 crbug.com/591099 external/wpt/editing/run/inserthtml.html [ Timeout ]
 crbug.com/591099 external/wpt/editing/run/insertlinebreak.html [ Timeout ]
@@ -5005,6 +5005,7 @@
 crbug.com/714962 compositing/images/direct-image-clip-path.html [ Failure ]
 crbug.com/714962 compositing/images/direct-image-dynamic-clip-path.html [ Failure ]
 crbug.com/714962 compositing/overflow/absolute-element-in-isolated-composited-ancestor.html [ Failure ]
+crbug.com/591099 compositing/overflow/border-radius-above-composited-subframe.html [ Failure ]
 crbug.com/591099 compositing/overflow/border-radius-composited-subframe.html [ Failure ]
 crbug.com/591099 compositing/overflow/border-radius-on-squashed-layers.html [ Failure Pass ]
 crbug.com/591099 compositing/overflow/composited-scroll-with-fractional-translation.html [ Pass ]
@@ -5176,9 +5177,9 @@
 crbug.com/714962 external/wpt/css/CSS2/text/text-indent-overflow-004.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-color-5.html [ Failure ]
 crbug.com/714962 external/wpt/css/css-backgrounds/background-attachment-local/attachment-local-clipping-image-5.html [ Failure ]
-crbug.com/714962 external/wpt/css/css-backgrounds/background-image-003.html [ Failure ]
+crbug.com/714962 external/wpt/css/css-backgrounds/background-image-003.html [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-backgrounds/background-image-004.html [ Failure ]
-crbug.com/714962 external/wpt/css/css-backgrounds/background-image-005.html [ Failure ]
+crbug.com/714962 external/wpt/css/css-backgrounds/background-image-005.html [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-backgrounds/background-image-006.html [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-backgrounds/background-size/background-size-cover.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-backgrounds/css3-background-clip.html [ Failure ]
@@ -5331,6 +5332,7 @@
 crbug.com/591099 external/wpt/css/css-multicol/multicol-width-001.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-width-002.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-width-003.xht [ Failure ]
+crbug.com/591099 external/wpt/css/css-multicol/multicol-width-ch-001.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-width-count-001.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-multicol/multicol-width-count-002.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-multicol/multicol-width-large-001.xht [ Failure ]
@@ -5353,6 +5355,8 @@
 crbug.com/714962 external/wpt/css/css-position/position-sticky-top.html [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-scroll-anchoring/start-edge-in-block-layout-direction.html [ Failure ]
 crbug.com/714962 external/wpt/css/css-scroll-anchoring/wrapped-text.html [ Failure ]
+crbug.com/591099 external/wpt/css/css-style-attr/style-attr-urls-001.xht [ Failure ]
+crbug.com/591099 external/wpt/css/css-style-attr/style-attr-urls-002.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-tables/visibility-collapse-colspan-003.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-above-left-001.xht [ Crash ]
 crbug.com/591099 external/wpt/css/css-text-decor/text-emphasis-position-above-right-001.xht [ Crash ]
@@ -5392,7 +5396,7 @@
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-003.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-005.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-007.xht [ Failure Pass ]
-crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-009.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-009.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-011.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-013.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-015.xht [ Failure ]
@@ -5419,7 +5423,7 @@
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-057.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-059.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-061.xht [ Failure ]
-crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-063.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-063.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-065.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-067.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-069.xht [ Failure ]
@@ -5439,7 +5443,7 @@
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-097.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-103.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-105.xht [ Failure Pass ]
-crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-107.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-107.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-109.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-111.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-113.xht [ Failure Pass ]
@@ -5471,11 +5475,11 @@
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-165.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-167.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-169.xht [ Failure Pass ]
-crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-171.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-171.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-173.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-175.xht [ Failure ]
-crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-177.xht [ Failure ]
-crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-179.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-177.xht [ Failure Pass ]
+crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-179.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-181.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-183.xht [ Failure Pass ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vlr-185.xht [ Failure Pass ]
@@ -5596,7 +5600,7 @@
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-190.xht [ Failure ]
 crbug.com/591099 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-192.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-194.xht [ Failure ]
-crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-196.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-196.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-198.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-200.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/abs-pos-non-replaced-vrl-202.xht [ Failure ]
@@ -5683,14 +5687,14 @@
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vlr-011.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vlr-013.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vlr-015.xht [ Failure ]
-crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vlr-017.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vlr-017.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vlr-019.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-002.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-004.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-006.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-008.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-010.xht [ Failure ]
-crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-012.xht [ Failure ]
+crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-012.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-014.xht [ Failure Pass ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-016.xht [ Failure ]
 crbug.com/714962 external/wpt/css/css-writing-modes/text-align-vrl-018.xht [ Failure ]
@@ -5801,6 +5805,8 @@
 crbug.com/591099 external/wpt/html/semantics/selectors/pseudo-classes/readwrite-readonly.html [ Crash ]
 crbug.com/714962 external/wpt/intersection-observer/root-margin.html [ Failure ]
 crbug.com/591099 external/wpt/mediacapture-streams/MediaStreamTrack-getSettings.https.html [ Pass ]
+crbug.com/591099 external/wpt/mimesniff/mime-types/parsing.any.html [ Timeout ]
+crbug.com/591099 external/wpt/mimesniff/mime-types/parsing.any.worker.html [ Timeout ]
 crbug.com/591099 external/wpt/offscreen-canvas/the-offscreen-canvas/offscreencanvas.getcontext.worker.html [ Pass ]
 crbug.com/714962 external/wpt/pointerevents/pointerevent_capture_mouse-manual.html [ Timeout ]
 crbug.com/714962 external/wpt/pointerevents/pointerevent_capture_suppressing_mouse-manual.html [ Timeout ]
@@ -5899,7 +5905,7 @@
 crbug.com/714962 fast/borders/border-radius-inline-flow.html [ Failure ]
 crbug.com/714962 fast/borders/border-radius-percent.html [ Failure ]
 crbug.com/714962 fast/borders/border-radius-split-inline.html [ Failure ]
-crbug.com/714962 fast/borders/border-radius-with-composited-child.html [ Failure ]
+crbug.com/714962 fast/borders/border-radius-with-composited-child.html [ Failure Pass ]
 crbug.com/714962 fast/borders/border-styles-split.html [ Failure ]
 crbug.com/714962 fast/borders/borderRadiusArcs01.html [ Failure ]
 crbug.com/714962 fast/borders/borderRadiusDouble01.html [ Failure ]
@@ -6094,7 +6100,7 @@
 crbug.com/714962 fast/events/input-element-display-none-in-dragleave-crash.html [ Failure ]
 crbug.com/714962 fast/events/inputevents/inputevent-drag-drop.html [ Failure ]
 crbug.com/714962 fast/events/middleClickAutoscroll-click-hyperlink.html [ Timeout ]
-crbug.com/714962 fast/events/middleClickAutoscroll-latching.html [ Timeout ]
+crbug.com/714962 fast/events/middleClickAutoscroll-latching.html [ Pass Timeout ]
 crbug.com/714962 fast/events/mouse-down-on-pseudo-element-remove-crash.html [ Failure ]
 crbug.com/714962 fast/events/mouse-events-on-textarea-resize.html [ Failure ]
 crbug.com/591099 fast/events/mouse-relative-position.html [ Failure ]
@@ -6625,7 +6631,7 @@
 crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-iframe-inset-negative-width-crash.html [ Failure ]
 crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-iframe-inset-rectangle-negative-width-crash.html [ Failure ]
 crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-image-001.html [ Failure ]
-crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-image-002.html [ Failure ]
+crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-image-002.html [ Crash Failure ]
 crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-image-margin-001.html [ Failure ]
 crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-image-margin-002.html [ Failure ]
 crbug.com/591099 fast/shapes/shape-outside-floats/shape-outside-floats-image-threshold-001.html [ Failure ]
@@ -6786,6 +6792,7 @@
 crbug.com/591099 fast/table/change-row-border-width-floating-container.html [ Failure ]
 crbug.com/591099 fast/table/change-table-border-width.html [ Failure ]
 crbug.com/591099 fast/table/change-tbody-border-width.html [ Failure ]
+crbug.com/591099 fast/table/collapsed-border-overflow-hidden.html [ Failure ]
 crbug.com/591099 fast/table/column-in-inline.html [ Failure ]
 crbug.com/591099 fast/table/convert-inline-to-table-cell.html [ Failure Pass ]
 crbug.com/591099 fast/table/dynamic-descendant-percentage-height.html [ Failure ]
@@ -7492,7 +7499,7 @@
 crbug.com/591099 http/tests/devtools/elements/event-listener-sidebar-jquery2.js [ Crash Failure ]
 crbug.com/591099 http/tests/devtools/elements/event-listener-sidebar-remove.js [ Crash Pass ]
 crbug.com/591099 http/tests/devtools/elements/event-listener-sidebar.js [ Crash Failure ]
-crbug.com/714962 http/tests/devtools/elements/hide-shortcut.js [ Crash ]
+crbug.com/714962 http/tests/devtools/elements/hide-shortcut.js [ Crash Pass ]
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-css-shapes-outside-scroll.js [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-css-shapes-outside.js [ Failure ]
 crbug.com/714962 http/tests/devtools/elements/highlight/highlight-dom-updates.js [ Crash ]
@@ -7500,7 +7507,7 @@
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-node-scaled.js [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-node-scroll.js [ Failure ]
 crbug.com/591099 http/tests/devtools/elements/highlight/highlight-node.js [ Failure ]
-crbug.com/714962 http/tests/devtools/elements/iframe-load-event.js [ Crash ]
+crbug.com/714962 http/tests/devtools/elements/iframe-load-event.js [ Crash Pass ]
 crbug.com/714962 http/tests/devtools/elements/insert-node.js [ Crash ]
 crbug.com/714962 http/tests/devtools/elements/inspect-pseudo-element.js [ Timeout ]
 crbug.com/714962 http/tests/devtools/elements/modify-chardata.js [ Crash ]
@@ -7512,7 +7519,7 @@
 crbug.com/714962 http/tests/devtools/elements/styles-1/add-new-rule-with-style-after-body.js [ Crash Pass ]
 crbug.com/714962 http/tests/devtools/elements/styles-1/edit-value-url-with-color.js [ Crash ]
 crbug.com/714962 http/tests/devtools/elements/styles-2/force-pseudo-state.js [ Crash ]
-crbug.com/714962 http/tests/devtools/elements/styles-2/paste-property.js [ Crash ]
+crbug.com/714962 http/tests/devtools/elements/styles-2/paste-property.js [ Crash Pass ]
 crbug.com/714962 http/tests/devtools/elements/styles-2/pseudo-elements.js [ Crash ]
 crbug.com/591099 http/tests/devtools/elements/styles-3/style-rule-from-imported-stylesheet.js [ Crash Pass ]
 crbug.com/714962 http/tests/devtools/elements/styles-3/styles-add-blank-property.js [ Crash ]
@@ -7537,6 +7544,7 @@
 crbug.com/591099 http/tests/devtools/extensions/extensions-sidebar.js [ Crash Failure ]
 crbug.com/591099 http/tests/devtools/extensions/extensions-timeline-api.js [ Failure Pass ]
 crbug.com/591099 http/tests/devtools/indexeddb/resources-panel.js [ Failure Timeout ]
+crbug.com/591099 http/tests/devtools/inspect-element.js [ Crash Pass ]
 crbug.com/714962 http/tests/devtools/jump-to-previous-editing-location.js [ Failure ]
 crbug.com/714962 http/tests/devtools/layers/layer-canvas-log.js [ Failure ]
 crbug.com/591099 http/tests/devtools/network/network-columns-visible.js [ Failure Timeout ]
@@ -7554,7 +7562,7 @@
 crbug.com/591099 http/tests/devtools/sources/debugger/live-edit-no-reveal.js [ Failure Pass ]
 crbug.com/591099 http/tests/devtools/sources/debugger/properties-special.js [ Failure ]
 crbug.com/591099 http/tests/devtools/startup/console/console-format-startup.html [ Failure ]
-crbug.com/591099 http/tests/devtools/startup/reattach-after-editing-styles.html [ Crash ]
+crbug.com/591099 http/tests/devtools/startup/reattach-after-editing-styles.html [ Crash Pass ]
 crbug.com/591099 http/tests/devtools/text-autosizing-override.js [ Failure ]
 crbug.com/714962 http/tests/devtools/tracing/scroll-invalidations.js [ Failure ]
 crbug.com/591099 http/tests/devtools/tracing/timeline-misc/timeline-bound-function.js [ Crash Failure ]
@@ -9240,7 +9248,7 @@
 crbug.com/714962 svg/clip-path/nested-clip-in-mask-image-based-clipping.svg [ Failure ]
 crbug.com/714962 svg/clip-path/nested-clip-in-mask-path-and-image-based-clipping.svg [ Crash Failure ]
 crbug.com/714962 svg/clip-path/nested-clip-in-mask-path-based-clipping.svg [ Failure ]
-crbug.com/714962 svg/clip-path/nested-empty-clip.html [ Failure ]
+crbug.com/714962 svg/clip-path/nested-empty-clip.html [ Crash Failure ]
 crbug.com/714962 svg/clip-path/opacity-assertion.svg [ Failure ]
 crbug.com/714962 svg/clip-path/transformed-clip.svg [ Failure ]
 crbug.com/714962 svg/clip-path/visible-clip-path-as-hidden-use-element.html [ Failure ]
@@ -10263,6 +10271,8 @@
 crbug.com/591099 virtual/spv175/compositing/columns/composited-in-paginated.html [ Failure ]
 crbug.com/714962 virtual/spv175/compositing/composited-descendant-grandparent-border-radius-mask.html [ Pass ]
 crbug.com/714962 virtual/spv175/compositing/composited-descendant-requiring-border-radius-mask.html [ Pass ]
+crbug.com/591099 virtual/spv175/compositing/composited-scaled-child-with-border-radius-parent-clip.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/composited-translated-child-with-border-radius-parent-clip.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/compositing-visible-descendant.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/contents-opaque/background-clip.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/contents-opaque/background-color.html [ Failure ]
@@ -10461,15 +10471,24 @@
 crbug.com/591099 virtual/spv175/compositing/masks/multiple-masks.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/masks/simple-composited-mask.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/nested-border-radius-composited-child.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/opacity-with-mask.html [ Failure ]
 crbug.com/714962 virtual/spv175/compositing/overflow/absolute-element-in-isolated-composited-ancestor.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective.html [ Failure ]
 crbug.com/714962 virtual/spv175/compositing/overflow/accelerated-scrolling-with-clip-reference.html [ Pass ]
 crbug.com/591099 virtual/spv175/compositing/overflow/ancestor-overflow.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-above-composited-subframe.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-composited-subframe.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-on-grandparent-composited-grandchild.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-on-parent-composited-grandchild.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-on-squashed-layers.html [ Failure Pass ]
+crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-on-two-ancestors-composited-grandchild.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-outside-bounds-of-compositing-ancestor.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/border-radius-styles-with-composited-child.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/clear-scroll-parent.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/clip-descendents.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/clip-parent-reset.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/clipping-ancestor-with-accelerated-scrolling-ancestor.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/composited-layer-under-border-radius-under-composited-layer.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/composited-nested-sticky-left.html [ Failure Pass ]
 crbug.com/591099 virtual/spv175/compositing/overflow/composited-scrolling-paint-phases.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/content-gains-scrollbars.html [ Failure ]
@@ -10479,6 +10498,8 @@
 crbug.com/591099 virtual/spv175/compositing/overflow/fixed-position-ancestor-clip.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/fractional-sized-scrolling-layer.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/get-transform-from-non-box-container.html [ Crash ]
+crbug.com/591099 virtual/spv175/compositing/overflow/grandchild-composited-with-border-radius-ancestor.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/grandchild-with-border-radius-ancestor.html [ Failure ]
 crbug.com/714962 virtual/spv175/compositing/overflow/image-load-overflow-scrollbars.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/mask-with-filter.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/mask-with-small-content-rect.html [ Failure ]
@@ -10510,8 +10531,12 @@
 crbug.com/591099 virtual/spv175/compositing/overflow/scrollbar-layer-placement.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/scrollbar-painting.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/scrolling-content-clip-to-viewport.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/siblings-composited-with-border-radius-ancestor-one-clipped.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/siblings-composited-with-border-radius-ancestor.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/siblings-with-border-radius-ancestor.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/textarea-scroll-touch.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/theme-affects-visual-overflow.html [ Failure ]
+crbug.com/591099 virtual/spv175/compositing/overflow/tiled-mask.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/universal-accelerated-overflow-scroll.html [ Failure Timeout ]
 crbug.com/591099 virtual/spv175/compositing/overflow/update-widget-positions-on-nested-frames-and-scrollers.html [ Failure ]
 crbug.com/591099 virtual/spv175/compositing/overflow/zero-size-overflow.html [ Failure ]
diff --git a/third_party/WebKit/Source/core/css/CSSSelector.cpp b/third_party/WebKit/Source/core/css/CSSSelector.cpp
index 307da292..f170482 100644
--- a/third_party/WebKit/Source/core/css/CSSSelector.cpp
+++ b/third_party/WebKit/Source/core/css/CSSSelector.cpp
@@ -541,14 +541,8 @@
         pseudo_type_ = kPseudoUnknown;
       break;
     case kPseudoShadow:
-      if (RuntimeEnabledFeatures::
-              ShadowPseudoElementInCSSDynamicProfileEnabled()) {
-        if (match_ != kPseudoElement)
-          pseudo_type_ = kPseudoUnknown;
-      } else {
-        if (match_ != kPseudoElement || context.IsDynamicProfile())
-          pseudo_type_ = kPseudoUnknown;
-      }
+      if (match_ != kPseudoElement || context.IsDynamicProfile())
+        pseudo_type_ = kPseudoUnknown;
       break;
     case kPseudoBlinkInternalElement:
       if (match_ != kPseudoElement || mode != kUASheetMode)
diff --git a/third_party/WebKit/Source/core/css/SelectorChecker.cpp b/third_party/WebKit/Source/core/css/SelectorChecker.cpp
index 7cd5a7d..80e92b5e 100644
--- a/third_party/WebKit/Source/core/css/SelectorChecker.cpp
+++ b/third_party/WebKit/Source/core/css/SelectorChecker.cpp
@@ -333,8 +333,6 @@
 
   switch (relation) {
     case CSSSelector::kShadowDeepAsDescendant:
-      DCHECK(
-          !RuntimeEnabledFeatures::DeepCombinatorInCSSDynamicProfileEnabled());
       Deprecation::CountDeprecation(context.element->GetDocument(),
                                     WebFeature::kCSSDeepCombinator);
     // fall through
@@ -416,15 +414,10 @@
 
     case CSSSelector::kShadowPseudo: {
       if (!is_ua_rule_ &&
-          context.selector->GetPseudoType() == CSSSelector::kPseudoShadow) {
-        if (mode_ == kQueryingRules) {
-          UseCounter::Count(context.element->GetDocument(),
-                            WebFeature::kPseudoShadowInStaticProfile);
-        } else if (RuntimeEnabledFeatures::
-                       ShadowPseudoElementInCSSDynamicProfileEnabled()) {
-          Deprecation::CountDeprecation(context.element->GetDocument(),
-                                        WebFeature::kCSSSelectorPseudoShadow);
-        }
+          context.selector->GetPseudoType() == CSSSelector::kPseudoShadow &&
+          mode_ == kQueryingRules) {
+        UseCounter::Count(context.element->GetDocument(),
+                          WebFeature::kPseudoShadowInStaticProfile);
       }
       // If we're in the same tree-scope as the scoping element, then following
       // a shadow descendant combinator would escape that and thus the scope.
diff --git a/third_party/WebKit/Source/core/css/StyleEngineTest.cpp b/third_party/WebKit/Source/core/css/StyleEngineTest.cpp
index b67108f..d13d1d8 100644
--- a/third_party/WebKit/Source/core/css/StyleEngineTest.cpp
+++ b/third_party/WebKit/Source/core/css/StyleEngineTest.cpp
@@ -689,14 +689,6 @@
   EXPECT_EQ(ScheduleInvalidationsForRules(
                 *shadow_root, ".a ::content span { background: green}"),
             kRuleSetInvalidationFullRecalc);
-  if (RuntimeEnabledFeatures::DeepCombinatorInCSSDynamicProfileEnabled()) {
-    EXPECT_EQ(ScheduleInvalidationsForRules(
-                  *shadow_root, ".a /deep/ span { background: green}"),
-              kRuleSetInvalidationFullRecalc);
-    EXPECT_EQ(ScheduleInvalidationsForRules(
-                  *shadow_root, ".a::shadow span { background: green}"),
-              kRuleSetInvalidationFullRecalc);
-  }
 }
 
 TEST_F(StyleEngineTest, HasViewportDependentMediaQueries) {
diff --git a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
index 962f5802a..53a807a 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
@@ -1,3 +1,4 @@
+
 // Copyright 2014 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
@@ -644,9 +645,6 @@
       const CSSParserToken& slash = range.ConsumeIncludingWhitespace();
       if (slash.GetType() != kDelimiterToken || slash.Delimiter() != '/')
         failed_parsing_ = true;
-      if (RuntimeEnabledFeatures::DeepCombinatorInCSSDynamicProfileEnabled()) {
-        return CSSSelector::kShadowDeep;
-      }
       return context_->IsDynamicProfile() ? CSSSelector::kShadowDeepAsDescendant
                                           : CSSSelector::kShadowDeep;
     }
diff --git a/third_party/WebKit/Source/platform/runtime_enabled_features.json5 b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
index 6859246..0106752 100644
--- a/third_party/WebKit/Source/platform/runtime_enabled_features.json5
+++ b/third_party/WebKit/Source/platform/runtime_enabled_features.json5
@@ -315,11 +315,6 @@
       name: "DecodeToYUV",
       status: "experimental",
     },
-    // Remove this flag once we can remove /deep/ at M63.
-    {
-      name: "DeepCombinatorInCSSDynamicProfile",
-      status: "test",
-    },
     {
       name: "DeprecationReporting",
       status: "experimental",
@@ -956,11 +951,6 @@
       name: "ShadowPiercingDescendantCombinator",
       status: "experimental",
     },
-    // Remove this flag once we can remove ::shadow at M63.
-    {
-      name: "ShadowPseudoElementInCSSDynamicProfile",
-      status: "test",
-    },
     {
       name: "ShapeDetection",
       status: "experimental",
diff --git a/third_party/WebKit/Source/platform/wtf/Functional.h b/third_party/WebKit/Source/platform/wtf/Functional.h
index e70c89c3..b42729e 100644
--- a/third_party/WebKit/Source/platform/wtf/Functional.h
+++ b/third_party/WebKit/Source/platform/wtf/Functional.h
@@ -49,44 +49,24 @@
 // arguments together into a function object that can be stored, copied and
 // invoked, similar to boost::bind and std::bind in C++11.
 
-// ***************************************************************************
-// ** NOTICE: same-thread WTF::Function is being migrated to base::Callback **
-// ***************************************************************************
+// To create a same-thread callback, use WTF::Bind() or WTF::BindRepeating().
+// Use the former to create a callback that's called only once, and use the
+// latter for a callback that may be called multiple times.
 //
-// We are in the process of replace same-thread WTF::Function with
-// base::{Once,Repeating}Callback (crbug.com/771087).
-//
-// One of the most important implications of this is that we'll need to
-// distinguish a callback function that's only used once from one that may be
-// called multiple times.
-//
-// Use the following guide to determine the correct usage for your use case
-// (note this guideline may change as the migration effort progresses):
-//
-// 1. To create a callback that may be used or destructed in another thread
-//
-// Use CrossThreadBind() (defined in platform/CrossThreadFunctional.h), and
-// receive the returned callback as WTF::CrossThreadFunction. There is no
-// distinction of Once and Repeating for cross-thread usage for now.
-//
-// 2. To create a same-thread callback that's called only once
-//
-// Use WTF::Bind(), and receive the returned callback as base::OnceCallback
-// or base::OnceClosure. Read //docs/callback.md for how to use them.
-//
-// 3. To create a same-thread callback that may be called multiple times
-//
-// Use WTF::BindRepeating(), and receive the returned callback as
-// base::RepeatingCallback or base::RepeatingClosure (WTF::RepeatingFunction
-// is gone).
+// WTF::Bind() and WTF::BindRepeating() returns base::OnceCallback and
+// base::RepeatingCallback respectively. See //docs/callback.md for how to use
+// those types.
 
 // Thread Safety:
 //
-// WTF::Bind() and WTF::Closure should be used for same-thread closures
-// only, i.e. the closures must be created, executed and destructed on
-// the same thread.
+// WTF::Bind(), WTF::BindRepeating and base::{Once,Repeating}Callback should
+// be used for same-thread closures only, i.e. the closures must be created,
+// executed and destructed on the same thread.
 // Use crossThreadBind() and CrossThreadClosure if the function/task is called
 // or destructed on a (potentially) different thread from the current thread.
+//
+// Currently, WTF::CrossThreadClosure does not distinguish Once and Repeating
+// usage.
 
 // WTF::Bind() / WTF::BindRepeating() and move semantics
 // =====================================================
@@ -96,12 +76,13 @@
 //     1) Pass by rvalue reference.
 //
 //            void YourFunction(Argument&& argument) { ... }
-//            Function<void(Argument&&)> functor = Bind(&YourFunction);
+//            base::OnceCallback<void(Argument&&)> functor =
+//                Bind(&YourFunction);
 //
 //     2) Pass by value.
 //
 //            void YourFunction(Argument argument) { ... }
-//            Function<void(Argument)> functor = Bind(&YourFunction);
+//            base::OnceCallback<void(Argument)> functor = Bind(&YourFunction);
 //
 // Note that with the latter there will be *two* move constructions happening,
 // because there needs to be at least one intermediary function call taking an
@@ -124,7 +105,7 @@
 //     }
 //
 //     ...
-//     Function<void()> functor = Bind(&YourFunction, WTF::Passed(Argument()));
+//     base::OnceClosure functor = Bind(&YourFunction, WTF::Passed(Argument()));
 //     ...
 //     std::move(functor).Run();
 //
@@ -313,9 +294,6 @@
 #endif
 
 template <typename Signature>
-using Function = base::OnceCallback<Signature>;
-
-template <typename Signature>
 class CrossThreadFunction;
 
 template <typename R, typename... Args>
@@ -358,9 +336,8 @@
 // Note: now there is WTF::Bind()and WTF::BindRepeating(). See the comment block
 // above for the correct usage of those.
 template <typename FunctionType, typename... BoundParameters>
-Function<base::MakeUnboundRunType<FunctionType, BoundParameters...>> Bind(
-    FunctionType function,
-    BoundParameters&&... bound_parameters) {
+base::OnceCallback<base::MakeUnboundRunType<FunctionType, BoundParameters...>>
+Bind(FunctionType function, BoundParameters&&... bound_parameters) {
   static_assert(internal::CheckGCedTypeRestrictions<
                     std::index_sequence_for<BoundParameters...>,
                     std::decay_t<BoundParameters>...>::ok,
@@ -399,9 +376,6 @@
   return cb;
 }
 
-// TODO(yutak): Replace WTF::Function with base::OnceCallback.
-using Closure = Function<void()>;
-
 template <typename T>
 using CrossThreadRepeatingFunction = CrossThreadFunction<T>;
 using CrossThreadRepeatingClosure = CrossThreadFunction<void()>;
@@ -443,7 +417,6 @@
 
 using WTF::CrossThreadUnretained;
 
-using WTF::Function;
 using WTF::CrossThreadFunction;
 using WTF::CrossThreadClosure;
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index ce795c0e..a1240c7 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -42269,6 +42269,7 @@
   <int value="26372521" label="sync_attachment_downloader"/>
   <int value="27071967" label="chrome_cleaner"/>
   <int value="27915688" label="oauth2_access_token_fetcher"/>
+  <int value="28406789" label="undefined-656607"/>
   <int value="29865866" label="headless_url_request"/>
   <int value="30913825" label="family_info"/>
   <int value="32030464" label="persist_blob_to_indexed_db"/>
diff --git a/tools/traffic_annotation/auditor/BUILD.gn b/tools/traffic_annotation/auditor/BUILD.gn
index fe86490..f4b33ba 100644
--- a/tools/traffic_annotation/auditor/BUILD.gn
+++ b/tools/traffic_annotation/auditor/BUILD.gn
@@ -56,6 +56,8 @@
     "traffic_annotation_exporter.h",
     "traffic_annotation_file_filter.cc",
     "traffic_annotation_file_filter.h",
+    "traffic_annotation_id_checker.cc",
+    "traffic_annotation_id_checker.h",
   ]
   data = [
     "safe_list.txt",
diff --git a/tools/traffic_annotation/auditor/auditor_result.cc b/tools/traffic_annotation/auditor/auditor_result.cc
index e59bab7..e09f10c2 100644
--- a/tools/traffic_annotation/auditor/auditor_result.cc
+++ b/tools/traffic_annotation/auditor/auditor_result.cc
@@ -19,14 +19,15 @@
          type == AuditorResult::Type::RESULT_OK ||
          type == AuditorResult::Type::RESULT_IGNORE ||
          type == AuditorResult::Type::ERROR_FATAL ||
-         type == AuditorResult::Type::ERROR_DUPLICATE_UNIQUE_ID_HASH_CODE ||
+         type == AuditorResult::Type::ERROR_HASH_CODE_COLLISION ||
+         type == AuditorResult::Type::ERROR_REPEATED_ID ||
          type == AuditorResult::Type::ERROR_MERGE_FAILED ||
          type == AuditorResult::Type::ERROR_ANNOTATIONS_XML_UPDATE);
   DCHECK(!message.empty() || type == AuditorResult::Type::RESULT_OK ||
          type == AuditorResult::Type::RESULT_IGNORE ||
          type == AuditorResult::Type::ERROR_MISSING_TAG_USED ||
          type == AuditorResult::Type::ERROR_NO_ANNOTATION ||
-         type == AuditorResult::Type::ERROR_MISSING_EXTRA_ID ||
+         type == AuditorResult::Type::ERROR_MISSING_SECOND_ID ||
          type == AuditorResult::Type::ERROR_INCOMPLETED_ANNOTATION ||
          type == AuditorResult::Type::ERROR_DIRECT_ASSIGNMENT);
   if (!message.empty())
@@ -77,31 +78,38 @@
                                 flat_message.c_str());
     }
 
-    case AuditorResult::Type::ERROR_RESERVED_UNIQUE_ID_HASH_CODE:
+    case AuditorResult::Type::ERROR_RESERVED_ID_HASH_CODE:
       DCHECK(details_.size());
       return base::StringPrintf(
-          "Unique id '%s' in '%s:%i' has a hash code similar to a reserved "
-          "word and should be changed.",
+          "Id '%s' in '%s:%i' has a hash code equal to a reserved word and "
+          "should be changed.",
           details_[0].c_str(), file_path_.c_str(), line_);
 
-    case AuditorResult::Type::ERROR_DEPRECATED_UNIQUE_ID_HASH_CODE:
+    case AuditorResult::Type::ERROR_DEPRECATED_ID_HASH_CODE:
       DCHECK(details_.size());
       return base::StringPrintf(
-          "Unique id '%s' in '%s:%i' has a hash code similar to a deprecated "
-          "unique id and should be changed.",
+          "Id '%s' in '%s:%i' has a hash code equal to a deprecated id and "
+          "should be changed.",
           details_[0].c_str(), file_path_.c_str(), line_);
 
-    case AuditorResult::Type::ERROR_DUPLICATE_UNIQUE_ID_HASH_CODE:
+    case AuditorResult::Type::ERROR_HASH_CODE_COLLISION:
       DCHECK_EQ(details_.size(), 2u);
       return base::StringPrintf(
-          "The following annotations have similar unique id "
-          "hash codes and should be updated: %s, %s.",
+          "The following annotations have colliding hash codes and should be "
+          "updated: %s, %s.",
           details_[0].c_str(), details_[1].c_str());
 
-    case AuditorResult::Type::ERROR_UNIQUE_ID_INVALID_CHARACTER:
+    case AuditorResult::Type::ERROR_REPEATED_ID:
+      DCHECK_EQ(details_.size(), 2u);
+      return base::StringPrintf(
+          "The following annotations have equal ids and should be updated: "
+          "%s, %s.",
+          details_[0].c_str(), details_[1].c_str());
+
+    case AuditorResult::Type::ERROR_ID_INVALID_CHARACTER:
       DCHECK(details_.size());
       return base::StringPrintf(
-          "Unique id '%s' in '%s:%i' contains an invalid character.",
+          "Id '%s' in '%s:%i' contains an invalid character.",
           details_[0].c_str(), file_path_.c_str(), line_);
 
     case AuditorResult::Type::ERROR_MISSING_ANNOTATION:
@@ -115,7 +123,7 @@
           "Annotation at '%s:%i' has the following missing fields: %s",
           file_path_.c_str(), line_, details_[0].c_str());
 
-    case AuditorResult::Type::ERROR_MISSING_EXTRA_ID:
+    case AuditorResult::Type::ERROR_MISSING_SECOND_ID:
       return base::StringPrintf(
           "Second id of annotation at '%s:%i' should be updated as it has the "
           "same hash code as the first one.",
diff --git a/tools/traffic_annotation/auditor/auditor_result.h b/tools/traffic_annotation/auditor/auditor_result.h
index e63aef1a..996921c 100644
--- a/tools/traffic_annotation/auditor/auditor_result.h
+++ b/tools/traffic_annotation/auditor/auditor_result.h
@@ -18,18 +18,21 @@
                              // MISSING_TRAFFIC_ANNOTATION tag.
     ERROR_NO_ANNOTATION,     // A function is called with NO_ANNOTATION tag.
     ERROR_SYNTAX,            // Annotation syntax is not right.
-    ERROR_RESERVED_UNIQUE_ID_HASH_CODE,  // A unique id has a hash code similar
-                                         // to a reserved word.
-    ERROR_DEPRECATED_UNIQUE_ID_HASH_CODE,  // A unique id has a hash code
-                                           // equal to a deprecated one.
-    ERROR_DUPLICATE_UNIQUE_ID_HASH_CODE,   // Two unique ids have equal hash
-                                           // codes.
-    ERROR_UNIQUE_ID_INVALID_CHARACTER,     // A unique id contanins a characer
-                                           // which is not alphanumeric or
-                                           // underline.
+    ERROR_RESERVED_ID_HASH_CODE,    // An id has a hash code equal to a reserved
+                                    // word.
+    ERROR_DEPRECATED_ID_HASH_CODE,  // An id has a hash code equal to a
+                                    // deprecated one.
+    ERROR_HASH_CODE_COLLISION,      // Two ids have equal hash codes.
+    ERROR_REPEATED_ID,            // An id is used in two places without proper
+                                  // matching conditions. Proper conditions
+                                  // include cases that two annotations are
+                                  // completing each other or are different
+                                  // branches of one completing annotation.
+    ERROR_ID_INVALID_CHARACTER,   // An id contanins a characer which is not
+                                  // alphanumeric or underline.
     ERROR_MISSING_ANNOTATION,     // A function that requires annotation is not
                                   // annotated.
-    ERROR_MISSING_EXTRA_ID,       // Annotation does not have a valid extra id.
+    ERROR_MISSING_SECOND_ID,      // Annotation does not have a valid second id.
     ERROR_INCOMPLETE_ANNOTATION,  // Annotation has some missing fields.
     ERROR_INCONSISTENT_ANNOTATION,  // Annotation has some inconsistent fields.
     ERROR_MERGE_FAILED,            // Two annotations that are supposed to merge
diff --git a/tools/traffic_annotation/auditor/instance.cc b/tools/traffic_annotation/auditor/instance.cc
index ba7699b..1478ebb 100644
--- a/tools/traffic_annotation/auditor/instance.cc
+++ b/tools/traffic_annotation/auditor/instance.cc
@@ -70,9 +70,9 @@
 AnnotationInstance::AnnotationInstance(const AnnotationInstance& other)
     : proto(other.proto),
       type(other.type),
-      extra_id(other.extra_id),
+      second_id(other.second_id),
       unique_id_hash_code(other.unique_id_hash_code),
-      extra_id_hash_code(other.extra_id_hash_code){};
+      second_id_hash_code(other.second_id_hash_code){};
 
 AuditorResult AnnotationInstance::Deserialize(
     const std::vector<std::string>& serialized_lines,
@@ -90,7 +90,7 @@
   base::StringToInt(serialized_lines[start_line++], &line_number);
   std::string function_type = serialized_lines[start_line++];
   const std::string& unique_id = serialized_lines[start_line++];
-  extra_id = serialized_lines[start_line++];
+  second_id = serialized_lines[start_line++];
 
   // Decode function type.
   if (function_type == "Definition") {
@@ -158,7 +158,7 @@
   src->set_function(function_context);
   src->set_line(line_number);
   proto.set_unique_id(unique_id);
-  extra_id_hash_code = TrafficAnnotationAuditor::ComputeHashValue(extra_id);
+  second_id_hash_code = TrafficAnnotationAuditor::ComputeHashValue(second_id);
 
   return AuditorResult(AuditorResult::Type::RESULT_OK);
 }
@@ -247,13 +247,13 @@
 
 bool AnnotationInstance::IsCompletableWith(
     const AnnotationInstance& other) const {
-  if (type != AnnotationInstance::Type::ANNOTATION_PARTIAL || extra_id.empty())
+  if (type != AnnotationInstance::Type::ANNOTATION_PARTIAL || second_id.empty())
     return false;
   if (other.type == AnnotationInstance::Type::ANNOTATION_COMPLETING) {
-    return extra_id_hash_code == other.unique_id_hash_code;
+    return second_id_hash_code == other.unique_id_hash_code;
   } else if (other.type ==
              AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING) {
-    return extra_id_hash_code == other.extra_id_hash_code;
+    return second_id_hash_code == other.second_id_hash_code;
   } else {
     return false;
   }
@@ -279,8 +279,8 @@
   }
 
   combination->type = AnnotationInstance::Type::ANNOTATION_COMPLETE;
-  combination->extra_id.clear();
-  combination->extra_id_hash_code = 0;
+  combination->second_id.clear();
+  combination->second_id_hash_code = 0;
   combination->comments = base::StringPrintf(
       "This annotation is a merge of the following two annotations:\n"
       "'%s' in '%s:%i' and '%s' in '%s:%i'.",
diff --git a/tools/traffic_annotation/auditor/instance.h b/tools/traffic_annotation/auditor/instance.h
index 8a04494..c7b45c9 100644
--- a/tools/traffic_annotation/auditor/instance.h
+++ b/tools/traffic_annotation/auditor/instance.h
@@ -63,6 +63,14 @@
   // type.
   bool IsCompletableWith(const AnnotationInstance& other) const;
 
+  // Tells if annotation requires two ids. All annotations have the unique id,
+  // but partial annotations also require the completing id, and branched
+  // completing annotations require the group id.
+  bool NeedsTwoIDs() const {
+    return type == Type::ANNOTATION_PARTIAL ||
+           type == Type::ANNOTATION_BRANCHED_COMPLETING;
+  }
+
   // Combines |*this| partial annotation with a completing/branched_completing
   // annotation and returns the combined complete annotation.
   AuditorResult CreateCompleteAnnotation(
@@ -75,12 +83,13 @@
   // Type of the annotation.
   Type type;
 
-  // Extra id of the annotation (if available).
-  std::string extra_id;
+  // Extra id of the annotation (if available). This can be the completing id
+  // for partial annotations, or group id for branched completing annotations.
+  std::string second_id;
 
   // Hash codes of unique id and extra id (if available).
   int unique_id_hash_code;
-  int extra_id_hash_code;
+  int second_id_hash_code;
 
   std::string comments;
 };
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
index 65ea72a3..fa30c2d 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.cc
@@ -20,6 +20,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "tools/traffic_annotation/auditor/traffic_annotation_exporter.h"
 #include "tools/traffic_annotation/auditor/traffic_annotation_file_filter.h"
+#include "tools/traffic_annotation/auditor/traffic_annotation_id_checker.h"
 
 namespace {
 
@@ -114,7 +115,7 @@
   if (!safe_list_loaded_ && !LoadSafeList())
     return false;
 
-  // Create a file to pass options to clang scripts.
+  // Create a file to pass options to the clang tool running script.
   base::FilePath options_filepath;
   if (!base::CreateTemporaryFile(&options_filepath)) {
     LOG(ERROR) << "Could not create temporary options file.";
@@ -152,10 +153,26 @@
 
     if (path_filters.size()) {
       for (const auto& path_filter : path_filters) {
-        filter.GetRelevantFiles(
-            source_path_,
-            safe_list_[static_cast<int>(AuditorException::ExceptionType::ALL)],
-            path_filter, &file_paths);
+// If path filter is a directory, search and add its contents.
+// Otherwise add it if it is a relevant file which is not safe listed.
+#if defined(OS_WIN)
+        base::FilePath current_path = base::FilePath(
+            base::FilePath::StringPieceType((base::UTF8ToWide(path_filter))));
+#else
+        base::FilePath current_path = base::FilePath(path_filter);
+#endif
+        if (base::DirectoryExists(current_path)) {
+          filter.GetRelevantFiles(source_path_,
+                                  safe_list_[static_cast<int>(
+                                      AuditorException::ExceptionType::ALL)],
+                                  path_filter, &file_paths);
+        } else {
+          if (!TrafficAnnotationAuditor::IsSafeListed(
+                  path_filter, AuditorException::ExceptionType::ALL) &&
+              filter.IsFileRelevant(path_filter)) {
+            file_paths.push_back(path_filter);
+          }
+        }
       }
     } else {
       filter.GetRelevantFiles(
@@ -380,10 +397,18 @@
 
 // static
 const std::map<int, std::string>&
-TrafficAnnotationAuditor::GetReservedUniqueIDs() {
+TrafficAnnotationAuditor::GetReservedIDsMap() {
   return kReservedAnnotations;
 }
 
+// static
+std::set<int> TrafficAnnotationAuditor::GetReservedIDsSet() {
+  std::set<int> reserved_ids;
+  for (const auto& item : kReservedAnnotations)
+    reserved_ids.insert(item.first);
+  return reserved_ids;
+}
+
 void TrafficAnnotationAuditor::PurgeAnnotations(
     const std::set<int>& hash_codes) {
   extracted_annotations_.erase(
@@ -396,150 +421,6 @@
       extracted_annotations_.end());
 }
 
-bool TrafficAnnotationAuditor::CheckDuplicateHashes() {
-  const std::map<int, std::string> reserved_ids = GetReservedUniqueIDs();
-
-  std::map<int, std::vector<AnnotationID>> collisions;
-  std::set<int> to_be_purged;
-  std::set<int> deprecated_ids;
-
-  // Load deprecated Hashcodes.
-  if (!TrafficAnnotationExporter(source_path_)
-           .GetDeprecatedHashCodes(&deprecated_ids)) {
-    return false;
-  }
-
-  for (AnnotationInstance& instance : extracted_annotations_) {
-    // Check if partial and branched completing annotation have an extra id
-    // which is different from their unique id.
-    if ((instance.type == AnnotationInstance::Type::ANNOTATION_PARTIAL ||
-         instance.type ==
-             AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING) &&
-        (instance.unique_id_hash_code == instance.extra_id_hash_code)) {
-      errors_.push_back(AuditorResult(
-          AuditorResult::Type::ERROR_MISSING_EXTRA_ID, std::string(),
-          instance.proto.source().file(), instance.proto.source().line()));
-      continue;
-    }
-
-    AnnotationID current;
-    current.instance = &instance;
-    // Iterate over unique id and extra id.
-    for (int id = 0; id < 2; id++) {
-      if (id) {
-        // If it's an empty extra id, no further check is required.
-        if (instance.extra_id.empty()) {
-          continue;
-        } else {
-          current.text = instance.extra_id;
-          current.hash = instance.extra_id_hash_code;
-          if (instance.type == AnnotationInstance::Type::ANNOTATION_PARTIAL) {
-            current.type = AnnotationID::Type::kPatrialExtra;
-          } else if (instance.type ==
-                     AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING) {
-            current.type = AnnotationID::Type::kBranchedExtra;
-          } else {
-            current.type = AnnotationID::Type::kOther;
-          }
-        }
-      } else {
-        current.text = instance.proto.unique_id();
-        current.hash = instance.unique_id_hash_code;
-        current.type =
-            instance.type == AnnotationInstance::Type::ANNOTATION_COMPLETING
-                ? AnnotationID::Type::kCompletingMain
-                : AnnotationID::Type::kOther;
-      }
-
-      // If the id's hash code is the same as a reserved id, add an error.
-      if (base::ContainsKey(reserved_ids, current.hash)) {
-        errors_.push_back(AuditorResult(
-            AuditorResult::Type::ERROR_RESERVED_UNIQUE_ID_HASH_CODE,
-            current.text, instance.proto.source().file(),
-            instance.proto.source().line()));
-        continue;
-      }
-
-      // If the id's hash code was formerly used by a deprecated annotation,
-      // add an error.
-      if (base::ContainsKey(deprecated_ids, current.hash)) {
-        errors_.push_back(AuditorResult(
-            AuditorResult::Type::ERROR_DEPRECATED_UNIQUE_ID_HASH_CODE,
-            current.text, instance.proto.source().file(),
-            instance.proto.source().line()));
-        continue;
-      }
-
-      // Check for collisions.
-      if (!base::ContainsKey(collisions, current.hash)) {
-        collisions[current.hash] = std::vector<AnnotationID>();
-      } else {
-        // Add error for ids with the same hash codes. If the texts are really
-        // different, there is a hash collision and should be corrected in any
-        // case. Otherwise, it's an error if it doesn't match the criteria that
-        // are previously spcified in definition of AnnotationID struct.
-        for (const auto& other : collisions[current.hash]) {
-          if (current.text == other.text &&
-              ((current.type == AnnotationID::Type::kPatrialExtra &&
-                (other.type == AnnotationID::Type::kPatrialExtra ||
-                 other.type == AnnotationID::Type::kCompletingMain ||
-                 other.type == AnnotationID::Type::kBranchedExtra)) ||
-               (other.type == AnnotationID::Type::kPatrialExtra &&
-                (current.type == AnnotationID::Type::kCompletingMain ||
-                 current.type == AnnotationID::Type::kBranchedExtra)) ||
-               (current.type == AnnotationID::Type::kBranchedExtra &&
-                other.type == AnnotationID::Type::kBranchedExtra))) {
-            continue;
-          }
-
-          AuditorResult error(
-              AuditorResult::Type::ERROR_DUPLICATE_UNIQUE_ID_HASH_CODE,
-              base::StringPrintf(
-                  "%s in '%s:%i'", current.text.c_str(),
-                  current.instance->proto.source().file().c_str(),
-                  current.instance->proto.source().line()));
-          error.AddDetail(
-              base::StringPrintf("%s in '%s:%i'", other.text.c_str(),
-                                 other.instance->proto.source().file().c_str(),
-                                 other.instance->proto.source().line()));
-
-          errors_.push_back(error);
-          to_be_purged.insert(current.hash);
-          to_be_purged.insert(other.hash);
-        }
-      }
-      collisions[current.hash].push_back(current);
-    }
-  }
-
-  PurgeAnnotations(to_be_purged);
-  return true;
-}
-
-void TrafficAnnotationAuditor::CheckUniqueIDsFormat() {
-  std::set<int> to_be_purged;
-  for (const AnnotationInstance& instance : extracted_annotations_) {
-    if (!base::ContainsOnlyChars(base::ToLowerASCII(instance.proto.unique_id()),
-                                 "0123456789_abcdefghijklmnopqrstuvwxyz")) {
-      errors_.push_back(AuditorResult(
-          AuditorResult::Type::ERROR_UNIQUE_ID_INVALID_CHARACTER,
-          instance.proto.unique_id(), instance.proto.source().file(),
-          instance.proto.source().line()));
-      to_be_purged.insert(instance.unique_id_hash_code);
-    }
-    if (!instance.extra_id.empty() &&
-        !base::ContainsOnlyChars(base::ToLowerASCII(instance.extra_id),
-                                 "0123456789_abcdefghijklmnopqrstuvwxyz")) {
-      errors_.push_back(
-          AuditorResult(AuditorResult::Type::ERROR_UNIQUE_ID_INVALID_CHARACTER,
-                        instance.extra_id, instance.proto.source().file(),
-                        instance.proto.source().line()));
-      to_be_purged.insert(instance.unique_id_hash_code);
-    }
-  }
-  PurgeAnnotations(to_be_purged);
-}
-
 void TrafficAnnotationAuditor::CheckAllRequiredFunctionsAreAnnotated() {
   for (const CallInstance& call : extracted_calls_) {
     if (!call.is_annotated && !CheckIfCallCanBeUnannotated(call)) {
@@ -698,10 +579,20 @@
 }
 
 bool TrafficAnnotationAuditor::RunAllChecks() {
-  if (!CheckDuplicateHashes())
+  std::set<int> deprecated_ids;
+  if (!TrafficAnnotationExporter(source_path_)
+           .GetDeprecatedHashCodes(&deprecated_ids)) {
     return false;
-  CheckUniqueIDsFormat();
-  CheckAnnotationsContents();
+  }
+  TrafficAnnotationIDChecker id_checker(GetReservedIDsSet(), deprecated_ids);
+  id_checker.Load(extracted_annotations_);
+  id_checker.CheckIDs(&errors_);
+
+  // Only check annotation contents if IDs are all OK, because if there are
+  // id errors, there might be some mismatching annotations and irrelevant
+  // content errors.
+  if (errors_.empty())
+    CheckAnnotationsContents();
 
   CheckAllRequiredFunctionsAreAnnotated();
   return true;
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
index 940afe9..c12c2a5 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor.h
@@ -75,17 +75,6 @@
   bool IsSafeListed(const std::string& file_path,
                     AuditorException::ExceptionType exception_type);
 
-  // Checks to see if any unique id or extra id or their hash code are
-  // duplicated, either in currently existing annotations, or in deprecated
-  // ones. Adds errors to |errors_| and purges annotations with duplicate ids.
-  // Returns false if any errors happen while checking.
-  bool CheckDuplicateHashes();
-
-  // Checks to see if unique ids only include alphanumeric characters and
-  // underline. Adds errors to |errors_| and purges annotations with
-  // incorrect ids.
-  void CheckUniqueIDsFormat();
-
   // Checks to see if annotation contents are valid. Complete annotations should
   // have all required fields and be consistent, and incomplete annotations
   // should be completed with each other. Merges all matching incomplete
@@ -106,7 +95,13 @@
   // texts. This list includes all unique ids that are defined in
   // net/traffic_annotation/network_traffic_annotation.h and
   // net/traffic_annotation/network_traffic_annotation_test_helper.h
-  static const std::map<int, std::string>& GetReservedUniqueIDs();
+  static const std::map<int, std::string>& GetReservedIDsMap();
+
+  // Returns a set of reserved unique ids' hash codes. This set includes all
+  // unique ids that are defined in
+  // net/traffic_annotation/network_traffic_annotation.h and
+  // net/traffic_annotation/network_traffic_annotation_test_helper.h
+  static std::set<int> GetReservedIDsSet();
 
   // Removes annotations whose unique id hash code are given.
   void PurgeAnnotations(const std::set<int>& hash_codes);
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
index a5564ff5..b82e9cf 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor_ui.cc
@@ -415,7 +415,7 @@
     TrafficAnnotationExporter exporter(source_path);
     if (!exporter.UpdateAnnotations(
             auditor.extracted_annotations(),
-            TrafficAnnotationAuditor::GetReservedUniqueIDs())) {
+            TrafficAnnotationAuditor::GetReservedIDsMap())) {
       return 1;
     }
     if (exporter.modified()) {
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc b/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
index 63ab3969..7b7720c 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_auditor_unittest.cc
@@ -19,6 +19,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "tools/traffic_annotation/auditor/traffic_annotation_exporter.h"
 #include "tools/traffic_annotation/auditor/traffic_annotation_file_filter.h"
+#include "tools/traffic_annotation/auditor/traffic_annotation_id_checker.h"
 
 namespace {
 
@@ -52,6 +53,7 @@
         .Append(FILE_PATH_LITERAL("scripts"))
         .Append(FILE_PATH_LITERAL("annotations_xml_downstream_caller.py"));
 
+const std::set<int> kDummyDeprecatedIDs = {100, 101, 102};
 }  // namespace
 
 using namespace testing;
@@ -86,11 +88,16 @@
         source_path_.Append(FILE_PATH_LITERAL("out"))
             .Append(FILE_PATH_LITERAL("Default")),
         clang_tool_path);
+
+    id_checker_ = std::make_unique<TrafficAnnotationIDChecker>(
+        TrafficAnnotationAuditor::GetReservedIDsSet(), kDummyDeprecatedIDs);
   }
 
   const base::FilePath source_path() const { return source_path_; }
   const base::FilePath tests_folder() const { return tests_folder_; };
   TrafficAnnotationAuditor& auditor() { return *auditor_; }
+  TrafficAnnotationIDChecker& id_checker() { return *id_checker_; }
+  std::vector<AuditorResult>* errors() { return &errors_; }
 
  protected:
   // Deserializes an annotation or a call instance from a sample file similar to
@@ -100,6 +107,13 @@
 
   // Creates a complete annotation instance using sample files.
   AnnotationInstance CreateAnnotationInstanceSample();
+  AnnotationInstance CreateAnnotationInstanceSample(
+      AnnotationInstance::Type type,
+      int unique_id);
+  AnnotationInstance CreateAnnotationInstanceSample(
+      AnnotationInstance::Type type,
+      int unique_id,
+      int second_id);
 
   void SetAnnotationForTesting(const AnnotationInstance& instance) {
     std::vector<AnnotationInstance> annotations;
@@ -108,10 +122,20 @@
     auditor_->ClearErrorsForTesting();
   }
 
+  void RunIDChecker(const AnnotationInstance& instance) {
+    std::vector<AnnotationInstance> annotations;
+    annotations.push_back(instance);
+    errors_.clear();
+    id_checker_->Load(annotations);
+    id_checker_->CheckIDs(&errors_);
+  }
+
  private:
   base::FilePath source_path_;
   base::FilePath tests_folder_;
   std::unique_ptr<TrafficAnnotationAuditor> auditor_;
+  std::unique_ptr<TrafficAnnotationIDChecker> id_checker_;
+  std::vector<AuditorResult> errors_;
 };
 
 AuditorResult::Type TrafficAnnotationAuditorTest::Deserialize(
@@ -138,6 +162,30 @@
   return instance;
 }
 
+AnnotationInstance TrafficAnnotationAuditorTest::CreateAnnotationInstanceSample(
+    AnnotationInstance::Type type,
+    int unique_id) {
+  return CreateAnnotationInstanceSample(type, unique_id, 0);
+}
+
+AnnotationInstance TrafficAnnotationAuditorTest::CreateAnnotationInstanceSample(
+    AnnotationInstance::Type type,
+    int unique_id,
+    int second_id) {
+  AnnotationInstance instance = CreateAnnotationInstanceSample();
+  instance.type = type;
+  instance.unique_id_hash_code = unique_id;
+  instance.proto.set_unique_id(base::StringPrintf("S%i", unique_id));
+  if (second_id) {
+    instance.second_id = base::StringPrintf("S%i", second_id);
+    instance.second_id_hash_code = second_id;
+  } else {
+    instance.second_id.clear();
+    instance.second_id_hash_code = 0;
+  }
+  return instance;
+}
+
 // Tests if the two hash computation functions have the same result.
 TEST_F(TrafficAnnotationAuditorTest, HashFunctionCheck) {
   TEST_HASH_CODE("test");
@@ -342,9 +390,9 @@
   }
 }
 
-// Tests if TrafficAnnotationAuditor::GetReservedUniqueIDs has all known ids and
+// Tests if TrafficAnnotationAuditor::GetReservedIDsMap has all known ids and
 // they have correct text.
-TEST_F(TrafficAnnotationAuditorTest, GetReservedUniqueIDs) {
+TEST_F(TrafficAnnotationAuditorTest, GetReservedIDsCoverage) {
   int expected_ids[] = {
       TRAFFIC_ANNOTATION_FOR_TESTS.unique_id_hash_code,
       PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS.unique_id_hash_code,
@@ -354,7 +402,7 @@
       NO_TRAFFIC_ANNOTATION_BUG_656607.unique_id_hash_code};
 
   std::map<int, std::string> reserved_words =
-      TrafficAnnotationAuditor::GetReservedUniqueIDs();
+      TrafficAnnotationAuditor::GetReservedIDsMap();
 
   for (int id : expected_ids) {
     EXPECT_TRUE(base::ContainsKey(reserved_words, id));
@@ -363,157 +411,171 @@
   }
 }
 
-// Tests if TrafficAnnotationAuditor::CheckDuplicateHashes works as expected.
-TEST_F(TrafficAnnotationAuditorTest, CheckDuplicateHashes) {
-  // Load a valid annotation.
-  AnnotationInstance instance = CreateAnnotationInstanceSample();
+// Tests if use of reserved ids are detected.
+TEST_F(TrafficAnnotationAuditorTest, CheckReservedIDsUsageDetection) {
+  for (const auto& reserved_id : auditor().GetReservedIDsSet()) {
+    RunIDChecker(CreateAnnotationInstanceSample(
+        AnnotationInstance::Type::ANNOTATION_COMPLETE, reserved_id));
+    EXPECT_EQ(1u, errors()->size());
+    EXPECT_EQ(AuditorResult::Type::ERROR_RESERVED_ID_HASH_CODE,
+              (*errors())[0].type());
 
-  const std::map<int, std::string>& reserved_words =
-      TrafficAnnotationAuditor::GetReservedUniqueIDs();
+    RunIDChecker(CreateAnnotationInstanceSample(
+        AnnotationInstance::Type::ANNOTATION_PARTIAL, 1, reserved_id));
+    EXPECT_EQ(1u, errors()->size());
+    EXPECT_EQ(AuditorResult::Type::ERROR_RESERVED_ID_HASH_CODE,
+              (*errors())[0].type());
+  }
+}
 
+// Tests if use of deprecated ids are detected.
+TEST_F(TrafficAnnotationAuditorTest, CheckDeprecatedIDsUsageDetection) {
+  for (const auto& deprecated_id : kDummyDeprecatedIDs) {
+    RunIDChecker(CreateAnnotationInstanceSample(
+        AnnotationInstance::Type::ANNOTATION_COMPLETE, deprecated_id));
+    EXPECT_EQ(1u, errors()->size());
+    EXPECT_EQ(AuditorResult::Type::ERROR_DEPRECATED_ID_HASH_CODE,
+              (*errors())[0].type());
+
+    RunIDChecker(CreateAnnotationInstanceSample(
+        AnnotationInstance::Type::ANNOTATION_PARTIAL, 1, deprecated_id));
+    EXPECT_EQ(1u, errors()->size());
+    EXPECT_EQ(AuditorResult::Type::ERROR_DEPRECATED_ID_HASH_CODE,
+              (*errors())[0].type());
+  }
+}
+
+// Tests if use of repeated ids are detected.
+TEST_F(TrafficAnnotationAuditorTest, CheckRepeatedIDsDetection) {
   std::vector<AnnotationInstance> annotations;
 
-  // Check for reserved words hash code duplication errors.
-  int next_id = 0;
-  for (const auto& reserved_word : reserved_words) {
-    EXPECT_FALSE(base::ContainsKey(reserved_words, next_id));
-    instance.unique_id_hash_code = next_id;
-    instance.extra_id_hash_code = reserved_word.first;
-    instance.extra_id = "SomeID";
-    annotations.push_back(instance);
-    next_id++;
-
-    EXPECT_FALSE(base::ContainsKey(reserved_words, next_id));
-    instance.unique_id_hash_code = reserved_word.first;
-    instance.extra_id_hash_code = next_id;
-    instance.extra_id.clear();
-    annotations.push_back(instance);
-    next_id++;
-  }
-
-  auditor().SetExtractedAnnotationsForTesting(annotations);
-  auditor().ClearErrorsForTesting();
-  auditor().CheckDuplicateHashes();
-  EXPECT_EQ(auditor().errors().size(), annotations.size());
-  for (const auto& error : auditor().errors()) {
-    EXPECT_EQ(error.type(),
-              AuditorResult::Type::ERROR_RESERVED_UNIQUE_ID_HASH_CODE);
-  }
-
   // Check if several different hash codes result in no error.
-  annotations.clear();
-  instance.extra_id_hash_code = 0;
-  instance.extra_id.clear();
   for (int i = 0; i < 10; i++) {
-    // Ensure that the test id is not a reserved hash code.
-    EXPECT_FALSE(base::ContainsKey(reserved_words, i));
-    instance.unique_id_hash_code = i;
-    annotations.push_back(instance);
+    annotations.push_back(CreateAnnotationInstanceSample(
+        AnnotationInstance::Type::ANNOTATION_COMPLETE, i));
   }
-  auditor().SetExtractedAnnotationsForTesting(annotations);
-  auditor().ClearErrorsForTesting();
-  auditor().CheckDuplicateHashes();
-  EXPECT_EQ(auditor().errors().size(), 0u);
+  id_checker().Load(annotations);
+  errors()->clear();
+  id_checker().CheckIDs(errors());
+  EXPECT_EQ(0u, errors()->size());
 
   // Check if repeating the same hash codes results in errors.
   annotations.clear();
   for (int i = 0; i < 10; i++) {
-    instance.unique_id_hash_code = i;
-    annotations.push_back(instance);
-    annotations.push_back(instance);
+    annotations.push_back(CreateAnnotationInstanceSample(
+        AnnotationInstance::Type::ANNOTATION_COMPLETE, i));
+    annotations.push_back(annotations.back());
   }
-  auditor().SetExtractedAnnotationsForTesting(annotations);
-  auditor().ClearErrorsForTesting();
-  auditor().CheckDuplicateHashes();
-  EXPECT_EQ(auditor().errors().size(), 10u);
-  for (const auto& error : auditor().errors()) {
-    EXPECT_EQ(error.type(),
-              AuditorResult::Type::ERROR_DUPLICATE_UNIQUE_ID_HASH_CODE);
+  id_checker().Load(annotations);
+  errors()->clear();
+  id_checker().CheckIDs(errors());
+  EXPECT_EQ(10u, errors()->size());
+  for (const auto& error : *errors()) {
+    EXPECT_EQ(error.type(), AuditorResult::Type::ERROR_REPEATED_ID);
   }
+}
 
-  // Check if the same value for unique id and extra id only results in error in
-  // types with valid extra id.
-  instance.unique_id_hash_code = 1;
-  instance.extra_id_hash_code = 1;
+// Tests if having the same unique id and second id is detected.
+TEST_F(TrafficAnnotationAuditorTest, CheckSimilarUniqueAndSecondIDsDetection) {
   const int last_type =
       static_cast<int>(AnnotationInstance::Type::ANNOTATION_INSTANCE_TYPE_LAST);
   for (int type = 0; type <= last_type; type++) {
-    instance.type = static_cast<AnnotationInstance::Type>(type);
-    SetAnnotationForTesting(instance);
-    auditor().CheckDuplicateHashes();
-    SCOPED_TRACE(type);
-    if (instance.type == AnnotationInstance::Type::ANNOTATION_COMPLETE ||
-        instance.type == AnnotationInstance::Type::ANNOTATION_COMPLETING) {
-      // Extra id of these two types is not used.
-      EXPECT_EQ(auditor().errors().size(), 0u);
-    } else {
-      EXPECT_EQ(auditor().errors().size(), 1u);
-    }
+    AnnotationInstance instance = CreateAnnotationInstanceSample(
+        static_cast<AnnotationInstance::Type>(type), 1, 1);
+    RunIDChecker(instance);
+    EXPECT_EQ(instance.NeedsTwoIDs() ? 1u : 0u, errors()->size()) << type;
   }
+}
 
-  // Check for unique id / extra id collision cases.
-  AnnotationInstance other = instance;
+// Tests Unique id and Second id collision cases.
+TEST_F(TrafficAnnotationAuditorTest, CheckDuplicateIDsDetection) {
+  const int last_type =
+      static_cast<int>(AnnotationInstance::Type::ANNOTATION_INSTANCE_TYPE_LAST);
+
   for (int type1 = 0; type1 < last_type; type1++) {
     for (int type2 = type1; type2 <= last_type; type2++) {
-      // Iterate different possiblities of common id.
-      for (int common1 = 0; common1 < 2; common1++) {
-        for (int common2 = 0; common2 < 2; common2++) {
-          instance.type = static_cast<AnnotationInstance::Type>(type1);
-          other.type = static_cast<AnnotationInstance::Type>(type2);
-          instance.unique_id_hash_code = common1 ? 1 : 2;
-          instance.extra_id_hash_code = common1 ? 2 : 1;
-          other.unique_id_hash_code = common2 ? 2 : 3;
-          other.extra_id_hash_code = common2 ? 3 : 2;
+      for (int id1 = 1; id1 < 5; id1++) {
+        for (int id2 = 1; id2 < 5; id2++) {
+          for (int id3 = 1; id3 < 5; id3++) {
+            for (int id4 = 1; id4 < 5; id4++) {
+              SCOPED_TRACE(
+                  base::StringPrintf("Testing (%i, %i, %i, %i, %i, %i).", type1,
+                                     type2, id1, id2, id3, id4));
 
-          annotations.clear();
-          annotations.push_back(instance);
-          annotations.push_back(other);
-          auditor().SetExtractedAnnotationsForTesting(annotations);
-          auditor().ClearErrorsForTesting();
-          auditor().CheckDuplicateHashes();
+              AnnotationInstance instance1 = CreateAnnotationInstanceSample(
+                  static_cast<AnnotationInstance::Type>(type1), id1, id2);
+              AnnotationInstance instance2 = CreateAnnotationInstanceSample(
+                  static_cast<AnnotationInstance::Type>(type2), id3, id4);
+              std::vector<AnnotationInstance> annotations;
+              annotations.push_back(instance1);
+              annotations.push_back(instance2);
+              id_checker().Load(annotations);
+              errors()->clear();
+              id_checker().CheckIDs(errors());
 
-          bool acceptable = false;
-          switch (instance.type) {
-            case AnnotationInstance::Type::ANNOTATION_COMPLETE:
-              // The unique id of a complete annotation must not be reused.
-              acceptable =
-                  (instance.unique_id_hash_code != other.unique_id_hash_code &&
-                   instance.unique_id_hash_code != other.extra_id_hash_code);
-              break;
+              std::set<int> unique_ids;
+              bool first_needs_two = instance1.NeedsTwoIDs();
+              bool second_needs_two = instance2.NeedsTwoIDs();
+              unique_ids.insert(id1);
+              if (first_needs_two)
+                unique_ids.insert(id2);
+              unique_ids.insert(id3);
+              if (second_needs_two)
+                unique_ids.insert(id4);
 
-            case AnnotationInstance::Type::ANNOTATION_PARTIAL:
-              // The unique id of a partial annotation should be unique.
-              // It's extra id can be used as unique id of a completing
-              // annotation or extra id of a branched completing one.
-              if (instance.unique_id_hash_code != other.unique_id_hash_code &&
-                  instance.unique_id_hash_code != other.extra_id_hash_code) {
-                if (instance.extra_id_hash_code == other.unique_id_hash_code) {
-                  acceptable =
-                      (other.type ==
-                       AnnotationInstance::Type::ANNOTATION_COMPLETING);
-                } else if (instance.extra_id_hash_code ==
-                           other.extra_id_hash_code) {
-                  acceptable =
-                      (other.type == AnnotationInstance::Type::
-                                         ANNOTATION_BRANCHED_COMPLETING);
+              if (first_needs_two && second_needs_two) {
+                // If both need two ids, either the 4 ids should be different,
+                // or the second ids should be equal and both annotations should
+                // be of types partial or branched completing.
+                if (unique_ids.size() == 4) {
+                  EXPECT_TRUE(errors()->empty());
+                } else if (unique_ids.size() == 3) {
+                  bool acceptable =
+                      (id2 == id4) &&
+                      (type1 ==
+                           static_cast<int>(
+                               AnnotationInstance::Type::ANNOTATION_PARTIAL) ||
+                       type1 == static_cast<int>(
+                                    AnnotationInstance::Type::
+                                        ANNOTATION_BRANCHED_COMPLETING)) &&
+                      (type2 ==
+                           static_cast<int>(
+                               AnnotationInstance::Type::ANNOTATION_PARTIAL) ||
+                       type2 == static_cast<int>(
+                                    AnnotationInstance::Type::
+                                        ANNOTATION_BRANCHED_COMPLETING));
+
+                  EXPECT_EQ(acceptable, errors()->empty());
                 } else {
-                  acceptable = true;
+                  EXPECT_FALSE(errors()->empty());
+                }
+              } else if (first_needs_two && !second_needs_two) {
+                // If just the first one needs two ids, then either the 3 ids
+                // should be different or the first annotation would be partial
+                // and the second completing, with one common id.
+                if (unique_ids.size() == 3) {
+                  EXPECT_TRUE(errors()->empty());
+                } else if (unique_ids.size() == 2) {
+                  bool acceptable =
+                      (id2 == id3) &&
+                      (type1 ==
+                           static_cast<int>(
+                               AnnotationInstance::Type::ANNOTATION_PARTIAL) &&
+                       type2 == static_cast<int>(AnnotationInstance::Type::
+                                                     ANNOTATION_COMPLETING));
+                  EXPECT_EQ(errors()->empty(), acceptable);
+                } else {
+                  EXPECT_FALSE(errors()->empty());
+                }
+              } else if (!first_needs_two && second_needs_two) {
+                // Can only be valid if all 3 are different.
+                EXPECT_EQ(unique_ids.size() == 3, errors()->empty());
+              } else {
+                // If none requires two ids, it can only be valid if ids are
+                // different.
+                EXPECT_EQ(unique_ids.size() == 2, errors()->empty());
                 }
               }
-              break;
-
-            case AnnotationInstance::Type::ANNOTATION_COMPLETING:
-            case AnnotationInstance::Type::ANNOTATION_INSTANCE_TYPE_LAST:
-              // Considering the other annotation has a higher type number,
-              // unique id of a completing or branched completing annotation
-              // should not be used as unique or extra id of another one.
-              acceptable =
-                  (instance.unique_id_hash_code != other.unique_id_hash_code &&
-                   instance.unique_id_hash_code != other.extra_id_hash_code);
-              break;
-
-            default:
-              NOTREACHED();
           }
         }
       }
@@ -521,46 +583,49 @@
   }
 }
 
-// Tests if TrafficAnnotationAuditor::CheckUniqueIDsFormat results are as
-// expected.
-TEST_F(TrafficAnnotationAuditorTest, CheckUniqueIDsFormat) {
+// Tests if IDs format is correctly checked.
+TEST_F(TrafficAnnotationAuditorTest, CheckIDsFormat) {
   std::map<std::string, bool> test_cases = {
       {"ID1", true},   {"id2", true},   {"Id_3", true},
       {"ID?4", false}, {"ID:5", false}, {"ID>>6", false},
   };
 
-  std::vector<AnnotationInstance> annotations;
   AnnotationInstance instance = CreateAnnotationInstanceSample();
-  unsigned int false_samples_count = 0;
-
-  // Test cases one by one.
   for (const auto& test_case : test_cases) {
+    // Set type to complete to require just unique id.
     instance.type = AnnotationInstance::Type::ANNOTATION_COMPLETE;
     instance.proto.set_unique_id(test_case.first);
-    instance.extra_id.clear();
-    SetAnnotationForTesting(instance);
-    annotations.push_back(instance);
-    auditor().CheckUniqueIDsFormat();
-    EXPECT_EQ(auditor().errors().size(), test_case.second ? 0u : 1u);
-    if (!test_case.second)
-      false_samples_count++;
+    instance.unique_id_hash_code = 1;
+    RunIDChecker(instance);
+    EXPECT_EQ(test_case.second ? 0u : 1u, errors()->size()) << test_case.first;
 
-    instance.type = AnnotationInstance::Type::ANNOTATION_COMPLETING;
+    // Set type to partial to require both ids.
+    instance.type = AnnotationInstance::Type::ANNOTATION_PARTIAL;
     instance.proto.set_unique_id("Something_Good");
-    instance.extra_id = test_case.first;
-    SetAnnotationForTesting(instance);
-    annotations.push_back(instance);
-    auditor().CheckUniqueIDsFormat();
-    EXPECT_EQ(auditor().errors().size(), test_case.second ? 0u : 1u);
-    if (!test_case.second)
-      false_samples_count++;
+    instance.second_id = test_case.first;
+    instance.unique_id_hash_code = 1;
+    instance.second_id_hash_code = 2;
+    RunIDChecker(instance);
+    EXPECT_EQ(test_case.second ? 0u : 1u, errors()->size()) << test_case.first;
   }
 
   // Test all cases together.
-  auditor().SetExtractedAnnotationsForTesting(annotations);
-  auditor().ClearErrorsForTesting();
-  auditor().CheckUniqueIDsFormat();
-  EXPECT_EQ(auditor().errors().size(), false_samples_count);
+  std::vector<AnnotationInstance> annotations;
+  instance.type = AnnotationInstance::Type::ANNOTATION_COMPLETE;
+  instance.unique_id_hash_code = 1;
+
+  unsigned int false_samples_count = 0;
+  for (const auto& test_case : test_cases) {
+    instance.proto.set_unique_id(test_case.first);
+    instance.unique_id_hash_code++;
+    annotations.push_back(instance);
+    if (!test_case.second)
+      false_samples_count++;
+  }
+  id_checker().Load(annotations);
+  errors()->clear();
+  id_checker().CheckIDs(errors());
+  EXPECT_EQ(false_samples_count, errors()->size());
 }
 
 // Tests if TrafficAnnotationAuditor::CheckAllRequiredFunctionsAreAnnotated
@@ -770,40 +835,35 @@
 
 // Tests if AnnotationInstance::IsCompletableWith works as expected.
 TEST_F(TrafficAnnotationAuditorTest, IsCompletableWith) {
-  AnnotationInstance instance = CreateAnnotationInstanceSample();
-  AnnotationInstance other = instance;
-
   const int last_type =
       static_cast<int>(AnnotationInstance::Type::ANNOTATION_INSTANCE_TYPE_LAST);
   for (int type1 = 0; type1 < last_type; type1++) {
     for (int type2 = 0; type2 <= last_type; type2++) {
       // Iterate all combination of common/specified ids.
       for (int ids = 0; ids < 256; ids++) {
-        instance.type = static_cast<AnnotationInstance::Type>(type1);
-        other.type = static_cast<AnnotationInstance::Type>(type2);
-        instance.unique_id_hash_code = ids % 4;
-        instance.extra_id_hash_code = (ids >> 2) % 4;
-        other.unique_id_hash_code = (ids >> 4) % 4;
-        other.extra_id_hash_code = (ids >> 6);
-        instance.extra_id =
-            instance.extra_id_hash_code ? "SomeID" : std::string();
-        other.extra_id = other.extra_id_hash_code ? "SomeID" : std::string();
+        AnnotationInstance instance1 = CreateAnnotationInstanceSample(
+            static_cast<AnnotationInstance::Type>(type1), ids % 4,
+            (ids >> 2) % 4);
+        AnnotationInstance instance2 = CreateAnnotationInstanceSample(
+            static_cast<AnnotationInstance::Type>(type2), (ids >> 4) % 4,
+            (ids >> 6));
 
         bool expectation = false;
-        // It's compatible only if the first one is partial and has extra_id,
+        // It's compatible only if the first one is partial and has second_id,
         // and the second one is either completing with matching unique id, or
-        // branched completing with matching extra id.
-        if (instance.type == AnnotationInstance::Type::ANNOTATION_PARTIAL &&
-            !instance.extra_id.empty()) {
+        // branched completing with matching second id.
+        if (instance1.type == AnnotationInstance::Type::ANNOTATION_PARTIAL &&
+            !instance1.second_id.empty()) {
           expectation |=
-              (other.type == AnnotationInstance::Type::ANNOTATION_COMPLETING &&
-               instance.extra_id_hash_code == other.unique_id_hash_code);
+              (instance2.type ==
+                   AnnotationInstance::Type::ANNOTATION_COMPLETING &&
+               instance1.second_id_hash_code == instance2.unique_id_hash_code);
           expectation |=
-              (other.type ==
+              (instance2.type ==
                    AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING &&
-               instance.extra_id_hash_code == other.extra_id_hash_code);
+               instance1.second_id_hash_code == instance2.second_id_hash_code);
         }
-        EXPECT_EQ(instance.IsCompletableWith(other), expectation);
+        EXPECT_EQ(instance1.IsCompletableWith(instance2), expectation);
       }
     }
   }
@@ -823,8 +883,8 @@
   // Partial and Completing.
   instance.type = AnnotationInstance::Type::ANNOTATION_PARTIAL;
   other.type = AnnotationInstance::Type::ANNOTATION_COMPLETING;
-  instance.extra_id_hash_code = 1;
-  instance.extra_id = "SomeID";
+  instance.second_id_hash_code = 1;
+  instance.second_id = "SomeID";
   other.unique_id_hash_code = 1;
   EXPECT_EQ(instance.CreateCompleteAnnotation(other, &combination).type(),
             AuditorResult::Type::RESULT_OK);
@@ -832,9 +892,9 @@
 
   // Partial and Branched Completing.
   other.type = AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING;
-  instance.extra_id_hash_code = 1;
-  other.extra_id_hash_code = 1;
-  other.extra_id = "SomeID";
+  instance.second_id_hash_code = 1;
+  other.second_id_hash_code = 1;
+  other.second_id = "SomeID";
   EXPECT_EQ(instance.CreateCompleteAnnotation(other, &combination).type(),
             AuditorResult::Type::RESULT_OK);
   EXPECT_EQ(combination.unique_id_hash_code, other.unique_id_hash_code);
@@ -842,9 +902,9 @@
   // Inconsistent field.
   other = instance;
   other.type = AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING;
-  instance.extra_id_hash_code = 1;
-  other.extra_id_hash_code = 1;
-  other.extra_id = "SomeID";
+  instance.second_id_hash_code = 1;
+  other.second_id_hash_code = 1;
+  other.second_id = "SomeID";
   instance.proto.mutable_semantics()->set_destination(
       traffic_annotation::
           NetworkTrafficAnnotation_TrafficSemantics_Destination_WEBSITE);
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_file_filter.cc b/tools/traffic_annotation/auditor/traffic_annotation_file_filter.cc
index 09a051ae..20460ca 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_file_filter.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_file_filter.cc
@@ -36,15 +36,9 @@
     "MISSING_TRAFFIC_ANNOTATION",
     "TRAFFIC_ANNOTATION_FOR_TESTS",
     "PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS",
-    "SSLClientSocket",     // SSLClientSocket::
-    "TCPClientSocket",     // TCPClientSocket::
-    "UDPClientSocket",     // UDPClientSocket::
     "URLFetcher::Create",  // This one is used with class as it's too generic.
-    "CreateDatagramClientSocket",   // ClientSocketFactory::
-    "CreateSSLClientSocket",        // ClientSocketFactory::
-    "CreateTransportClientSocket",  // ClientSocketFactory::
-    "CreateRequest",                // URLRequestContext::
-    nullptr                         // End Marker
+    "CreateRequest",       // URLRequestContext::
+    nullptr                // End Marker
 };
 
 }  // namespace
@@ -101,6 +95,14 @@
     return false;
   }
 
+  // Ignore unittest files to speed up the tests. They would be only tested when
+  // filters are disabled.
+  pos = file_path.length() - 12;
+  if (pos >= 0 && (!strcmp("_unittest.cc", file_path.c_str() + pos) ||
+                   !strcmp("_unittest.mm", file_path.c_str() + pos))) {
+    return false;
+  }
+
   base::FilePath converted_file_path =
 #if defined(OS_WIN)
       base::FilePath(
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_id_checker.cc b/tools/traffic_annotation/auditor/traffic_annotation_id_checker.cc
new file mode 100644
index 0000000..f5abd0e
--- /dev/null
+++ b/tools/traffic_annotation/auditor/traffic_annotation_id_checker.cc
@@ -0,0 +1,182 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "tools/traffic_annotation/auditor/traffic_annotation_id_checker.h"
+
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+TrafficAnnotationIDChecker::TrafficAnnotationIDChecker(
+    const std::set<int>& reserved_ids,
+    const std::set<int>& deprecated_ids)
+    : deprecated_ids_(deprecated_ids), reserved_ids_(reserved_ids) {}
+
+TrafficAnnotationIDChecker::~TrafficAnnotationIDChecker() = default;
+
+void TrafficAnnotationIDChecker::Load(
+    const std::vector<AnnotationInstance>& extracted_annotations) {
+  annotations_.clear();
+  for (const AnnotationInstance& instance : extracted_annotations) {
+    AnnotationItem item;
+    item.type = instance.type;
+    item.ids[0].hash_code = instance.unique_id_hash_code;
+    item.ids[0].text = instance.proto.unique_id();
+    if (instance.NeedsTwoIDs()) {
+      item.ids_count = 2;
+      item.ids[1].hash_code = instance.second_id_hash_code;
+      item.ids[1].text = instance.second_id;
+    } else {
+      item.ids_count = 1;
+    }
+    item.file_path = instance.proto.source().file();
+    item.line_number = instance.proto.source().line();
+    annotations_.push_back(item);
+  }
+}
+
+void TrafficAnnotationIDChecker::CheckIDs(std::vector<AuditorResult>* errors) {
+  CheckIDsFormat(errors);
+  CheckForSecondIDs(errors);
+  CheckForInvalidValues(
+      reserved_ids_, AuditorResult::Type::ERROR_RESERVED_ID_HASH_CODE, errors);
+  CheckForInvalidValues(deprecated_ids_,
+                        AuditorResult::Type::ERROR_DEPRECATED_ID_HASH_CODE,
+                        errors);
+  CheckForHashCollisions(errors);
+  CheckForInvalidRepeatedIDs(errors);
+}
+
+void TrafficAnnotationIDChecker::CheckForInvalidValues(
+    const std::set<int>& invalid_set,
+    AuditorResult::Type error_type,
+    std::vector<AuditorResult>* errors) {
+  for (AnnotationItem& item : annotations_) {
+    for (int i = 0; i < item.ids_count; i++) {
+      if (base::ContainsKey(invalid_set, item.ids[i].hash_code)) {
+        errors->push_back(AuditorResult(error_type, item.ids[i].text,
+                                        item.file_path, item.line_number));
+      }
+    }
+  }
+}
+
+void TrafficAnnotationIDChecker::CheckForSecondIDs(
+    std::vector<AuditorResult>* errors) {
+  for (AnnotationItem& item : annotations_) {
+    if (item.ids_count == 2 &&
+        (item.ids[1].text.empty() ||
+         item.ids[0].hash_code == item.ids[1].hash_code)) {
+      errors->push_back(
+          AuditorResult(AuditorResult::Type::ERROR_MISSING_SECOND_ID,
+                        std::string(), item.file_path, item.line_number));
+      // Remove this id from next tests.
+      item.ids_count = 0;
+    }
+  }
+}
+
+void TrafficAnnotationIDChecker::CheckForHashCollisions(
+    std::vector<AuditorResult>* errors) {
+  std::map<int, std::string> collisions;
+  for (AnnotationItem& item : annotations_) {
+    for (int i = 0; i < item.ids_count; i++) {
+      if (!base::ContainsKey(collisions, item.ids[i].hash_code)) {
+        collisions.insert(
+            std::make_pair(item.ids[i].hash_code, item.ids[i].text));
+      } else {
+        if (item.ids[i].text != collisions[item.ids[i].hash_code]) {
+          AuditorResult error(AuditorResult::Type::ERROR_HASH_CODE_COLLISION,
+                              item.ids[i].text);
+          error.AddDetail(collisions[item.ids[i].hash_code]);
+          errors->push_back(error);
+        }
+      }
+    }
+  }
+}
+
+void TrafficAnnotationIDChecker::CheckForInvalidRepeatedIDs(
+    std::vector<AuditorResult>* errors) {
+  std::map<int, AnnotationItem*> first_ids;
+  std::map<int, AnnotationItem*> second_ids;
+
+  // Check if first ids are unique.
+  for (AnnotationItem& item : annotations_) {
+    if (!base::ContainsKey(first_ids, item.ids[0].hash_code)) {
+      first_ids[item.ids[0].hash_code] = &item;
+    } else {
+      errors->push_back(CreateRepeatedIDError(
+          item.ids[0].text, item, *first_ids[item.ids[0].hash_code]));
+    }
+  }
+
+  // If a second id is equal to a first id, owner of the second id should be of
+  // type PARTIAL and owner of the first id should be of type COMPLETING.
+  for (AnnotationItem& item : annotations_) {
+    if (item.ids_count == 2 &&
+        base::ContainsKey(first_ids, item.ids[1].hash_code)) {
+      if (item.type != AnnotationInstance::Type::ANNOTATION_PARTIAL ||
+          first_ids[item.ids[1].hash_code]->type !=
+              AnnotationInstance::Type::ANNOTATION_COMPLETING) {
+        errors->push_back(CreateRepeatedIDError(
+            item.ids[1].text, item, *first_ids[item.ids[1].hash_code]));
+      }
+    }
+  }
+
+  // If two second ids are equal, they should be either PARTIAL or
+  // BRANCHED_COMPLETING.
+  for (AnnotationItem& item : annotations_) {
+    if (item.ids_count != 2)
+      continue;
+    if (!base::ContainsKey(second_ids, item.ids[1].hash_code)) {
+      second_ids[item.ids[1].hash_code] = &item;
+    } else {
+      AnnotationInstance::Type other_type =
+          second_ids[item.ids[1].hash_code]->type;
+      if ((item.type != AnnotationInstance::Type::ANNOTATION_PARTIAL &&
+           item.type !=
+               AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING) ||
+          (other_type != AnnotationInstance::Type::ANNOTATION_PARTIAL &&
+           other_type !=
+               AnnotationInstance::Type::ANNOTATION_BRANCHED_COMPLETING)) {
+        errors->push_back(CreateRepeatedIDError(
+            item.ids[1].text, item, *second_ids[item.ids[1].hash_code]));
+      }
+    }
+  }
+}
+
+void TrafficAnnotationIDChecker::CheckIDsFormat(
+    std::vector<AuditorResult>* errors) {
+  for (AnnotationItem& item : annotations_) {
+    bool any_failed = false;
+    for (int i = 0; i < item.ids_count; i++) {
+      if (!base::ContainsOnlyChars(base::ToLowerASCII(item.ids[i].text),
+                                   "0123456789_abcdefghijklmnopqrstuvwxyz")) {
+        errors->push_back(
+            AuditorResult(AuditorResult::Type::ERROR_ID_INVALID_CHARACTER,
+                          item.ids[i].text, item.file_path, item.line_number));
+        any_failed = true;
+      }
+    }
+    // Remove this id from next tests.
+    if (any_failed)
+      item.ids_count = 0;
+  }
+}
+
+AuditorResult TrafficAnnotationIDChecker::CreateRepeatedIDError(
+    const std::string& common_id,
+    const AnnotationItem& item1,
+    const AnnotationItem& item2) {
+  AuditorResult error(
+      AuditorResult::Type::ERROR_REPEATED_ID,
+      base::StringPrintf("%s in '%s:%i'", common_id.c_str(),
+                         item1.file_path.c_str(), item1.line_number));
+  error.AddDetail(base::StringPrintf("'%s:%i'", item2.file_path.c_str(),
+                                     item2.line_number));
+  return error;
+}
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_id_checker.h b/tools/traffic_annotation/auditor/traffic_annotation_id_checker.h
new file mode 100644
index 0000000..efca132
--- /dev/null
+++ b/tools/traffic_annotation/auditor/traffic_annotation_id_checker.h
@@ -0,0 +1,68 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TRAFFIC_ANNOTATION_ID_CHECKER_H_
+#define TRAFFIC_ANNOTATION_ID_CHECKER_H_
+
+#include <set>
+#include <vector>
+
+#include "tools/traffic_annotation/auditor/instance.h"
+
+// Performs all tests that are required to ensure that annotations have correct
+// ids.
+class TrafficAnnotationIDChecker {
+ public:
+  TrafficAnnotationIDChecker(const std::set<int>& reserved_ids,
+                             const std::set<int>& deprecated_ids);
+  ~TrafficAnnotationIDChecker();
+
+  // Loads |extracted_annotations| into |annotations_|;
+  void Load(const std::vector<AnnotationInstance>& extracted_annotations);
+
+  // Checks loaded |annotations_| for all sort of ID related errors and writes
+  // errors to |errors|.
+  void CheckIDs(std::vector<AuditorResult>* errors);
+
+ private:
+  struct AnnotationItem {
+    struct {
+      std::string text;
+      int hash_code;
+    } ids[2];
+    int ids_count;  // Number of existing ids (1 or 2).
+    AnnotationInstance::Type type;
+    std::string file_path;
+    int line_number;
+  };
+
+  // Checks if the ids in |invalid_set| are not used in annotations. If found,
+  // creates an error with |error_type| and writes it to |errors|.
+  void CheckForInvalidValues(const std::set<int>& invalid_set,
+                             AuditorResult::Type error_type,
+                             std::vector<AuditorResult>* errors);
+
+  // Check if annotations that need two ids, have two and the second one is
+  // different from their unique id.
+  void CheckForSecondIDs(std::vector<AuditorResult>* errors);
+
+  // Checks if there are ids with colliding hash values.
+  void CheckForHashCollisions(std::vector<AuditorResult>* errors);
+
+  // Checks if there are invalid repeated ids.
+  void CheckForInvalidRepeatedIDs(std::vector<AuditorResult>* errors);
+
+  // Checks if ids only include alphanumeric characters and underline.
+  void CheckIDsFormat(std::vector<AuditorResult>* errors);
+
+  AuditorResult CreateRepeatedIDError(const std::string& common_id,
+                                      const AnnotationItem& item1,
+                                      const AnnotationItem& item2);
+
+  std::vector<AnnotationItem> annotations_;
+  std::set<int> deprecated_ids_;
+  std::set<int> reserved_ids_;
+};
+
+#endif  // TRAFFIC_ANNOTATION_ID_CHECKER_H_
\ No newline at end of file
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 128d4e2..9565b0f4 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -199,6 +199,7 @@
  <item id="thumbnail_source" hash_code="135251783" content_hash_code="31086298" os_list="linux,windows"/>
  <item id="translate_url_fetcher" hash_code="137116619" content_hash_code="1127120" os_list="linux,windows"/>
  <item id="undefined" hash_code="45578882" reserved="1" os_list="linux,windows"/>
+ <item id="undefined-656607" hash_code="28406789" reserved="1" os_list="linux,windows"/>
  <item id="url_fetcher_downloader" hash_code="113231892" content_hash_code="61085066" os_list="linux,windows"/>
  <item id="url_prevision_fetcher" hash_code="118389509" content_hash_code="66145513" os_list="linux,windows"/>
  <item id="user_info_fetcher" hash_code="22265491" content_hash_code="72016232" os_list="linux,windows"/>