1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "config.h" 6 #include "modules/beacon/NavigatorBeacon.h" 7 8 #include "bindings/core/v8/ExceptionState.h" 9 #include "core/dom/ExceptionCode.h" 10 #include "core/dom/ExecutionContext.h" 11 #include "core/fileapi/Blob.h" 12 #include "core/frame/LocalFrame.h" 13 #include "core/frame/Settings.h" 14 #include "core/frame/UseCounter.h" 15 #include "core/frame/csp/ContentSecurityPolicy.h" 16 #include "core/html/DOMFormData.h" 17 #include "core/loader/BeaconLoader.h" 18 #include "wtf/ArrayBufferView.h" 19 20 namespace blink { 21 22 NavigatorBeacon::NavigatorBeacon(Navigator& navigator) 23 : m_transmittedBytes(0) 24 , m_navigator(navigator) 25 { 26 } 27 28 const char* NavigatorBeacon::supplementName() 29 { 30 return "NavigatorBeacon"; 31 } 32 33 NavigatorBeacon& NavigatorBeacon::from(Navigator& navigator) 34 { 35 NavigatorBeacon* supplement = static_cast<NavigatorBeacon*>(WillBeHeapSupplement<Navigator>::from(navigator, supplementName())); 36 if (!supplement) { 37 supplement = new NavigatorBeacon(navigator); 38 provideTo(navigator, supplementName(), adoptPtrWillBeNoop(supplement)); 39 } 40 return *supplement; 41 } 42 43 bool NavigatorBeacon::canSendBeacon(ExecutionContext* context, const KURL& url, ExceptionState& exceptionState) 44 { 45 if (!url.isValid()) { 46 exceptionState.throwDOMException(SyntaxError, "The URL argument is ill-formed or unsupported."); 47 return false; 48 } 49 // For now, only support HTTP and related. 50 if (!url.protocolIsInHTTPFamily()) { 51 exceptionState.throwDOMException(SyntaxError, "Beacons are only supported over HTTP(S)."); 52 return false; 53 } 54 // FIXME: CSP is not enforced on redirects, crbug.com/372197 55 if (!ContentSecurityPolicy::shouldBypassMainWorld(context) && !context->contentSecurityPolicy()->allowConnectToSource(url)) { 56 // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information. 57 exceptionState.throwSecurityError("Refused to send beacon to '" + url.elidedString() + "' because it violates the document's Content Security Policy."); 58 return false; 59 } 60 61 // Do not allow sending Beacons over a Navigator that is detached. 62 if (!m_navigator.frame() || !m_navigator.frame()->client()) 63 return false; 64 65 return true; 66 } 67 68 int NavigatorBeacon::maxAllowance() const 69 { 70 const Settings* settings = m_navigator.frame()->settings(); 71 if (settings) { 72 int maxAllowed = settings->maxBeaconTransmission(); 73 if (maxAllowed < m_transmittedBytes) 74 return 0; 75 return maxAllowed - m_transmittedBytes; 76 } 77 return m_transmittedBytes; 78 } 79 80 bool NavigatorBeacon::beaconResult(ExecutionContext* context, bool allowed, int sentBytes) 81 { 82 if (allowed) { 83 ASSERT(sentBytes >= 0); 84 m_transmittedBytes += sentBytes; 85 } else { 86 UseCounter::count(context, UseCounter::SendBeaconQuotaExceeded); 87 } 88 return allowed; 89 } 90 91 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, Navigator& navigator, const String& urlstring, const String& data, ExceptionState& exceptionState) 92 { 93 return NavigatorBeacon::from(navigator).sendBeacon(context, urlstring, data, exceptionState); 94 } 95 96 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, const String& urlstring, const String& data, ExceptionState& exceptionState) 97 { 98 KURL url = context->completeURL(urlstring); 99 if (!canSendBeacon(context, url, exceptionState)) 100 return false; 101 102 int bytes = 0; 103 bool result = BeaconLoader::sendBeacon(m_navigator.frame(), maxAllowance(), url, data, bytes); 104 return beaconResult(context, result, bytes); 105 } 106 107 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, Navigator& navigator, const String& urlstring, PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState) 108 { 109 return NavigatorBeacon::from(navigator).sendBeacon(context, urlstring, data, exceptionState); 110 } 111 112 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, const String& urlstring, PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState) 113 { 114 KURL url = context->completeURL(urlstring); 115 if (!canSendBeacon(context, url, exceptionState)) 116 return false; 117 118 int bytes = 0; 119 bool result = BeaconLoader::sendBeacon(m_navigator.frame(), maxAllowance(), url, data, bytes); 120 return beaconResult(context, result, bytes); 121 } 122 123 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, Navigator& navigator, const String& urlstring, PassRefPtrWillBeRawPtr<Blob> data, ExceptionState& exceptionState) 124 { 125 return NavigatorBeacon::from(navigator).sendBeacon(context, urlstring, data, exceptionState); 126 } 127 128 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, const String& urlstring, PassRefPtrWillBeRawPtr<Blob> data, ExceptionState& exceptionState) 129 { 130 KURL url = context->completeURL(urlstring); 131 if (!canSendBeacon(context, url, exceptionState)) 132 return false; 133 134 int bytes = 0; 135 bool result = BeaconLoader::sendBeacon(m_navigator.frame(), maxAllowance(), url, data, bytes); 136 return beaconResult(context, result, bytes); 137 } 138 139 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, Navigator& navigator, const String& urlstring, PassRefPtrWillBeRawPtr<DOMFormData> data, ExceptionState& exceptionState) 140 { 141 return NavigatorBeacon::from(navigator).sendBeacon(context, urlstring, data, exceptionState); 142 } 143 144 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, const String& urlstring, PassRefPtrWillBeRawPtr<DOMFormData> data, ExceptionState& exceptionState) 145 { 146 KURL url = context->completeURL(urlstring); 147 if (!canSendBeacon(context, url, exceptionState)) 148 return false; 149 150 int bytes = 0; 151 bool result = BeaconLoader::sendBeacon(m_navigator.frame(), maxAllowance(), url, data, bytes); 152 return beaconResult(context, result, bytes); 153 } 154 155 } // namespace blink 156