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/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/csp/ContentSecurityPolicy.h" 15 #include "core/html/DOMFormData.h" 16 #include "core/loader/BeaconLoader.h" 17 #include "wtf/ArrayBufferView.h" 18 19 namespace WebCore { 20 21 NavigatorBeacon::NavigatorBeacon(Navigator& navigator) 22 : m_transmittedBytes(0) 23 , m_navigator(navigator) 24 { 25 } 26 27 const char* NavigatorBeacon::supplementName() 28 { 29 return "NavigatorBeacon"; 30 } 31 32 NavigatorBeacon& NavigatorBeacon::from(Navigator& navigator) 33 { 34 NavigatorBeacon* supplement = static_cast<NavigatorBeacon*>(WillBeHeapSupplement<Navigator>::from(navigator, supplementName())); 35 if (!supplement) { 36 supplement = new NavigatorBeacon(navigator); 37 provideTo(navigator, supplementName(), adoptPtrWillBeNoop(supplement)); 38 } 39 return *supplement; 40 } 41 42 bool NavigatorBeacon::canSendBeacon(ExecutionContext* context, const KURL& url, ExceptionState& exceptionState) 43 { 44 if (!url.isValid()) { 45 exceptionState.throwDOMException(SyntaxError, "The URL argument is ill-formed or unsupported."); 46 return false; 47 } 48 // For now, only support HTTP and related. 49 if (!url.protocolIsInHTTPFamily()) { 50 exceptionState.throwDOMException(SyntaxError, "Beacons are only supported over HTTP(S)."); 51 return false; 52 } 53 // FIXME: CSP is not enforced on redirects, crbug.com/372197 54 if (!ContentSecurityPolicy::shouldBypassMainWorld(context) && !context->contentSecurityPolicy()->allowConnectToSource(url)) { 55 // We can safely expose the URL to JavaScript, as these checks happen synchronously before redirection. JavaScript receives no new information. 56 exceptionState.throwSecurityError("Refused to send beacon to '" + url.elidedString() + "' because it violates the document's Content Security Policy."); 57 return false; 58 } 59 60 // Do not allow sending Beacons over a Navigator that is detached. 61 if (!m_navigator.frame()) 62 return false; 63 64 return true; 65 } 66 67 int NavigatorBeacon::maxAllowance() const 68 { 69 const Settings* settings = m_navigator.frame()->settings(); 70 if (settings) { 71 int maxAllowed = settings->maxBeaconTransmission(); 72 if (maxAllowed < m_transmittedBytes) 73 return 0; 74 return maxAllowed - m_transmittedBytes; 75 } 76 return m_transmittedBytes; 77 } 78 79 void NavigatorBeacon::updateTransmittedBytes(int length) 80 { 81 ASSERT(length >= 0); 82 m_transmittedBytes += length; 83 } 84 85 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, Navigator& navigator, const String& urlstring, const String& data, ExceptionState& exceptionState) 86 { 87 return NavigatorBeacon::from(navigator).sendBeacon(context, urlstring, data, exceptionState); 88 } 89 90 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, const String& urlstring, const String& data, ExceptionState& exceptionState) 91 { 92 KURL url = context->completeURL(urlstring); 93 if (!canSendBeacon(context, url, exceptionState)) 94 return false; 95 96 int bytes = 0; 97 bool result = BeaconLoader::sendBeacon(m_navigator.frame(), maxAllowance(), url, data, bytes); 98 if (result) 99 updateTransmittedBytes(bytes); 100 101 return result; 102 } 103 104 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, Navigator& navigator, const String& urlstring, PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState) 105 { 106 return NavigatorBeacon::from(navigator).sendBeacon(context, urlstring, data, exceptionState); 107 } 108 109 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, const String& urlstring, PassRefPtr<ArrayBufferView> data, ExceptionState& exceptionState) 110 { 111 KURL url = context->completeURL(urlstring); 112 if (!canSendBeacon(context, url, exceptionState)) 113 return false; 114 115 int bytes = 0; 116 bool result = BeaconLoader::sendBeacon(m_navigator.frame(), maxAllowance(), url, data, bytes); 117 if (result) 118 updateTransmittedBytes(bytes); 119 120 return result; 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 if (result) 137 updateTransmittedBytes(bytes); 138 139 return result; 140 } 141 142 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, Navigator& navigator, const String& urlstring, PassRefPtrWillBeRawPtr<DOMFormData> data, ExceptionState& exceptionState) 143 { 144 return NavigatorBeacon::from(navigator).sendBeacon(context, urlstring, data, exceptionState); 145 } 146 147 bool NavigatorBeacon::sendBeacon(ExecutionContext* context, const String& urlstring, PassRefPtrWillBeRawPtr<DOMFormData> data, ExceptionState& exceptionState) 148 { 149 KURL url = context->completeURL(urlstring); 150 if (!canSendBeacon(context, url, exceptionState)) 151 return false; 152 153 int bytes = 0; 154 bool result = BeaconLoader::sendBeacon(m_navigator.frame(), maxAllowance(), url, data, bytes); 155 if (result) 156 updateTransmittedBytes(bytes); 157 158 return result; 159 } 160 161 } // namespace WebCore 162