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/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