Home | History | Annotate | Download | only in beacon
      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