Make fragment-only URLs always be document-local references

This implements the 'local url' handling per:

  https://drafts.csswg.org/css-values/#local-urls

which is also referenced from:

  https://svgwg.org/svg2-draft/linking.html#linkRefAttrs

Most of the logic is handled by a new helper class named
SVGURLReferenceResolver, which keeps state, resolves the URL
and extracts the fragment identifier as needed.

BUG=470608

Review-Url: https://codereview.chromium.org/2174833002
Cr-Commit-Position: refs/heads/master@{#407299}
diff --git a/third_party/WebKit/LayoutTests/css3/filters/effect-reference-local-url-with-base-expected.html b/third_party/WebKit/LayoutTests/css3/filters/effect-reference-local-url-with-base-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/filters/effect-reference-local-url-with-base-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/css3/filters/effect-reference-local-url-with-base.html b/third_party/WebKit/LayoutTests/css3/filters/effect-reference-local-url-with-base.html
new file mode 100644
index 0000000..c928cbe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/filters/effect-reference-local-url-with-base.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<base href="http://www.example.com/">
+<style>
+#target {
+  width: 100px;
+  height: 100px;
+  background-color: red;
+  filter: url(#filter);
+}
+</style>
+<div id="target"></div>
+<svg>
+  <filter id="filter" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
+    <feFlood flood-color="green"/>
+  </filter>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/css3/masking/clip-path-reference-local-url-with-base-expected.html b/third_party/WebKit/LayoutTests/css3/masking/clip-path-reference-local-url-with-base-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/masking/clip-path-reference-local-url-with-base-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/css3/masking/clip-path-reference-local-url-with-base.html b/third_party/WebKit/LayoutTests/css3/masking/clip-path-reference-local-url-with-base.html
new file mode 100644
index 0000000..3403359a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/masking/clip-path-reference-local-url-with-base.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<base href="http://www.example.com/">
+<style>
+#target {
+  width: 100px;
+  height: 100px;
+  border-right: 100px solid red;
+  background-color: green;
+  -webkit-clip-path: url(#clip);
+  clip-path: url(#clip);
+}
+</style>
+<div id="target"></div>
+<svg>
+  <clipPath id="clip">
+    <rect width="100" height="100"/>
+  </filter>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/http/tests/svg/local-url-with-valid-base-and-resource-expected.html b/third_party/WebKit/LayoutTests/http/tests/svg/local-url-with-valid-base-and-resource-expected.html
new file mode 100644
index 0000000..5fc78bb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/svg/local-url-with-valid-base-and-resource-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/http/tests/svg/local-url-with-valid-base-and-resource.html b/third_party/WebKit/LayoutTests/http/tests/svg/local-url-with-valid-base-and-resource.html
new file mode 100644
index 0000000..14cbba5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/svg/local-url-with-valid-base-and-resource.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<base href="http://127.0.0.1:8000/svg/resources/red100x100.svg">
+<svg>
+  <rect id="rect" width="100" height="100" fill="green"/>
+  <use xlink:href="#rect" x="100"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/http/tests/svg/resources/red100x100.svg b/third_party/WebKit/LayoutTests/http/tests/svg/resources/red100x100.svg
new file mode 100644
index 0000000..263b3af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/svg/resources/red100x100.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+  <rect id="rect" width="100" height="100" fill="red"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/platform/android/svg/custom/linking-base-external-reference-expected.png b/third_party/WebKit/LayoutTests/platform/android/svg/custom/linking-base-external-reference-expected.png
deleted file mode 100644
index 5f49f784..0000000
--- a/third_party/WebKit/LayoutTests/platform/android/svg/custom/linking-base-external-reference-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/linking-base-external-reference-expected.png b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/linking-base-external-reference-expected.png
deleted file mode 100644
index 29a87826..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/linking-base-external-reference-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/linking-base-external-reference-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/svg/custom/linking-base-external-reference-expected.txt
deleted file mode 100644
index 229e5d2..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/svg/custom/linking-base-external-reference-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 604
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 785x600
-  LayoutBlockFlow {html} at (0,0) size 785x600
-    LayoutInline {base} at (0,0) size 0x0
-    LayoutText {#text} at (0,0) size 0x0
-    LayoutText {#text} at (0,0) size 0x0
-    LayoutSVGRoot {svg} at (0,0) size 100x100
-      LayoutSVGHiddenContainer {defs} at (0,0) size 0x0
-        LayoutSVGResourceLinearGradient {linearGradient} [id="orange_red"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,0)]
-          LayoutSVGGradientStop {stop} [offset=0.00] [color=#FF0000]
-          LayoutSVGGradientStop {stop} [offset=1.00] [color=#FF0000]
-      LayoutSVGRect {rect} at (0,0) size 100x100 [x=0.00] [y=0.00] [width=100.00] [height=100.00]
-    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/linking-base-external-reference-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/linking-base-external-reference-expected.png
deleted file mode 100644
index 8cc3425a..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/svg/custom/linking-base-external-reference-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/linking-base-external-reference-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/linking-base-external-reference-expected.png
deleted file mode 100644
index b76d497..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/linking-base-external-reference-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/linking-base-external-reference-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/svg/custom/linking-base-external-reference-expected.txt
deleted file mode 100644
index 229e5d2..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/svg/custom/linking-base-external-reference-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 604
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 785x600
-  LayoutBlockFlow {html} at (0,0) size 785x600
-    LayoutInline {base} at (0,0) size 0x0
-    LayoutText {#text} at (0,0) size 0x0
-    LayoutText {#text} at (0,0) size 0x0
-    LayoutSVGRoot {svg} at (0,0) size 100x100
-      LayoutSVGHiddenContainer {defs} at (0,0) size 0x0
-        LayoutSVGResourceLinearGradient {linearGradient} [id="orange_red"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,0)]
-          LayoutSVGGradientStop {stop} [offset=0.00] [color=#FF0000]
-          LayoutSVGGradientStop {stop} [offset=1.00] [color=#FF0000]
-      LayoutSVGRect {rect} at (0,0) size 100x100 [x=0.00] [y=0.00] [width=100.00] [height=100.00]
-    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/linking-base-external-reference-expected.png b/third_party/WebKit/LayoutTests/platform/win/svg/custom/linking-base-external-reference-expected.png
deleted file mode 100644
index b4e81fd..0000000
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/linking-base-external-reference-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/svg/custom/linking-base-external-reference-expected.txt b/third_party/WebKit/LayoutTests/platform/win/svg/custom/linking-base-external-reference-expected.txt
deleted file mode 100644
index 3629126..0000000
--- a/third_party/WebKit/LayoutTests/platform/win/svg/custom/linking-base-external-reference-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 603
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 785x600
-  LayoutBlockFlow {html} at (0,0) size 785x600
-    LayoutInline {base} at (0,0) size 0x0
-    LayoutText {#text} at (0,0) size 0x0
-    LayoutText {#text} at (0,0) size 0x0
-    LayoutSVGRoot {svg} at (0,0) size 100x100
-      LayoutSVGHiddenContainer {defs} at (0,0) size 0x0
-        LayoutSVGResourceLinearGradient {linearGradient} [id="orange_red"] [gradientUnits=objectBoundingBox] [start=(0,0)] [end=(1,0)]
-          LayoutSVGGradientStop {stop} [offset=0.00] [color=#FF0000]
-          LayoutSVGGradientStop {stop} [offset=1.00] [color=#FF0000]
-      LayoutSVGRect {rect} at (0,0) size 100x100 [x=0.00] [y=0.00] [width=100.00] [height=100.00]
-    LayoutText {#text} at (0,0) size 0x0
diff --git a/third_party/WebKit/LayoutTests/svg/animations/local-url-target-reference-expected.html b/third_party/WebKit/LayoutTests/svg/animations/local-url-target-reference-expected.html
new file mode 100644
index 0000000..5fc78bb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/animations/local-url-target-reference-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/animations/local-url-target-reference.html b/third_party/WebKit/LayoutTests/svg/animations/local-url-target-reference.html
new file mode 100644
index 0000000..4898575
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/animations/local-url-target-reference.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<base href="http://www.example.com/">
+<svg>
+  <script>
+    var svgRoot = document.querySelector('svg');
+    svgRoot.pauseAnimations();
+    svgRoot.setCurrentTime(1);
+  </script>
+  <defs>
+    <path id="path" d="M100,0z"/>
+  </defs>
+  <rect width="100" height="100" fill="green">
+    <animateMotion dur="0.01" fill="freeze">
+      <mpath xlink:href="#path"/>
+    </animateMotion>
+  </rect>
+  <rect id="rect" width="100" height="100" fill="red"/>
+  <set attributeName="fill" xlink:href="#rect" to="green" dur="indefinite"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/svg/custom/baseuri-href-expected.html b/third_party/WebKit/LayoutTests/svg/custom/baseuri-href-expected.html
index 75768a0..bc3a3d9 100644
--- a/third_party/WebKit/LayoutTests/svg/custom/baseuri-href-expected.html
+++ b/third_party/WebKit/LayoutTests/svg/custom/baseuri-href-expected.html
@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <svg>
-   <rect id="rect" width="100" height="100" fill="green"/>
+   <rect width="100" height="100" fill="green"/>
+   <rect x="150" width="100" height="100" fill="green"/>
 </svg>
 
diff --git a/third_party/WebKit/LayoutTests/svg/custom/linking-base-external-reference.xhtml b/third_party/WebKit/LayoutTests/svg/custom/linking-base-external-reference.xhtml
deleted file mode 100644
index 16cf05cc..0000000
--- a/third_party/WebKit/LayoutTests/svg/custom/linking-base-external-reference.xhtml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<html xmlns="http://www.w3.org/1999/xhtml" style="height: 100%">
-  <base href="http://www.example.com"/>
-  <!-- There should be no red -->
-  <svg width="100%" height="100%" version="1.1"
-       xmlns="http://www.w3.org/2000/svg">
-
-    <defs>
-      <linearGradient id="orange_red" x1="0%" y1="0%" x2="100%" y2="0%">
-        <stop offset="0%" style="stop-color:red;"/>
-        <stop offset="100%" style="stop-color:red;"/>
-      </linearGradient>
-
-    </defs>
-
-    <rect x="0" y="0" width="100" height="100" style="fill: url(#orange_red)"/>
-  </svg>
-</html>
diff --git a/third_party/WebKit/LayoutTests/svg/custom/local-url-reference-srcdoc-expected.html b/third_party/WebKit/LayoutTests/svg/custom/local-url-reference-srcdoc-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/custom/local-url-reference-srcdoc-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/custom/local-url-reference-srcdoc.html b/third_party/WebKit/LayoutTests/svg/custom/local-url-reference-srcdoc.html
new file mode 100644
index 0000000..201ba10
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/custom/local-url-reference-srcdoc.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<iframe srcdoc="
+  <!DOCTYPE html>
+  <style>body { margin: 0; }</style>
+  <svg xmlns='http://www.w3.org/2000/svg'>
+    <linearGradient id='gradient'>
+      <stop stop-color='green'/>
+    </linearGradient>
+    <rect width='100' height='100' fill='url(#gradient) red'/>
+  </svg>"
+  style="margin: 0; border: none" scrolling="no">
+</iframe>
diff --git a/third_party/WebKit/LayoutTests/svg/custom/local-url-references-expected.html b/third_party/WebKit/LayoutTests/svg/custom/local-url-references-expected.html
new file mode 100644
index 0000000..2a4e1f1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/custom/local-url-references-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 400px; height: 300px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/custom/local-url-references.html b/third_party/WebKit/LayoutTests/svg/custom/local-url-references.html
new file mode 100644
index 0000000..b804987
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/custom/local-url-references.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<base href="http://www.example.com/">
+<svg width="400" height="300">
+  <linearGradient id="paint">
+    <stop stop-color="green"/>
+  </linearGradient>
+  <linearGradient id="inheritedPaint" xlink:href="#paint"/>
+  <rect id="rect" width="100" height="100" fill="url(#paint) red"/>
+  <rect x="125" y="25" width="50" height="50" stroke="url(#paint) red" stroke-width="50"/>
+  <use x="200" xlink:href="#rect"/>
+  <rect x="300" width="100" height="100" fill="url(#inheritedPaint) red"/>
+
+  <filter id="filter" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
+    <feImage xlink:href="#rect"/>
+  </filter>
+  <rect y="100" width="100" height="100" fill="red" filter="url(#filter)"/>
+
+  <clipPath id="clip">
+    <rect y="50" width="100" height="50"/>
+  </clipPath>
+  <rect x="100" y="100" width="100" height="50" fill="green"/>
+  <g clip-path="url(#clip)" transform="translate(100, 100)">
+    <rect width="100" height="50" fill="red"/>
+    <rect y="50" width="100" height="50" fill="green"/>
+  </g>
+
+  <mask id="mask">
+    <rect y="50" width="100" height="50" fill="white"/>
+  </mask>
+  <rect y="100" x="200" width="100" height="50" fill="green"/>
+  <g mask="url(#mask)" transform="translate(200, 100)">
+    <rect width="100" height="50" fill="red"/>
+    <rect y="50" width="100" height="50" fill="green"/>
+  </g>
+
+  <pattern id="pattern" width="100" height="100" patternUnits="userSpaceOnUse">
+    <rect width="100" height="100" fill="green"/>
+  </pattern>
+  <pattern id="inheritedPattern" xlink:href="#pattern"/>
+  <rect x="300" y="100" width="100" height="100" fill="url(#inheritedPattern) red"/>
+
+  <marker id="marker" refY="0.5">
+    <rect width="1" height="1" fill="green"/>
+  </marker>
+  <polyline points="0,250 100,250 200,250" stroke="red" stroke-width="100"
+            marker-start="url(#marker)" marker-mid="url(#marker)" marker-end="url(#marker)"/>
+
+  <radialGradient id="radial">
+    <stop stop-color="green"/>
+  </radialGradient>
+  <radialGradient id="inheritedRadial" xlink:href="#radial"/>
+  <rect x="300" y="200" width="100" height="100" fill="url(#inheritedRadial) red"/>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/svg/text/textpath-local-url-reference-expected.html b/third_party/WebKit/LayoutTests/svg/text/textpath-local-url-reference-expected.html
new file mode 100644
index 0000000..f718ea6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/text/textpath-local-url-reference-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 100px; height: 100px; background-color: green"></div>
diff --git a/third_party/WebKit/LayoutTests/svg/text/textpath-local-url-reference.html b/third_party/WebKit/LayoutTests/svg/text/textpath-local-url-reference.html
new file mode 100644
index 0000000..0051ba8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/svg/text/textpath-local-url-reference.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<base href="http://www.example.com/">
+<script src="../../resources/ahem.js"></script>
+<svg>
+  <path id="path" d="M0,80h100"/>
+  <text font-size="100" font-family="Ahem" fill="green"><textPath xlink:href="#path">X</textPath></textPath>
+</svg>
diff --git a/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp b/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp
index 5f1a777..ade3260 100644
--- a/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/FilterOperationResolver.cpp
@@ -35,6 +35,7 @@
 #include "core/css/resolver/StyleResolverState.h"
 #include "core/frame/UseCounter.h"
 #include "core/layout/svg/ReferenceFilterBuilder.h"
+#include "core/svg/SVGURIReference.h"
 
 namespace blink {
 
@@ -130,10 +131,9 @@
             countFilterUse(FilterOperation::REFERENCE, state.document());
 
             const CSSURIValue& urlValue = toCSSURIValue(*currValue);
-            KURL url = state.document().completeURL(urlValue.url());
-
-            ReferenceFilterOperation* operation = ReferenceFilterOperation::create(urlValue.url(), AtomicString(url.fragmentIdentifier()));
-            if (!equalIgnoringFragmentIdentifier(url, state.document().url())) {
+            SVGURLReferenceResolver resolver(urlValue.value(), state.document());
+            ReferenceFilterOperation* operation = ReferenceFilterOperation::create(urlValue.value(), resolver.fragmentIdentifier());
+            if (!resolver.isLocal()) {
                 if (!urlValue.loadRequested())
                     state.elementStyleResources().addPendingSVGDocument(operation, &urlValue);
                 else if (urlValue.cachedDocument())
diff --git a/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp b/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp
index bd5c532..c1c4a2e 100644
--- a/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/StyleBuilderConverter.cpp
@@ -123,10 +123,9 @@
     if (value.isBasicShapeValue())
         return ShapeClipPathOperation::create(basicShapeForValue(state, value));
     if (value.isURIValue()) {
-        String cssURLValue = toCSSURIValue(value).value();
-        KURL url = state.document().completeURL(cssURLValue);
+        SVGURLReferenceResolver resolver(toCSSURIValue(value).value(), state.document());
         // TODO(fs): Doesn't work with forward or external SVG references (crbug.com/391604, crbug.com/109212, ...)
-        return ReferenceClipPathOperation::create(cssURLValue, AtomicString(url.fragmentIdentifier()));
+        return ReferenceClipPathOperation::create(toCSSURIValue(value).value(), resolver.fragmentIdentifier());
     }
     DCHECK(value.isPrimitiveValue() && toCSSPrimitiveValue(value).getValueID() == CSSValueNone);
     return nullptr;
diff --git a/third_party/WebKit/Source/core/svg/SVGURIReference.cpp b/third_party/WebKit/Source/core/svg/SVGURIReference.cpp
index 64fd240..8ed6a84 100644
--- a/third_party/WebKit/Source/core/svg/SVGURIReference.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGURIReference.cpp
@@ -56,24 +56,47 @@
     return document.completeURL(stripLeadingAndTrailingHTMLSpaces(hrefString()));
 }
 
+SVGURLReferenceResolver::SVGURLReferenceResolver(const String& urlString, const Document& document)
+    : m_relativeUrl(urlString)
+    , m_document(&document)
+    , m_isLocal(urlString.startsWith('#'))
+{
+}
+
+KURL SVGURLReferenceResolver::absoluteUrl() const
+{
+    if (m_absoluteUrl.isNull())
+        m_absoluteUrl = m_document->completeURL(m_relativeUrl);
+    return m_absoluteUrl;
+}
+
+bool SVGURLReferenceResolver::isLocal() const
+{
+    return m_isLocal || equalIgnoringFragmentIdentifier(absoluteUrl(), m_document->url());
+}
+
+AtomicString SVGURLReferenceResolver::fragmentIdentifier() const
+{
+    // If this is a "fragment-only" URL, then the reference is always local, so
+    // just return what's after the '#' as the fragment.
+    if (m_isLocal)
+        return AtomicString(m_relativeUrl.substring(1));
+    return AtomicString(absoluteUrl().fragmentIdentifier());
+}
+
 AtomicString SVGURIReference::fragmentIdentifierFromIRIString(const String& urlString, const TreeScope& treeScope)
 {
-    const Document& document = treeScope.document();
-
-    KURL url = document.completeURL(urlString);
-    if (!url.hasFragmentIdentifier() || !equalIgnoringFragmentIdentifier(url, document.url()))
+    SVGURLReferenceResolver resolver(urlString, treeScope.document());
+    if (!resolver.isLocal())
         return emptyAtom;
-    return AtomicString(url.fragmentIdentifier());
+    return resolver.fragmentIdentifier();
 }
 
 Element* SVGURIReference::targetElementFromIRIString(const String& urlString, const TreeScope& treeScope, AtomicString* fragmentIdentifier)
 {
-    const Document& document = treeScope.document();
-
-    KURL url = document.completeURL(urlString);
-    if (!url.hasFragmentIdentifier() || !equalIgnoringFragmentIdentifier(url, document.url()))
+    AtomicString id = fragmentIdentifierFromIRIString(urlString, treeScope);
+    if (id.isEmpty())
         return nullptr;
-    AtomicString id(url.fragmentIdentifier());
     if (fragmentIdentifier)
         *fragmentIdentifier = id;
     return treeScope.getElementById(id);
diff --git a/third_party/WebKit/Source/core/svg/SVGURIReference.h b/third_party/WebKit/Source/core/svg/SVGURIReference.h
index 97248ec..0bf7ddc 100644
--- a/third_party/WebKit/Source/core/svg/SVGURIReference.h
+++ b/third_party/WebKit/Source/core/svg/SVGURIReference.h
@@ -48,18 +48,6 @@
     static AtomicString fragmentIdentifierFromIRIString(const String&, const TreeScope&);
     static Element* targetElementFromIRIString(const String&, const TreeScope&, AtomicString* = nullptr);
 
-    static inline bool isExternalURIReference(const String& uri, const Document& document)
-    {
-        // Fragment-only URIs are always internal if the baseURL is same as the document URL.
-        // This is common case, so check that first to avoid resolving URL (which is relatively expensive). See crbug.com/557979
-        if (document.baseURL() == document.url() && uri.startsWith('#'))
-            return false;
-
-        // If the URI matches our documents URL, we're dealing with a local reference.
-        KURL url = document.completeURL(uri);
-        return !equalIgnoringFragmentIdentifier(url, document.url());
-    }
-
     const String& hrefString() const { return m_href->currentValue()->value(); }
 
     // JS API
@@ -74,6 +62,24 @@
     Member<SVGAnimatedHref> m_href;
 };
 
+// Helper class used to resolve fragment references. Handles the 'local url
+// flag' per https://drafts.csswg.org/css-values/#local-urls .
+class SVGURLReferenceResolver {
+    STACK_ALLOCATED();
+public:
+    SVGURLReferenceResolver(const String& urlString, const Document&);
+
+    bool isLocal() const;
+    KURL absoluteUrl() const;
+    AtomicString fragmentIdentifier() const;
+
+private:
+    const String& m_relativeUrl;
+    Member<const Document> m_document;
+    mutable KURL m_absoluteUrl;
+    bool m_isLocal;
+};
+
 } // namespace blink
 
 #endif // SVGURIReference_h
diff --git a/third_party/WebKit/Source/core/svg/SVGUseElement.cpp b/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
index ca7d957..fe8395d 100644
--- a/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
+++ b/third_party/WebKit/Source/core/svg/SVGUseElement.cpp
@@ -184,16 +184,21 @@
         SVGGraphicsElement::collectStyleForPresentationAttribute(name, value, style);
 }
 
+bool SVGUseElement::isStructurallyExternal() const
+{
+    return !m_elementIdentifierIsLocal;
+}
+
 void SVGUseElement::updateTargetReference()
 {
-    KURL resolvedUrl = document().completeURL(hrefString());
-    m_elementIdentifier = AtomicString(resolvedUrl.fragmentIdentifier());
-    m_elementIdentifierIsLocal = resolvedUrl.isNull()
-        || equalIgnoringFragmentIdentifier(resolvedUrl, document().url());
+    SVGURLReferenceResolver resolver(hrefString(), document());
+    m_elementIdentifier = resolver.fragmentIdentifier();
+    m_elementIdentifierIsLocal = resolver.isLocal();
     if (m_elementIdentifierIsLocal) {
         setDocumentResource(nullptr);
         return;
     }
+    KURL resolvedUrl = resolver.absoluteUrl();
     if (m_elementIdentifier.isEmpty()
         || (m_resource && equalIgnoringFragmentIdentifier(resolvedUrl, m_resource->url())))
         return;
diff --git a/third_party/WebKit/Source/core/svg/SVGUseElement.h b/third_party/WebKit/Source/core/svg/SVGUseElement.h
index 26fe4b62..cd8332a 100644
--- a/third_party/WebKit/Source/core/svg/SVGUseElement.h
+++ b/third_party/WebKit/Source/core/svg/SVGUseElement.h
@@ -73,7 +73,7 @@
     void collectStyleForPresentationAttribute(const QualifiedName&, const AtomicString&, MutableStylePropertySet*) override;
     bool isPresentationAttributeWithSVGDOM(const QualifiedName&) const override;
 
-    bool isStructurallyExternal() const override { return !hrefString().isNull() && isExternalURIReference(hrefString(), document()); }
+    bool isStructurallyExternal() const override;
 
     InsertionNotificationRequest insertedInto(ContainerNode*) override;
     void removedFrom(ContainerNode*) override;