diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/css/cascade.nim | 5 | ||||
-rw-r--r-- | src/html/dom.nim | 60 | ||||
-rw-r--r-- | src/img/bitmap.nim | 3 | ||||
-rw-r--r-- | src/io/bufreader.nim | 11 | ||||
-rw-r--r-- | src/io/bufwriter.nim | 3 | ||||
-rw-r--r-- | src/layout/engine.nim | 30 | ||||
-rw-r--r-- | src/layout/renderdocument.nim | 4 | ||||
-rw-r--r-- | src/loader/loader.nim | 96 | ||||
-rw-r--r-- | src/loader/loaderhandle.nim | 19 | ||||
-rw-r--r-- | src/loader/response.nim | 13 | ||||
-rw-r--r-- | src/local/pager.nim | 45 | ||||
-rw-r--r-- | src/server/buffer.nim | 1 | ||||
-rw-r--r-- | src/utils/sandbox.nim | 2 |
13 files changed, 174 insertions, 118 deletions
diff --git a/src/css/cascade.nim b/src/css/cascade.nim index 78953303..f39a335c 100644 --- a/src/css/cascade.nim +++ b/src/css/cascade.nim @@ -570,8 +570,9 @@ proc appendChildren(styledStack: var seq[CascadeFrame]; frame: CascadeFrame; let elem = Element(styledChild.node) styledStack.stackAppend(frame, styledChild, peAfter, idx, parentDeclMap) case elem.tagType - of TAG_TEXTAREA: styledStack.stackAppend(frame, styledChild, peTextareaText, idx) - of TAG_IMG, TAG_IMAGE: styledStack.stackAppend(frame, styledChild, peImage, idx) + of TAG_TEXTAREA: + styledStack.stackAppend(frame, styledChild, peTextareaText, idx) + of TAG_IMG: styledStack.stackAppend(frame, styledChild, peImage, idx) of TAG_VIDEO: styledStack.stackAppend(frame, styledChild, peVideo, idx) of TAG_AUDIO: styledStack.stackAppend(frame, styledChild, peAudio, idx) of TAG_BR: styledStack.stackAppend(frame, styledChild, peNewline, idx) diff --git a/src/html/dom.nim b/src/html/dom.nim index 3e5dddd0..503bdf27 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -2911,46 +2911,52 @@ proc loadResource(window: Window; image: HTMLImageElement) = # mixed content :/ #TODO maybe do this in loader? url.scheme = "https" - let p = window.loader.fetch(newRequest(url)) - .then(proc(res: JSResult[Response]): Promise[JSResult[Response]] = + let p = window.loader.fetch(newRequest(url)).then( + proc(res: JSResult[Response]): EmptyPromise = if res.isNone: return let response = res.get let contentType = response.getContentType("image/x-unknown") if contentType.until('/') != "image": return + let cacheId = window.loader.addCacheFile(response.outputId, + window.loader.clientPid) let request = newRequest( newURL("img-codec+" & contentType.after('/') & ":decode").get, httpMethod = hmPost, - body = RequestBody(t: rbtOutput, outputId: response.outputId) + headers = newHeaders({"Cha-Image-Info-Only": "1"}), + body = RequestBody(t: rbtOutput, outputId: response.outputId), ) let r = window.loader.fetch(request) - window.loader.resume(response.outputId) + response.resume() response.unregisterFun() response.body.sclose() - return r - ).then(proc(res: JSResult[Response]): EmptyPromise = - if res.isNone: - return - let response = res.get - # we can close immediately; loader will not clean this output up until - # the `resume' command in pager. - response.unregisterFun() - response.body.sclose() - if "Cha-Image-Dimensions" notin response.headers.table: - window.console.error("Cha-Image-Dimensions missing in", $response.url) - return - let dims = response.headers.table["Cha-Image-Dimensions"][0] - let width = parseUInt64(dims.until('x'), allowSign = false) - let height = parseUInt64(dims.after('x'), allowSign = false) - if width.isNone or height.isNone: - window.console.error("wrong Cha-Image-Dimensions in", $response.url) - return - image.bitmap = NetworkBitmap( - width: width.get, - height: height.get, - outputId: response.outputId, - imageId: window.getImageId() + return r.then(proc(res: JSResult[Response]): EmptyPromise = + if res.isNone: + window.console.error("Failed to decode", $response.url) + return + let response = res.get + # close immediately; all data we're interested in is in the headers. + response.resume() + response.unregisterFun() + response.body.sclose() + if "Cha-Image-Dimensions" notin response.headers.table: + window.console.error("Cha-Image-Dimensions missing in", + $response.url) + return + let dims = response.headers.table["Cha-Image-Dimensions"][0] + let width = parseUInt64(dims.until('x'), allowSign = false) + let height = parseUInt64(dims.after('x'), allowSign = false) + if width.isNone or height.isNone: + window.console.error("wrong Cha-Image-Dimensions in", $response.url) + return + image.bitmap = NetworkBitmap( + width: width.get, + height: height.get, + cacheId: cacheId, + imageId: window.getImageId(), + contentType: contentType + ) ) ) window.loadingResourcePromises.add(p) diff --git a/src/img/bitmap.nim b/src/img/bitmap.nim index 70244643..c5df19fc 100644 --- a/src/img/bitmap.nim +++ b/src/img/bitmap.nim @@ -9,8 +9,9 @@ type ImageBitmap* = ref object of Bitmap NetworkBitmap* = ref object of Bitmap - outputId*: int + cacheId*: int imageId*: int + contentType*: string proc newBitmap*(width, height: uint64): ImageBitmap = return ImageBitmap( diff --git a/src/io/bufreader.nim b/src/io/bufreader.nim index bd2486be..5f522666 100644 --- a/src/io/bufreader.nim +++ b/src/io/bufreader.nim @@ -230,13 +230,16 @@ proc sread*(reader: var BufferedReader; bmp: var Bitmap) = ) reader.sread(bmp.px) else: - var outputId: int + var cacheId: int var imageId: int - reader.sread(outputId) + var contentType: string + reader.sread(cacheId) reader.sread(imageId) + reader.sread(contentType) bmp = NetworkBitmap( width: width, height: height, - outputId: outputId, - imageId: imageId + cacheId: cacheId, + imageId: imageId, + contentType: contentType ) diff --git a/src/io/bufwriter.nim b/src/io/bufwriter.nim index 56e30f5b..8166eab8 100644 --- a/src/io/bufwriter.nim +++ b/src/io/bufwriter.nim @@ -208,5 +208,6 @@ proc swrite*(writer: var BufferedWriter; bmp: Bitmap) = if bmp of ImageBitmap: writer.swrite(bmp.px) else: - writer.swrite(NetworkBitmap(bmp).outputId) + writer.swrite(NetworkBitmap(bmp).cacheId) writer.swrite(NetworkBitmap(bmp).imageId) + writer.swrite(NetworkBitmap(bmp).contentType) diff --git a/src/layout/engine.nim b/src/layout/engine.nim index a764faad..3acc958e 100644 --- a/src/layout/engine.nim +++ b/src/layout/engine.nim @@ -1370,7 +1370,7 @@ proc addInlineBlock(ictx: var InlineContext; state: var InlineState; ictx.whitespacenum = 0 proc addInlineImage(ictx: var InlineContext; state: var InlineState; - bmp: Bitmap) = + bmp: Bitmap; padding: LayoutUnit) = let h = int(bmp.height).toLayoutUnit().ceilTo(ictx.cellHeight) let iastate = InlineAtomState( vertalign: state.fragment.computed{"vertical-align"}, @@ -1381,6 +1381,20 @@ proc addInlineImage(ictx: var InlineContext; state: var InlineState; bmp: bmp, size: size(w = int(bmp.width), h = h), #TODO overflow ) + let computed = state.fragment.computed + let lctx = ictx.lctx + if computed{"width"}.canpx(ictx.space.w): + let w = computed{"width"}.spx(lctx, ictx.space.w, computed, padding) + if not computed{"height"}.canpx(ictx.space.h): + # maintain aspect ratio + atom.size.h = atom.size.h div atom.size.w * w + atom.size.w = w + if computed{"height"}.canpx(ictx.space.h): + let h = computed{"height"}.spx(lctx, ictx.space.h, computed, padding) + if not computed{"width"}.canpx(ictx.space.w): + # maintain aspect ratio + atom.size.w = atom.size.w div atom.size.h * h + atom.size.h = h discard ictx.addAtom(state, iastate, atom) func calcLineHeight(computed: CSSComputedValues; lctx: LayoutContext): @@ -1421,7 +1435,7 @@ proc layoutInline(ictx: var InlineContext; fragment: InlineFragment) = case fragment.t of iftNewline: ictx.flushLine(state) of iftBox: ictx.addInlineBlock(state, fragment.box) - of iftBitmap: ictx.addInlineImage(state, fragment.bmp) + of iftBitmap: ictx.addInlineImage(state, fragment.bmp, padding.sum()) of iftText: ictx.layoutText(state, fragment.text) of iftParent: for child in fragment.children: @@ -2636,13 +2650,12 @@ proc pushInline(ctx: var InnerBlockContext; fragment: InlineFragment) = proc pushInlineText(ctx: var InnerBlockContext; computed: CSSComputedValues; styledNode: StyledNode; text: string) = - let box = InlineFragment( + ctx.pushInline(InlineFragment( t: iftText, computed: computed, node: styledNode, text: text - ) - ctx.pushInline(box) + )) proc pushInlineBlock(ctx: var InnerBlockContext; styledNode: StyledNode; computed: CSSComputedValues) = @@ -2788,13 +2801,12 @@ proc buildReplacement(ctx: var InnerBlockContext; child, parent: StyledNode; ctx.pushInlineText(computed, parent, child.content.s) of ContentImage: if child.content.bmp != nil: - let wrapper = InlineFragment( + ctx.pushInline(InlineFragment( t: iftBitmap, - computed: computed, + computed: parent.computed, node: parent, bmp: child.content.bmp - ) - ctx.pushInline(wrapper) + )) else: ctx.pushInlineText(computed, parent, "[img]") of ContentVideo: diff --git a/src/layout/renderdocument.nim b/src/layout/renderdocument.nim index e45b8af4..89afde32 100644 --- a/src/layout/renderdocument.nim +++ b/src/layout/renderdocument.nim @@ -218,6 +218,8 @@ type PosBitmap* = ref object x*: int y*: int + width*: int + height*: int bmp*: Bitmap RenderState = object @@ -379,6 +381,8 @@ proc renderInlineFragment(grid: var FlexibleGrid; state: var RenderState; state.images.add(PosBitmap( x: (offset.x div state.attrs.ppc).toInt, y: (offset.y div state.attrs.ppl).toInt, + width: atom.size.w.toInt, + height: atom.size.h.toInt, bmp: atom.bmp )) if fragment.computed{"position"} != PositionStatic: diff --git a/src/loader/loader.nim b/src/loader/loader.nim index 7c76ae59..11cf714f 100644 --- a/src/loader/loader.nim +++ b/src/loader/loader.nim @@ -174,6 +174,16 @@ func findCachedHandle(ctx: LoaderContext; cacheId: int): LoaderHandle = type PushBufferResult = enum pbrDone, pbrUnregister +proc register(ctx: LoaderContext; output: OutputHandle) = + assert not output.registered + ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) + output.registered = true + +proc unregister(ctx: LoaderContext; output: OutputHandle) = + assert output.registered + ctx.selector.unregister(output.ostream.fd) + output.registered = false + # Either write data to the target output, or append it to the list of buffers to # write and register the output in our selector. proc pushBuffer(ctx: LoaderContext; output: OutputHandle; buffer: LoaderBuffer; @@ -203,8 +213,7 @@ proc pushBuffer(ctx: LoaderContext; output: OutputHandle; buffer: LoaderBuffer; if n < buffer.len: output.currentBuffer = buffer output.currentBufferIdx = n - ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) - output.registered = true + ctx.register(output) else: output.buffers.addLast(buffer) pbrDone @@ -213,8 +222,11 @@ proc getOutputId(ctx: LoaderContext): int = result = ctx.outputNum inc ctx.outputNum -proc redirectToStream(ctx: LoaderContext; output: OutputHandle; - ps: PosixStream): bool = +proc redirectToFile(ctx: LoaderContext; output: OutputHandle; + targetPath: string): bool = + let ps = newPosixStream(targetPath, O_CREAT or O_WRONLY, 0o600) + if ps == nil: + return false try: if output.currentBuffer != nil: let n = ps.sendData(output.currentBuffer, output.currentBufferIdx) @@ -227,7 +239,7 @@ proc redirectToStream(ctx: LoaderContext; output: OutputHandle; ps.sclose() return false except ErrorBrokenPipe: - # ps or output is dead; give up. + # ps is dead; give up. ps.sclose() return false if output.istreamAtEnd: @@ -241,13 +253,6 @@ proc redirectToStream(ctx: LoaderContext; output: OutputHandle; )) return true -proc redirectToFile(ctx: LoaderContext; output: OutputHandle; - targetPath: string): bool = - let ps = newPosixStream(targetPath, O_CREAT or O_WRONLY, 0o600) - if ps == nil: - return false - return ctx.redirectToStream(output, ps) - proc addCacheFile(ctx: LoaderContext; client: ClientData; output: OutputHandle): int = if output.parent != nil and output.parent.cacheId != -1: @@ -326,8 +331,7 @@ proc loadStreamRegular(ctx: LoaderContext; handle, cachedHandle: LoaderHandle) = output.parent = nil let i = handle.outputs.find(output) if output.registered: - ctx.selector.unregister(output.ostream.fd) - output.registered = false + ctx.unregister(output) handle.outputs.del(i) for output in handle.outputs: if r == hrrBrokenPipe: @@ -397,8 +401,8 @@ proc loadFromCache(ctx: LoaderContext; client: ClientData; handle: LoaderHandle; else: handle.sendResult(ERROR_URL_NOT_IN_CACHE) -proc loadResource(ctx: LoaderContext; client: ClientData; config: LoaderClientConfig; - request: Request; handle: LoaderHandle) = +proc loadResource(ctx: LoaderContext; client: ClientData; + config: LoaderClientConfig; request: Request; handle: LoaderHandle) = var redo = true var tries = 0 var prevurl: URL = nil @@ -419,16 +423,18 @@ proc loadResource(ctx: LoaderContext; client: ClientData; config: LoaderClientCo config.insecureSSLNoVerify, ostream) if handle.istream != nil: if ostream != nil: - let output = ctx.findOutput(request.body.outputId, client) - if output != nil: - if not ctx.redirectToStream(output, ostream): - # give up. - handle.rejectHandle(ERROR_FAILED_TO_REDIRECT) - return + let outputIn = ctx.findOutput(request.body.outputId, client) + if outputIn != nil: + ostream.setBlocking(false) + let output = outputIn.tee(ostream, ctx.getOutputId(), client.pid) + ctx.outputMap[ostream.fd] = output + output.suspended = false + ctx.register(output) else: ostream.sclose() ctx.addFd(handle) else: + assert ostream == nil handle.close() elif request.url.scheme == "stream": ctx.loadStream(handle, request) @@ -538,16 +544,15 @@ proc removeClient(ctx: LoaderContext; stream: SocketStream; ctx.clientData.del(pid) stream.sclose() -proc addCacheFile(ctx: LoaderContext; stream: SocketStream; +proc addCacheFile(ctx: LoaderContext; stream: SocketStream; client: ClientData; r: var BufferedReader) = var outputId: int var targetPid: int - var sourcePid: int r.sread(outputId) + #TODO get rid of targetPid r.sread(targetPid) - r.sread(sourcePid) - let sourceClient = ctx.clientData[sourcePid] - let output = ctx.findOutput(outputId, sourceClient) + doAssert ctx.isPrivileged(client) or client.pid == targetPid + let output = ctx.findOutput(outputId, client) assert output != nil let targetClient = ctx.clientData[targetPid] let id = ctx.addCacheFile(targetClient, output) @@ -613,10 +618,11 @@ proc tee(ctx: LoaderContext; stream: SocketStream; client: ClientData; var targetPid: int r.sread(sourceId) r.sread(targetPid) - let output = ctx.findOutput(sourceId, client) - if output != nil: + let outputIn = ctx.findOutput(sourceId, client) + if outputIn != nil: let id = ctx.getOutputId() - output.tee(stream, id, targetPid) + let output = outputIn.tee(stream, id, targetPid) + ctx.outputMap[output.ostream.fd] = output stream.withPacketWriter w: w.swrite(id) stream.setBlocking(false) @@ -635,8 +641,7 @@ proc suspend(ctx: LoaderContext; stream: SocketStream; client: ClientData; output.suspended = true if output.registered: # do not waste cycles trying to push into output - output.registered = false - ctx.selector.unregister(output.ostream.fd) + ctx.unregister(output) proc resume(ctx: LoaderContext; stream: SocketStream; client: ClientData; r: var BufferedReader) = @@ -646,9 +651,7 @@ proc resume(ctx: LoaderContext; stream: SocketStream; client: ClientData; let output = ctx.findOutput(id, client) if output != nil: output.suspended = false - assert not output.registered - output.registered = true - ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) + ctx.register(output) proc equalsConstantTime(a, b: ClientKey): bool = static: @@ -690,9 +693,6 @@ proc acceptConnection(ctx: LoaderContext) = of lcRemoveClient: privileged_command ctx.removeClient(stream, r) - of lcAddCacheFile: - privileged_command - ctx.addCacheFile(stream, r) of lcShareCachedItem: privileged_command ctx.shareCachedItem(stream, r) @@ -708,6 +708,8 @@ proc acceptConnection(ctx: LoaderContext) = of lcGetCacheFile: privileged_command ctx.getCacheFile(stream, client, r) + of lcAddCacheFile: + ctx.addCacheFile(stream, client, r) of lcRemoveCachedItem: ctx.removeCachedItem(stream, client, r) of lcLoad: @@ -812,8 +814,7 @@ proc handleWrite(ctx: LoaderContext; output: OutputHandle; unregWrite.add(output) else: # all buffers sent, no need to select on this output again for now - output.registered = false - ctx.selector.unregister(output.ostream.fd) + ctx.unregister(output) proc finishCycle(ctx: LoaderContext; unregRead: var seq[LoaderHandle]; unregWrite: var seq[OutputHandle]) = @@ -835,7 +836,7 @@ proc finishCycle(ctx: LoaderContext; unregRead: var seq[LoaderHandle]; for output in unregWrite: if output.ostream != nil: if output.registered: - ctx.selector.unregister(output.ostream.fd) + ctx.unregister(output) ctx.outputMap.del(output.ostream.fd) output.oclose() let handle = output.parent @@ -973,20 +974,14 @@ proc tee*(loader: FileLoader; sourceId, targetPid: int): (SocketStream, int) = r.sread(outputId) return (stream, outputId) -# sourcePid is the PID of the output's owner. This is used in pager for images, -# so that we can be sure that a container only loads images on the page that -# it owns. -proc addCacheFile*(loader: FileLoader; outputId, targetPid: int; - sourcePid = -1): int = +proc addCacheFile*(loader: FileLoader; outputId, targetPid: int): int = let stream = loader.connect() if stream == nil: return -1 - let sourcePid = if sourcePid == -1: loader.clientPid else: sourcePid stream.withLoaderPacketWriter loader, w: w.swrite(lcAddCacheFile) w.swrite(outputId) w.swrite(targetPid) - w.swrite(sourcePid) var r = stream.initPacketReader() var outputId: int r.sread(outputId) @@ -1098,12 +1093,13 @@ proc doRequest*(loader: FileLoader; request: Request): Response = stream.sclose() return response -proc shareCachedItem*(loader: FileLoader; id, targetPid: int) = +proc shareCachedItem*(loader: FileLoader; id, targetPid: int; sourcePid = -1) = let stream = loader.connect() if stream != nil: + let sourcePid = if sourcePid != -1: sourcePid else: loader.clientPid stream.withLoaderPacketWriter loader, w: w.swrite(lcShareCachedItem) - w.swrite(loader.clientPid) + w.swrite(sourcePid) w.swrite(targetPid) w.swrite(id) stream.sclose() diff --git a/src/loader/loaderhandle.nim b/src/loader/loaderhandle.nim index 13869c8f..b1b04dee 100644 --- a/src/loader/loaderhandle.nim +++ b/src/loader/loaderhandle.nim @@ -108,10 +108,11 @@ proc bufferCleared*(output: OutputHandle) = else: output.currentBuffer = nil -proc tee*(outputIn: OutputHandle; ostream: PosixStream; outputId, pid: int) = - let parent = outputIn.parent - parent.outputs.add(OutputHandle( - parent: parent, +proc tee*(outputIn: OutputHandle; ostream: PosixStream; outputId, pid: int): + OutputHandle = + assert outputIn.suspended + let output = OutputHandle( + parent: outputIn.parent, ostream: ostream, currentBuffer: outputIn.currentBuffer, currentBufferIdx: outputIn.currentBufferIdx, @@ -120,7 +121,13 @@ proc tee*(outputIn: OutputHandle; ostream: PosixStream; outputId, pid: int) = outputId: outputId, ownerPid: pid, suspended: outputIn.suspended - )) + ) + when defined(debug): + output.url = outputIn.url + if outputIn.parent != nil: + assert outputIn.parent.parser == nil + outputIn.parent.outputs.add(output) + return output template output*(handle: LoaderHandle): OutputHandle = handle.outputs[0] @@ -194,6 +201,6 @@ proc oclose*(output: OutputHandle) = proc close*(handle: LoaderHandle) = handle.iclose() for output in handle.outputs: - #TODO assert not output.registered + assert not output.registered if output.ostream != nil: output.oclose() diff --git a/src/loader/response.nim b/src/loader/response.nim index ab74571e..b8d95e36 100644 --- a/src/loader/response.nim +++ b/src/loader/response.nim @@ -129,6 +129,10 @@ proc onReadText(response: Response) = opaque.buf.setLen(olen) break +proc resume*(response: Response) = + response.resumeFun(response.outputId) + response.resumeFun = nil + proc text*(response: Response): Promise[JSResult[string]] {.jsfunc.} = if response.body == nil: let p = newPromise[JSResult[string]]() @@ -144,8 +148,7 @@ proc text*(response: Response): Promise[JSResult[string]] {.jsfunc.} = response.opaque = opaque response.onRead = onReadText response.bodyUsed = true - response.resumeFun(response.outputId) - response.resumeFun = nil + response.resume() return response.bodyRead.then(proc(): JSResult[string] = let charset = response.getCharset(CHARSET_UTF_8) ok(opaque.buf.decodeAll(charset)) @@ -183,8 +186,7 @@ proc blob*(response: Response): Promise[JSResult[Blob]] {.jsfunc.} = response.opaque = opaque response.onRead = onReadBlob response.bodyUsed = true - response.resumeFun(response.outputId) - response.resumeFun = nil + response.resume() let contentType = response.getContentType() return response.bodyRead.then(proc(): JSResult[Blob] = let p = realloc(opaque.p, opaque.len) @@ -220,8 +222,7 @@ proc saveToBitmap*(response: Response; bmp: Bitmap): EmptyPromise = response.opaque = opaque response.onRead = onReadBitmap response.bodyUsed = true - response.resumeFun(response.outputId) - response.resumeFun = nil + response.resume() return response.bodyRead proc json(ctx: JSContext; this: Response): Promise[JSResult[JSValue]] diff --git a/src/local/pager.nim b/src/local/pager.nim index 643e7689..cd0df216 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -21,6 +21,7 @@ import io/socketstream import io/stdio import io/tempfile import io/urlfilter +import layout/renderdocument import loader/connecterror import loader/headers import loader/loader @@ -472,7 +473,7 @@ proc redraw(pager: Pager) {.jsfunc.} = if pager.container.select != nil: pager.container.select.redraw = true -proc loadCachedImage(pager: Pager; container: Container; bmp: NetworkBitmap) = +proc loadCachedImage(pager: Pager; container: Container; image: PosBitmap) = #TODO this is kinda dumb, because we cannot unload cached images. # ideally the filesystem cache should serve as the only cache, but right # now it's just sort of a temporary place before the image is dumped to @@ -481,22 +482,42 @@ proc loadCachedImage(pager: Pager; container: Container; bmp: NetworkBitmap) = # load start" event in container, and then add one in the pager? # the first option seems better; it's simpler, and buffers can add arbitrary # cache files if they just tell the pager it's an image anyway. - let cacheId = pager.loader.addCacheFile(bmp.outputId, - pager.loader.clientPid, container.process) - let request = newRequest(newURL("cache:" & $cacheId).get) + let bmp = NetworkBitmap(image.bmp) + let request = newRequest(newURL("cache:" & $bmp.cacheId).get) let cachedImage = CachedImage(bmp: bmp) - pager.loader.fetch(request).then(proc(res: JSResult[Response]): EmptyPromise = + pager.loader.shareCachedItem(bmp.cacheId, pager.loader.clientPid, + container.process) + pager.loader.fetch(request).then(proc(res: JSResult[Response]): + Promise[JSResult[Response]] = if res.isNone: - let i = container.cachedImages.find(cachedImage) - container.cachedImages.del(i) - return nil - return res.get.saveToBitmap(bmp) + return + let response = res.get + let headers = newHeaders() + if uint64(image.width) != bmp.width or uint64(image.height) != bmp.height: + headers.add("Cha-Image-Target-Dimensions", $image.width & 'x' & + $image.height) + let request = newRequest( + newURL("img-codec+" & bmp.contentType.after('/') & ":decode").get, + httpMethod = hmPost, + headers = headers, + body = RequestBody(t: rbtOutput, outputId: response.outputId), + ) + let r = pager.loader.fetch(request) + response.resume() + response.unregisterFun() + response.body.sclose() + return r + ).then(proc(res: JSResult[Response]): EmptyPromise = + let response = res.get + # take target sizes + bmp.width = uint64(image.width) + bmp.height = uint64(image.height) + return response.saveToBitmap(bmp) ).then(proc() = container.redraw = true cachedImage.loaded = true - pager.loader.removeCachedItem(cacheId) + pager.loader.removeCachedItem(bmp.cacheId) ) - pager.loader.resume(bmp.outputId) # get rid of dangling output container.cachedImages.add(cachedImage) proc initImages(pager: Pager; container: Container) = @@ -509,7 +530,7 @@ proc initImages(pager: Pager; container: Container) = let cached = container.findCachedImage(bmp.imageId) imageId = bmp.imageId if cached == nil: - pager.loadCachedImage(container, bmp) + pager.loadCachedImage(container, image) continue image.bmp = cached.bmp if not cached.loaded: diff --git a/src/server/buffer.nim b/src/server/buffer.nim index df0cb0ae..f14cda60 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -1022,6 +1022,7 @@ proc clone*(buffer: Buffer; newurl: URL): int {.proxy.} = let c = ps.sreadChar() assert c == char(0) ps.sclose() + #TODO share cached images with new buffer buffer.loader.resume(ids) return pid diff --git a/src/utils/sandbox.nim b/src/utils/sandbox.nim index ce9b194e..d4312a49 100644 --- a/src/utils/sandbox.nim +++ b/src/utils/sandbox.nim @@ -89,6 +89,7 @@ elif defined(linux) and not disableSandbox: "getrlimit", # glibc uses it after fork it seems "getsockname", # Nim needs it for connecting "gettimeofday", # used by QuickJS in Date.now() + "lseek", # glibc calls lseek on open files at exit "mmap", # memory allocation "mmap2", # memory allocation "mremap", # memory allocation @@ -131,6 +132,7 @@ elif defined(linux) and not disableSandbox: const allowList = [ cstring"close", "exit_group", # duh "read", "write", "recv", "send", "recvfrom", "sendto", # socket i/o + "lseek", # glibc calls lseek on open files at exit "fcntl", "fcntl64", # so we can set nonblock etc. "mmap", "mmap2", "mremap", "munmap", "brk", # memory allocation "poll", # curl needs poll |