1 #include "url/url_util_qt.h" 2 3 #include "base/command_line.h" 4 #include "base/no_destructor.h" 5 #include "base/numerics/safe_conversions.h" 6 #include "base/strings/string_number_conversions.h" 7 #include "base/strings/string_util.h" 8 #include "url/gurl.h" 9 #include "url/url_canon.h" 10 #include "url/url_util.h" 11 12 namespace url { 13 14 namespace { 15 ToString(const CustomScheme & cs)16std::string ToString(const CustomScheme& cs) 17 { 18 std::string serialized; 19 20 serialized += cs.name; 21 serialized += ':'; 22 23 switch (cs.type) { 24 case SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION: 25 serialized += 'u'; 26 serialized += base::NumberToString(cs.default_port); 27 break; 28 case SCHEME_WITH_HOST_AND_PORT: 29 serialized += 'p'; 30 serialized += base::NumberToString(cs.default_port); 31 break; 32 case SCHEME_WITH_HOST: 33 serialized += 'h'; 34 break; 35 case SCHEME_WITHOUT_AUTHORITY: 36 break; 37 } 38 39 if (cs.flags & CustomScheme::Secure) 40 serialized += 's'; 41 if (cs.flags & CustomScheme::Local) 42 serialized += 'l'; 43 if (cs.flags & CustomScheme::LocalAccessAllowed) 44 serialized += 'L'; 45 if (cs.flags & CustomScheme::NoAccessAllowed) 46 serialized += 'N'; 47 if (cs.flags & CustomScheme::ServiceWorkersAllowed) 48 serialized += 'W'; 49 if (cs.flags & CustomScheme::ViewSourceAllowed) 50 serialized += 'V'; 51 if (cs.flags & CustomScheme::ContentSecurityPolicyIgnored) 52 serialized += 'C'; 53 if (cs.flags & CustomScheme::CorsEnabled) 54 serialized += 'F'; 55 56 return serialized; 57 } 58 59 class Parser { 60 public: CharacterArrived(char ch)61 void CharacterArrived(char ch) { 62 switch (state) { 63 case NAME: CharacterArrivedWhileParsingName(ch); break; 64 case OPTIONS: CharacterArrivedWhileParsingOptions(ch); break; 65 case PORT: CharacterArrivedWhileParsingPort(ch); break; 66 } 67 } 68 EndReached()69 void EndReached() { 70 if (!default_port_string.empty()) 71 FlushPort(); 72 if (!cs.name.empty()) 73 Flush(); 74 } 75 76 private: CharacterArrivedWhileParsingName(char ch)77 void CharacterArrivedWhileParsingName(char ch) { 78 switch (ch) { 79 case ':': state = OPTIONS; break; 80 case ';': Flush(); break; 81 default: cs.name += ch; break; 82 } 83 } 84 CharacterArrivedWhileParsingOptions(char ch)85 void CharacterArrivedWhileParsingOptions(char ch) { 86 switch (ch) { 87 case 'u': cs.type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION; state = PORT; break; 88 case 'p': cs.type = SCHEME_WITH_HOST_AND_PORT; state = PORT; break; 89 case 'h': cs.type = SCHEME_WITH_HOST; break; 90 case 's': cs.flags |= CustomScheme::Secure; break; 91 case 'l': cs.flags |= CustomScheme::Local; break; 92 case 'L': cs.flags |= CustomScheme::LocalAccessAllowed; break; 93 case 'N': cs.flags |= CustomScheme::NoAccessAllowed; break; 94 case 'W': cs.flags |= CustomScheme::ServiceWorkersAllowed; break; 95 case 'V': cs.flags |= CustomScheme::ViewSourceAllowed; break; 96 case 'C': cs.flags |= CustomScheme::ContentSecurityPolicyIgnored; break; 97 case 'F': cs.flags |= CustomScheme::CorsEnabled; break; 98 case ';': Flush(); state = NAME; break; 99 default: CHECK(false) << "Unexpected character '" << ch << "'."; 100 } 101 } 102 CharacterArrivedWhileParsingPort(char ch)103 void CharacterArrivedWhileParsingPort(char ch) { 104 if (base::IsAsciiDigit(ch)) { 105 default_port_string += ch; 106 return; 107 } 108 109 FlushPort(); 110 111 state = OPTIONS; 112 CharacterArrivedWhileParsingOptions(ch); 113 } 114 FlushPort()115 void FlushPort() { 116 CHECK(base::StringToInt(default_port_string, &cs.default_port)) 117 << "Failed to parse '" << default_port_string << "'."; 118 default_port_string.clear(); 119 } 120 Flush()121 void Flush() { 122 CustomScheme::AddScheme(cs); 123 cs = CustomScheme(); 124 } 125 126 enum { NAME, OPTIONS, PORT } state = NAME; 127 CustomScheme cs; 128 std::string default_port_string; 129 }; 130 131 } // namespace 132 GetMutableSchemes()133std::vector<CustomScheme>& CustomScheme::GetMutableSchemes() { 134 static base::NoDestructor<std::vector<CustomScheme>> schemes; 135 return *schemes; 136 } 137 GetSchemes()138const std::vector<CustomScheme>& CustomScheme::GetSchemes() { 139 return GetMutableSchemes(); 140 } 141 ClearSchemes()142void CustomScheme::ClearSchemes() 143 { 144 GetMutableSchemes().clear(); 145 } 146 AddScheme(const CustomScheme & cs)147void CustomScheme::AddScheme(const CustomScheme& cs) 148 { 149 DCHECK(!cs.name.empty()); 150 DCHECK_EQ(cs.has_port_component(), (cs.default_port != PORT_UNSPECIFIED)) 151 << "Scheme '" << cs.name << "' has invalid configuration."; 152 DCHECK_EQ(base::ToLowerASCII(cs.name), cs.name) 153 << "Scheme '" << cs.name << "' should be lower-case."; 154 DCHECK(!FindScheme(cs.name)) 155 << "Scheme '" << cs.name << "' already added."; 156 157 GetMutableSchemes().push_back(cs); 158 } 159 FindScheme(base::StringPiece name)160const CustomScheme* CustomScheme::FindScheme(base::StringPiece name) 161 { 162 for (const CustomScheme& cs : GetSchemes()) 163 if (base::LowerCaseEqualsASCII(name, cs.name)) 164 return &cs; 165 return nullptr; 166 } 167 168 const char CustomScheme::kCommandLineFlag[] = "webengine-schemes"; 169 SaveSchemes(base::CommandLine * command_line)170void CustomScheme::SaveSchemes(base::CommandLine* command_line) 171 { 172 std::string serialized; 173 174 for (const CustomScheme& cs : GetSchemes()) { 175 if (!serialized.empty()) 176 serialized += ';'; 177 serialized += ToString(cs); 178 } 179 180 command_line->AppendSwitchASCII(kCommandLineFlag, std::move(serialized)); 181 } 182 LoadSchemes(const base::CommandLine * command_line)183void CustomScheme::LoadSchemes(const base::CommandLine* command_line) 184 { 185 std::string serialized = command_line->GetSwitchValueASCII(kCommandLineFlag); 186 Parser parser; 187 for (char ch : serialized) 188 parser.CharacterArrived(ch); 189 parser.EndReached(); 190 } 191 192 } // namespace url 193