summaryrefslogtreecommitdiffstats
path: root/netwerk/streamconv/converters/nsUnknownDecoder.h
blob: 0df9df93ad372f23bc130e96d6dcc364bc9b7666 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsUnknownDecoder_h__
#define nsUnknownDecoder_h__

#include "nsIStreamConverter.h"
#include "nsIThreadRetargetableStreamListener.h"
#include "nsIContentSniffer.h"
#include "mozilla/Mutex.h"
#include "mozilla/Atomics.h"

#include "nsCOMPtr.h"
#include "nsString.h"

#define NS_UNKNOWNDECODER_CID                        \
  { /* 7d7008a0-c49a-11d3-9b22-0080c7cb1080 */       \
    0x7d7008a0, 0xc49a, 0x11d3, {                    \
      0x9b, 0x22, 0x00, 0x80, 0xc7, 0xcb, 0x10, 0x80 \
    }                                                \
  }

class nsUnknownDecoder : public nsIStreamConverter,
                         public nsIContentSniffer,
                         public nsIThreadRetargetableStreamListener {
 public:
  // nsISupports methods
  NS_DECL_ISUPPORTS

  // nsIStreamConverter methods
  NS_DECL_NSISTREAMCONVERTER

  // nsIStreamListener methods
  NS_DECL_NSISTREAMLISTENER

  // nsIRequestObserver methods
  NS_DECL_NSIREQUESTOBSERVER

  // nsIContentSniffer methods
  NS_DECL_NSICONTENTSNIFFER

  // nsIThreadRetargetableStreamListener methods
  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER

  explicit nsUnknownDecoder(nsIStreamListener* aListener = nullptr);

 protected:
  virtual ~nsUnknownDecoder();

  virtual void DetermineContentType(nsIRequest* aRequest);
  nsresult FireListenerNotifications(nsIRequest* request, nsISupports* aCtxt);

  class ConvertedStreamListener : public nsIStreamListener {
   public:
    explicit ConvertedStreamListener(nsUnknownDecoder* aDecoder);

    NS_DECL_ISUPPORTS
    NS_DECL_NSIREQUESTOBSERVER
    NS_DECL_NSISTREAMLISTENER

   private:
    virtual ~ConvertedStreamListener() = default;
    static nsresult AppendDataToString(nsIInputStream* inputStream,
                                       void* closure, const char* rawSegment,
                                       uint32_t toOffset, uint32_t count,
                                       uint32_t* writeCount);
    nsUnknownDecoder* mDecoder;
  };

 protected:
  nsCOMPtr<nsIStreamListener> mNextListener;

  // Various sniffer functions.  Returning true means that a type
  // was determined; false means no luck.
  bool SniffForHTML(nsIRequest* aRequest);
  bool SniffForXML(nsIRequest* aRequest);

  // SniffURI guesses at the content type based on the URI (typically
  // using the extentsion)
  bool SniffURI(nsIRequest* aRequest);

  // LastDitchSniff guesses at text/plain vs. application/octet-stream
  // by just looking at whether the data contains null bytes, and
  // maybe at the fraction of chars with high bit set.  Use this only
  // as a last-ditch attempt to decide a content type!
  bool LastDitchSniff(nsIRequest* aRequest);

  /**
   * An entry struct for our array of sniffers.  Each entry has either
   * a type associated with it (set these with the SNIFFER_ENTRY macro)
   * or a function to be executed (set these with the
   * SNIFFER_ENTRY_WITH_FUNC macro).  The function should take a single
   * nsIRequest* and returns bool -- true if it sets mContentType,
   * false otherwise
   */
  struct nsSnifferEntry {
    using TypeSniffFunc = bool (nsUnknownDecoder::*)(nsIRequest*);

    const char* mBytes;
    uint32_t mByteLen;

    // Exactly one of mMimeType and mContentTypeSniffer should be set non-null
    const char* mMimeType;
    TypeSniffFunc mContentTypeSniffer;
  };

#define SNIFFER_ENTRY(_bytes, _type) \
  { _bytes, sizeof(_bytes) - 1, _type, nullptr }

#define SNIFFER_ENTRY_WITH_FUNC(_bytes, _func) \
  { _bytes, sizeof(_bytes) - 1, nullptr, _func }

  static nsSnifferEntry sSnifferEntries[];
  static uint32_t sSnifferEntryNum;

  // We guarantee in order delivery of OnStart, OnStop and OnData, therefore
  // we do not need proper locking for mBuffer.
  mozilla::Atomic<char*> mBuffer;
  mozilla::Atomic<uint32_t> mBufferLen;

  nsCString mContentType;

  // This mutex syncs: mContentType, mDecodedData and mNextListener.
  mutable mozilla::Mutex mMutex MOZ_UNANNOTATED;

 protected:
  nsresult ConvertEncodedData(nsIRequest* request, const char* data,
                              uint32_t length);
  nsCString mDecodedData;  // If data are encoded this will be uncompress data.
};

#define NS_BINARYDETECTOR_CID                        \
  { /* a2027ec6-ba0d-4c72-805d-148233f5f33c */       \
    0xa2027ec6, 0xba0d, 0x4c72, {                    \
      0x80, 0x5d, 0x14, 0x82, 0x33, 0xf5, 0xf3, 0x3c \
    }                                                \
  }

/**
 * Class that detects whether a data stream is text or binary.  This reuses
 * most of nsUnknownDecoder except the actual content-type determination logic
 * -- our overridden DetermineContentType simply calls LastDitchSniff and sets
 * the type to APPLICATION_GUESS_FROM_EXT if the data is detected as binary.
 */
class nsBinaryDetector : public nsUnknownDecoder {
 protected:
  virtual void DetermineContentType(nsIRequest* aRequest) override;
};

#endif /* nsUnknownDecoder_h__ */