| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/url/url_search_params.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "third_party/blink/renderer/core/url/dom_url.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_messages.h" |
| #include "third_party/blink/renderer/platform/network/form_data_encoder.h" |
| #include "third_party/blink/renderer/platform/weborigin/kurl.h" |
| #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| class URLSearchParamsIterationSource final |
| : public PairIterable<String, String>::IterationSource { |
| public: |
| explicit URLSearchParamsIterationSource(URLSearchParams* params) |
| : params_(params), current_(0) {} |
| |
| bool Next(ScriptState*, |
| String& key, |
| String& value, |
| ExceptionState&) override { |
| if (current_ >= params_->Params().size()) |
| return false; |
| |
| key = params_->Params()[current_].first; |
| value = params_->Params()[current_].second; |
| current_++; |
| return true; |
| } |
| |
| void Trace(blink::Visitor* visitor) override { |
| visitor->Trace(params_); |
| PairIterable<String, String>::IterationSource::Trace(visitor); |
| } |
| |
| private: |
| Member<URLSearchParams> params_; |
| size_t current_; |
| }; |
| |
| bool CompareParams(const std::pair<String, String>& a, |
| const std::pair<String, String>& b) { |
| return WTF::CodePointCompareLessThan(a.first, b.first); |
| } |
| |
| } // namespace |
| |
| URLSearchParams* URLSearchParams::Create(const URLSearchParamsInit& init, |
| ExceptionState& exception_state) { |
| if (init.IsUSVString()) { |
| const String& query_string = init.GetAsUSVString(); |
| if (query_string.StartsWith('?')) |
| return MakeGarbageCollected<URLSearchParams>(query_string.Substring(1)); |
| return MakeGarbageCollected<URLSearchParams>(query_string); |
| } |
| if (init.IsUSVStringUSVStringRecord()) { |
| return URLSearchParams::Create(init.GetAsUSVStringUSVStringRecord(), |
| exception_state); |
| } |
| if (init.IsUSVStringSequenceSequence()) { |
| return URLSearchParams::Create(init.GetAsUSVStringSequenceSequence(), |
| exception_state); |
| } |
| |
| DCHECK(init.IsNull()); |
| return MakeGarbageCollected<URLSearchParams>(String()); |
| } |
| |
| URLSearchParams* URLSearchParams::Create(const Vector<Vector<String>>& init, |
| ExceptionState& exception_state) { |
| URLSearchParams* instance = MakeGarbageCollected<URLSearchParams>(String()); |
| if (!init.size()) |
| return instance; |
| for (unsigned i = 0; i < init.size(); ++i) { |
| const Vector<String>& pair = init[i]; |
| if (pair.size() != 2) { |
| exception_state.ThrowTypeError(ExceptionMessages::FailedToConstruct( |
| "URLSearchParams", |
| "Sequence initializer must only contain pair elements")); |
| return nullptr; |
| } |
| instance->AppendWithoutUpdate(pair[0], pair[1]); |
| } |
| return instance; |
| } |
| |
| URLSearchParams::URLSearchParams(const String& query_string, DOMURL* url_object) |
| : url_object_(url_object) { |
| if (!query_string.IsEmpty()) |
| SetInputWithoutUpdate(query_string); |
| } |
| |
| URLSearchParams* URLSearchParams::Create( |
| const Vector<std::pair<String, String>>& init, |
| ExceptionState& exception_state) { |
| URLSearchParams* instance = MakeGarbageCollected<URLSearchParams>(String()); |
| if (init.IsEmpty()) |
| return instance; |
| for (const auto& item : init) |
| instance->AppendWithoutUpdate(item.first, item.second); |
| return instance; |
| } |
| |
| URLSearchParams::~URLSearchParams() = default; |
| |
| void URLSearchParams::Trace(blink::Visitor* visitor) { |
| visitor->Trace(url_object_); |
| ScriptWrappable::Trace(visitor); |
| } |
| |
| #if DCHECK_IS_ON() |
| DOMURL* URLSearchParams::UrlObject() const { |
| return url_object_; |
| } |
| #endif |
| |
| void URLSearchParams::RunUpdateSteps() { |
| if (!url_object_) |
| return; |
| |
| if (url_object_->IsInUpdate()) |
| return; |
| |
| url_object_->SetSearchInternal(toString()); |
| } |
| |
| static String DecodeString(String input) { |
| return DecodeURLEscapeSequences(input.Replace('+', ' '), |
| DecodeURLMode::kUTF8OrIsomorphic); |
| } |
| |
| void URLSearchParams::SetInputWithoutUpdate(const String& query_string) { |
| params_.clear(); |
| |
| size_t start = 0; |
| size_t query_string_length = query_string.length(); |
| while (start < query_string_length) { |
| size_t name_start = start; |
| size_t name_value_end = query_string.find('&', start); |
| if (name_value_end == kNotFound) |
| name_value_end = query_string_length; |
| if (name_value_end > start) { |
| size_t end_of_name = query_string.find('=', start); |
| if (end_of_name == kNotFound || end_of_name > name_value_end) |
| end_of_name = name_value_end; |
| String name = DecodeString( |
| query_string.Substring(name_start, end_of_name - name_start)); |
| String value; |
| if (end_of_name != name_value_end) |
| value = DecodeString(query_string.Substring( |
| end_of_name + 1, name_value_end - end_of_name - 1)); |
| if (value.IsNull()) |
| value = ""; |
| AppendWithoutUpdate(name, value); |
| } |
| start = name_value_end + 1; |
| } |
| } |
| |
| String URLSearchParams::toString() const { |
| Vector<char> encoded_data; |
| EncodeAsFormData(encoded_data); |
| return String(encoded_data.data(), encoded_data.size()); |
| } |
| |
| void URLSearchParams::AppendWithoutUpdate(const String& name, |
| const String& value) { |
| params_.push_back(std::make_pair(name, value)); |
| } |
| |
| void URLSearchParams::append(const String& name, const String& value) { |
| AppendWithoutUpdate(name, value); |
| RunUpdateSteps(); |
| } |
| |
| void URLSearchParams::deleteAllWithName(const String& name) { |
| for (size_t i = 0; i < params_.size();) { |
| if (params_[i].first == name) |
| params_.EraseAt(i); |
| else |
| i++; |
| } |
| RunUpdateSteps(); |
| } |
| |
| String URLSearchParams::get(const String& name) const { |
| for (const auto& param : params_) { |
| if (param.first == name) |
| return param.second; |
| } |
| return String(); |
| } |
| |
| Vector<String> URLSearchParams::getAll(const String& name) const { |
| Vector<String> result; |
| for (const auto& param : params_) { |
| if (param.first == name) |
| result.push_back(param.second); |
| } |
| return result; |
| } |
| |
| bool URLSearchParams::has(const String& name) const { |
| for (const auto& param : params_) { |
| if (param.first == name) |
| return true; |
| } |
| return false; |
| } |
| |
| void URLSearchParams::set(const String& name, const String& value) { |
| bool found_match = false; |
| for (size_t i = 0; i < params_.size();) { |
| // If there are any name-value whose name is 'name', set |
| // the value of the first such name-value pair to 'value' |
| // and remove the others. |
| if (params_[i].first == name) { |
| if (!found_match) { |
| params_[i++].second = value; |
| found_match = true; |
| } else { |
| params_.EraseAt(i); |
| } |
| } else { |
| i++; |
| } |
| } |
| // Otherwise, append a new name-value pair to the list. |
| if (!found_match) |
| append(name, value); |
| else |
| RunUpdateSteps(); |
| } |
| |
| void URLSearchParams::sort() { |
| std::stable_sort(params_.begin(), params_.end(), CompareParams); |
| RunUpdateSteps(); |
| } |
| |
| void URLSearchParams::EncodeAsFormData(Vector<char>& encoded_data) const { |
| for (const auto& param : params_) |
| FormDataEncoder::AddKeyValuePairAsFormData( |
| encoded_data, param.first.Utf8(), param.second.Utf8(), |
| EncodedFormData::kFormURLEncoded, FormDataEncoder::kDoNotNormalizeCRLF); |
| } |
| |
| scoped_refptr<EncodedFormData> URLSearchParams::ToEncodedFormData() const { |
| Vector<char> encoded_data; |
| EncodeAsFormData(encoded_data); |
| return EncodedFormData::Create(encoded_data.data(), encoded_data.size()); |
| } |
| |
| PairIterable<String, String>::IterationSource* URLSearchParams::StartIteration( |
| ScriptState*, |
| ExceptionState&) { |
| return MakeGarbageCollected<URLSearchParamsIterationSource>(this); |
| } |
| |
| } // namespace blink |