Skip to content

Commit caa564f

Browse files
Cancel pending style request when loading style JSON (#3709)
Co-authored-by: Tim Sylvester <[email protected]>
1 parent 84df7b1 commit caa564f

File tree

7 files changed

+124
-0
lines changed

7 files changed

+124
-0
lines changed

include/mbgl/style/style.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ class Style {
7171
void addLayer(std::unique_ptr<Layer>, const std::optional<std::string>& beforeLayerID = std::nullopt);
7272
std::unique_ptr<Layer> removeLayer(const std::string& layerID);
7373

74+
void cancelPendingRequests();
75+
7476
// Private implementation
7577
class Impl;
7678
const std::unique_ptr<Impl> impl;

src/mbgl/style/style_impl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,15 @@ Style::Impl::Impl(std::shared_ptr<FileSource> fileSource_, float pixelRatio, con
3939

4040
Style::Impl::~Impl() = default;
4141

42+
void Style::Impl::cancelPendingRequest() noexcept {
43+
styleRequest.reset();
44+
}
45+
4246
void Style::Impl::loadJSON(const std::string& json_) {
4347
lastError = nullptr;
4448
observer->onStyleLoading();
4549

50+
cancelPendingRequest();
4651
url.clear();
4752
parse(json_);
4853
}

src/mbgl/style/style_impl.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ class Style::Impl : public SpriteLoaderObserver,
4545
void loadJSON(const std::string&);
4646
void loadURL(const std::string&);
4747

48+
/**
49+
* @brief Cancels any pending style request.
50+
*
51+
* This will cancel any in-progress style URL load. Has no effect if no
52+
* style load is in progress.
53+
*/
54+
void cancelPendingRequest() noexcept;
55+
4856
std::string getJSON() const;
4957
std::string getURL() const;
5058

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ add_library(
2929
${PROJECT_SOURCE_DIR}/test/src/mbgl/test/getrss.cpp
3030
${PROJECT_SOURCE_DIR}/test/src/mbgl/test/sqlite3_test_fs.cpp
3131
${PROJECT_SOURCE_DIR}/test/src/mbgl/test/stub_file_source.cpp
32+
${PROJECT_SOURCE_DIR}/test/src/mbgl/test/delayed_file_source.cpp
3233
${PROJECT_SOURCE_DIR}/test/src/mbgl/test/test.cpp
3334
${PROJECT_SOURCE_DIR}/test/src/mbgl/test/util.cpp
3435
${PROJECT_SOURCE_DIR}/test/storage/asset_file_source.test.cpp
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <mbgl/test/delayed_file_source.hpp>
2+
3+
namespace mbgl {
4+
5+
DelayedFileSource::AsyncRequestImpl::AsyncRequestImpl(DelayedFileSource& fs, Callback callback_)
6+
: fileSource(fs),
7+
callback(std::move(callback_)) {}
8+
9+
void DelayedFileSource::AsyncRequestImpl::respond() {
10+
if (callback) {
11+
callback(*fileSource.response);
12+
}
13+
}
14+
15+
DelayedFileSource::DelayedFileSource()
16+
: StubFileSource(StubFileSource::ResponseType::Synchronous) {}
17+
18+
std::unique_ptr<AsyncRequest> DelayedFileSource::request(const Resource&, Callback callback) {
19+
auto req = std::make_unique<AsyncRequestImpl>(*this, std::move(callback));
20+
pendingRequest = req.get();
21+
return req;
22+
}
23+
24+
void DelayedFileSource::respondToRequest(const std::string& data) {
25+
response = Response();
26+
response->data = std::make_shared<std::string>(data);
27+
if (pendingRequest) {
28+
pendingRequest->respond();
29+
}
30+
}
31+
32+
} // namespace mbgl
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#pragma once
2+
3+
#include <mbgl/storage/file_source.hpp>
4+
#include <mbgl/util/async_request.hpp>
5+
#include <mbgl/test/stub_file_source.hpp>
6+
#include <memory>
7+
#include <string>
8+
#include <optional>
9+
10+
namespace mbgl {
11+
12+
/*
13+
This is a file source that allows control of when a request is responded to.
14+
*/
15+
class DelayedFileSource : public StubFileSource {
16+
public:
17+
DelayedFileSource();
18+
~DelayedFileSource() override = default;
19+
20+
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
21+
22+
// Custom method to respond to pending request
23+
void respondToRequest(const std::string& data);
24+
25+
private:
26+
class AsyncRequestImpl : public AsyncRequest {
27+
public:
28+
using Callback = std::function<void(const Response&)>;
29+
30+
AsyncRequestImpl(DelayedFileSource& fs, Callback callback_);
31+
void respond();
32+
33+
private:
34+
DelayedFileSource& fileSource;
35+
Callback callback;
36+
};
37+
38+
friend class AsyncRequestImpl;
39+
std::optional<Response> response;
40+
AsyncRequestImpl* pendingRequest = nullptr;
41+
};
42+
43+
} // namespace mbgl

test/style/style.test.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <mbgl/style/layers/line_layer.hpp>
1010
#include <mbgl/util/io.hpp>
1111
#include <mbgl/util/run_loop.hpp>
12+
#include <mbgl/test/delayed_file_source.hpp>
1213

1314
#include <memory>
1415

@@ -110,6 +111,38 @@ TEST(Style, RemoveSourceInUse) {
110111
EXPECT_EQ(log.count(logMessage), 1u);
111112
}
112113

114+
TEST(Style, LoadJSONCancelsPendingLoadURL) {
115+
util::RunLoop loop;
116+
117+
auto fileSource = std::make_shared<::DelayedFileSource>();
118+
Style::Impl style{fileSource, 1.0, {Scheduler::GetBackground(), {}}};
119+
120+
// Start loading a URL (this will be pending)
121+
auto url = "http://some-url";
122+
style.loadURL(url);
123+
124+
// Before the URL request completes, load JSON directly
125+
const std::string json = R"STYLE({
126+
"version": 8,
127+
"name": "Test Style",
128+
"sources": {},
129+
"layers": []
130+
})STYLE";
131+
style.loadJSON(json);
132+
133+
// The style should now be loaded with our JSON content
134+
ASSERT_EQ("Test Style", style.getName());
135+
ASSERT_EQ("", style.getURL());
136+
ASSERT_TRUE(style.getJSON().find("Test Style") != std::string::npos);
137+
138+
// Now respond to the original URL request - this should be ignored
139+
fileSource->respondToRequest(R"STYLE({"version":8,"name":"Streets"})STYLE");
140+
141+
// The style should still show our JSON content, not the URL content
142+
ASSERT_EQ("Test Style", style.getName());
143+
ASSERT_NE("Streets", style.getName());
144+
}
145+
113146
TEST(Style, SourceImplsOrder) {
114147
util::RunLoop loop;
115148
auto fileSource = std::make_shared<StubFileSource>();

0 commit comments

Comments
 (0)