diff options
-rw-r--r-- | src/app.c | 1 | ||||
-rw-r--r-- | src/history.c | 9 | ||||
-rw-r--r-- | src/history.h | 1 | ||||
-rw-r--r-- | src/media.c | 11 | ||||
-rw-r--r-- | src/media.h | 3 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 60 |
6 files changed, 69 insertions, 16 deletions
@@ -2828,6 +2828,7 @@ iBool handleCommand_App(const char *cmd) { | |||
2828 | const iBool isHistory = argLabel_Command(cmd, "history") != 0; | 2828 | const iBool isHistory = argLabel_Command(cmd, "history") != 0; |
2829 | int redirectCount = argLabel_Command(cmd, "redirect"); | 2829 | int redirectCount = argLabel_Command(cmd, "redirect"); |
2830 | if (!isHistory) { | 2830 | if (!isHistory) { |
2831 | /* TODO: Shouldn't DocumentWidget manage history on its own? */ | ||
2831 | if (redirectCount) { | 2832 | if (redirectCount) { |
2832 | replace_History(history, url); | 2833 | replace_History(history, url); |
2833 | } | 2834 | } |
diff --git a/src/history.c b/src/history.c index 837b38cb..56454009 100644 --- a/src/history.c +++ b/src/history.c | |||
@@ -310,6 +310,15 @@ void add_History(iHistory *d, const iString *url) { | |||
310 | unlock_Mutex(d->mtx); | 310 | unlock_Mutex(d->mtx); |
311 | } | 311 | } |
312 | 312 | ||
313 | void undo_History(iHistory *d) { | ||
314 | lock_Mutex(d->mtx); | ||
315 | if (!isEmpty_Array(&d->recent) || d->recentPos != 0) { | ||
316 | deinit_RecentUrl(back_Array(&d->recent)); | ||
317 | popBack_Array(&d->recent); | ||
318 | } | ||
319 | unlock_Mutex(d->mtx); | ||
320 | } | ||
321 | |||
313 | iRecentUrl *precedingLocked_History(iHistory *d) { | 322 | iRecentUrl *precedingLocked_History(iHistory *d) { |
314 | /* NOTE: Manual lock and unlock are required when using this; returning an internal pointer. */ | 323 | /* NOTE: Manual lock and unlock are required when using this; returning an internal pointer. */ |
315 | iBool ok = iFalse; | 324 | iBool ok = iFalse; |
diff --git a/src/history.h b/src/history.h index 383c132b..7959187d 100644 --- a/src/history.h +++ b/src/history.h | |||
@@ -61,6 +61,7 @@ void unlock_History (iHistory *); | |||
61 | 61 | ||
62 | void clear_History (iHistory *); | 62 | void clear_History (iHistory *); |
63 | void add_History (iHistory *, const iString *url); | 63 | void add_History (iHistory *, const iString *url); |
64 | void undo_History (iHistory *); /* removes the most recent URL */ | ||
64 | void replace_History (iHistory *, const iString *url); | 65 | void replace_History (iHistory *, const iString *url); |
65 | void setCachedResponse_History (iHistory *, const iGmResponse *response); | 66 | void setCachedResponse_History (iHistory *, const iGmResponse *response); |
66 | void setCachedDocument_History (iHistory *, iGmDocument *doc); | 67 | void setCachedDocument_History (iHistory *, iGmDocument *doc); |
diff --git a/src/media.c b/src/media.c index c02090b0..4940c13e 100644 --- a/src/media.c +++ b/src/media.c | |||
@@ -629,6 +629,17 @@ void deinit_MediaRequest(iMediaRequest *d) { | |||
629 | iRelease(d->req); | 629 | iRelease(d->req); |
630 | } | 630 | } |
631 | 631 | ||
632 | iMediaRequest *newReused_MediaRequest(iDocumentWidget *doc, unsigned int linkId, | ||
633 | iGmRequest *request) { | ||
634 | iMediaRequest *d = new_Object(&Class_MediaRequest); | ||
635 | d->doc = doc; | ||
636 | d->linkId = linkId; | ||
637 | d->req = request; /* takes ownership */ | ||
638 | iConnect(GmRequest, d->req, updated, d, updated_MediaRequest_); | ||
639 | iConnect(GmRequest, d->req, finished, d, finished_MediaRequest_); | ||
640 | return d; | ||
641 | } | ||
642 | |||
632 | iDefineObjectConstructionArgs(MediaRequest, | 643 | iDefineObjectConstructionArgs(MediaRequest, |
633 | (iDocumentWidget *doc, unsigned int linkId, const iString *url, | 644 | (iDocumentWidget *doc, unsigned int linkId, const iString *url, |
634 | iBool enableFilters), | 645 | iBool enableFilters), |
diff --git a/src/media.h b/src/media.h index 3b329716..584c77eb 100644 --- a/src/media.h +++ b/src/media.h | |||
@@ -123,3 +123,6 @@ struct Impl_MediaRequest { | |||
123 | 123 | ||
124 | iDeclareObjectConstructionArgs(MediaRequest, iDocumentWidget *doc, unsigned int linkId, | 124 | iDeclareObjectConstructionArgs(MediaRequest, iDocumentWidget *doc, unsigned int linkId, |
125 | const iString *url, iBool enableFilters) | 125 | const iString *url, iBool enableFilters) |
126 | |||
127 | iMediaRequest * newReused_MediaRequest (iDocumentWidget *doc, unsigned int linkId, | ||
128 | iGmRequest *request); | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index cbd32066..f9efdd28 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -20,8 +20,8 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ |
22 | 22 | ||
23 | /* TODO: This file is a little (!) too large. DocumentWidget could be split into | 23 | /* TODO: Move DocumentView into a source file of its own. Consider cleaning up the network |
24 | a couple of smaller objects. One for rendering the document, for instance. */ | 24 | request handling. */ |
25 | 25 | ||
26 | #include "documentwidget.h" | 26 | #include "documentwidget.h" |
27 | 27 | ||
@@ -297,6 +297,7 @@ struct Impl_DocumentWidget { | |||
297 | /* Network request: */ | 297 | /* Network request: */ |
298 | enum iRequestState state; | 298 | enum iRequestState state; |
299 | iGmRequest * request; | 299 | iGmRequest * request; |
300 | iGmLinkId requestLinkId; /* ID of the link that initiated the current request */ | ||
300 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ | 301 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ |
301 | int certFlags; | 302 | int certFlags; |
302 | iBlock * certFingerprint; | 303 | iBlock * certFingerprint; |
@@ -1079,9 +1080,9 @@ static size_t visibleLinkOrdinal_DocumentView_(const iDocumentView *d, iGmLinkId | |||
1079 | } | 1080 | } |
1080 | 1081 | ||
1081 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { | 1082 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { |
1082 | d->foundMark = iNullRange; | 1083 | d->foundMark = iNullRange; |
1083 | d->selectMark = iNullRange; | 1084 | d->selectMark = iNullRange; |
1084 | d->contextLink = NULL; | 1085 | d->contextLink = NULL; |
1085 | documentRunsInvalidated_DocumentView_(&d->view); | 1086 | documentRunsInvalidated_DocumentView_(&d->view); |
1086 | } | 1087 | } |
1087 | 1088 | ||
@@ -2136,6 +2137,7 @@ static void updateBanner_DocumentWidget_(iDocumentWidget *d) { | |||
2136 | static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { | 2137 | static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { |
2137 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse); | 2138 | iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse); |
2138 | setFlags_Widget(as_Widget(d), touchDrag_WidgetFlag, iFalse); | 2139 | setFlags_Widget(as_Widget(d), touchDrag_WidgetFlag, iFalse); |
2140 | d->requestLinkId = 0; | ||
2139 | updateVisitedLinks_GmDocument(d->view.doc); | 2141 | updateVisitedLinks_GmDocument(d->view.doc); |
2140 | documentRunsInvalidated_DocumentWidget_(d); | 2142 | documentRunsInvalidated_DocumentWidget_(d); |
2141 | updateWindowTitle_DocumentWidget_(d); | 2143 | updateWindowTitle_DocumentWidget_(d); |
@@ -2746,11 +2748,8 @@ static void fetch_DocumentWidget_(iDocumentWidget *d) { | |||
2746 | "document.request.started doc:%p url:%s", | 2748 | "document.request.started doc:%p url:%s", |
2747 | d, | 2749 | d, |
2748 | cstr_String(d->mod.url)); | 2750 | cstr_String(d->mod.url)); |
2749 | clear_ObjectList(d->media); | ||
2750 | d->certFlags = 0; | ||
2751 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 2751 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
2752 | d->flags &= ~drawDownloadCounter_DocumentWidgetFlag; | 2752 | d->flags &= ~drawDownloadCounter_DocumentWidgetFlag; |
2753 | d->flags &= ~fromCache_DocumentWidgetFlag; | ||
2754 | d->state = fetching_RequestState; | 2753 | d->state = fetching_RequestState; |
2755 | set_Atomic(&d->isRequestUpdated, iFalse); | 2754 | set_Atomic(&d->isRequestUpdated, iFalse); |
2756 | d->request = new_GmRequest(certs_App()); | 2755 | d->request = new_GmRequest(certs_App()); |
@@ -3043,6 +3042,14 @@ static const char *humanReadableStatusCode_(enum iGmStatusCode code) { | |||
3043 | return format_CStr("%d ", code); | 3042 | return format_CStr("%d ", code); |
3044 | } | 3043 | } |
3045 | 3044 | ||
3045 | static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { | ||
3046 | url = canonicalUrl_String(url); | ||
3047 | if (!equal_String(d->mod.url, url)) { | ||
3048 | d->flags |= urlChanged_DocumentWidgetFlag; | ||
3049 | set_String(d->mod.url, url); | ||
3050 | } | ||
3051 | } | ||
3052 | |||
3046 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | 3053 | static void checkResponse_DocumentWidget_(iDocumentWidget *d) { |
3047 | if (!d->request) { | 3054 | if (!d->request) { |
3048 | return; | 3055 | return; |
@@ -3053,8 +3060,35 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { | |||
3053 | } | 3060 | } |
3054 | iGmResponse *resp = lockResponse_GmRequest(d->request); | 3061 | iGmResponse *resp = lockResponse_GmRequest(d->request); |
3055 | if (d->state == fetching_RequestState) { | 3062 | if (d->state == fetching_RequestState) { |
3063 | /* Under certain conditions, inline any image response into the current document. */ | ||
3064 | if (d->requestLinkId && | ||
3065 | isSuccess_GmStatusCode(d->sourceStatus) && | ||
3066 | startsWithCase_String(&d->sourceMime, "text/gemini") && | ||
3067 | isSuccess_GmStatusCode(statusCode) && | ||
3068 | startsWithCase_String(&resp->meta, "image/")) { | ||
3069 | /* This request is turned into a new media request in the current document. */ | ||
3070 | iDisconnect(GmRequest, d->request, updated, d, requestUpdated_DocumentWidget_); | ||
3071 | iDisconnect(GmRequest, d->request, finished, d, requestFinished_DocumentWidget_); | ||
3072 | iMediaRequest *mr = newReused_MediaRequest(d, d->requestLinkId, d->request); | ||
3073 | unlockResponse_GmRequest(d->request); | ||
3074 | d->request = NULL; /* ownership moved */ | ||
3075 | postCommand_Widget(d, "document.request.cancelled doc:%p", d); | ||
3076 | pushBack_ObjectList(d->media, mr); | ||
3077 | iRelease(mr); | ||
3078 | /* Reset the fetch state, returning to the originating page. */ | ||
3079 | d->state = ready_RequestState; | ||
3080 | if (equal_String(&mostRecentUrl_History(d->mod.history)->url, url_GmRequest(mr->req))) { | ||
3081 | undo_History(d->mod.history); | ||
3082 | } | ||
3083 | setUrl_DocumentWidget_(d, url_GmDocument(d->view.doc)); | ||
3084 | updateFetchProgress_DocumentWidget_(d); | ||
3085 | postCommand_Widget(d, "media.updated link:%u request:%p", d->requestLinkId, mr); | ||
3086 | return; | ||
3087 | } | ||
3088 | /* Get ready for the incoming new document. */ | ||
3056 | d->state = receivedPartialResponse_RequestState; | 3089 | d->state = receivedPartialResponse_RequestState; |
3057 | d->flags &= ~fromCache_DocumentWidgetFlag; | 3090 | d->flags &= ~fromCache_DocumentWidgetFlag; |
3091 | clear_ObjectList(d->media); | ||
3058 | updateTrust_DocumentWidget_(d, resp); | 3092 | updateTrust_DocumentWidget_(d, resp); |
3059 | if (isSuccess_GmStatusCode(statusCode)) { | 3093 | if (isSuccess_GmStatusCode(statusCode)) { |
3060 | clear_Banner(d->banner); | 3094 | clear_Banner(d->banner); |
@@ -3441,14 +3475,6 @@ static iWidget *swipeParent_DocumentWidget_(iDocumentWidget *d) { | |||
3441 | return findChild_Widget(as_Widget(d)->root->widget, "doctabs"); | 3475 | return findChild_Widget(as_Widget(d)->root->widget, "doctabs"); |
3442 | } | 3476 | } |
3443 | 3477 | ||
3444 | static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { | ||
3445 | url = canonicalUrl_String(url); | ||
3446 | if (!equal_String(d->mod.url, url)) { | ||
3447 | d->flags |= urlChanged_DocumentWidgetFlag; | ||
3448 | set_String(d->mod.url, url); | ||
3449 | } | ||
3450 | } | ||
3451 | |||
3452 | static void setupSwipeOverlay_DocumentWidget_(iDocumentWidget *d, iWidget *overlay) { | 3478 | static void setupSwipeOverlay_DocumentWidget_(iDocumentWidget *d, iWidget *overlay) { |
3453 | iWidget *w = as_Widget(d); | 3479 | iWidget *w = as_Widget(d); |
3454 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); | 3480 | iWidget *swipeParent = swipeParent_DocumentWidget_(d); |
@@ -4598,6 +4624,7 @@ static void interactingWithLink_DocumentWidget_(iDocumentWidget *d, iGmLinkId id | |||
4598 | clear_String(&d->linePrecedingLink); | 4624 | clear_String(&d->linePrecedingLink); |
4599 | return; | 4625 | return; |
4600 | } | 4626 | } |
4627 | d->requestLinkId = id; | ||
4601 | const char *start = range_String(source_GmDocument(d->view.doc)).start; | 4628 | const char *start = range_String(source_GmDocument(d->view.doc)).start; |
4602 | /* Find the preceding line. This is offered as a prefill option for a possible input query. */ | 4629 | /* Find the preceding line. This is offered as a prefill option for a possible input query. */ |
4603 | while (loc.start > start && *loc.start != '\n') { | 4630 | while (loc.start > start && *loc.start != '\n') { |
@@ -5526,6 +5553,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
5526 | d->state = blank_RequestState; | 5553 | d->state = blank_RequestState; |
5527 | d->titleUser = new_String(); | 5554 | d->titleUser = new_String(); |
5528 | d->request = NULL; | 5555 | d->request = NULL; |
5556 | d->requestLinkId = 0; | ||
5529 | d->isRequestUpdated = iFalse; | 5557 | d->isRequestUpdated = iFalse; |
5530 | d->media = new_ObjectList(); | 5558 | d->media = new_ObjectList(); |
5531 | d->banner = new_Banner(); | 5559 | d->banner = new_Banner(); |