mirror of
				https://github.com/lightpanda-io/browser.git
				synced 2025-10-29 15:13:28 +00:00 
			
		
		
		
	Merge pull request #975 from lightpanda-io/dynamic_script
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				e2e-test / zig build release (push) Has been cancelled
				
			
		
			
				
	
				e2e-test / demo-scripts (push) Has been cancelled
				
			
		
			
				
	
				e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
				
			
		
			
				
	
				e2e-test / perf-fmt (push) Has been cancelled
				
			
		
			
				
	
				zig-test / zig build dev (push) Has been cancelled
				
			
		
			
				
	
				zig-test / browser fetch (push) Has been cancelled
				
			
		
			
				
	
				zig-test / zig test (push) Has been cancelled
				
			
		
			
				
	
				zig-test / perf-fmt (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	e2e-test / zig build release (push) Has been cancelled
				
			e2e-test / demo-scripts (push) Has been cancelled
				
			e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
				
			e2e-test / perf-fmt (push) Has been cancelled
				
			zig-test / zig build dev (push) Has been cancelled
				
			zig-test / browser fetch (push) Has been cancelled
				
			zig-test / zig test (push) Has been cancelled
				
			zig-test / perf-fmt (push) Has been cancelled
				
			Support dynamic scripts which are added to the DOM before src is set
This commit is contained in:
		| @@ -143,32 +143,7 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void { | ||||
|         return; | ||||
|     }; | ||||
|  | ||||
|     var onload: ?Script.Callback = null; | ||||
|     var onerror: ?Script.Callback = null; | ||||
|  | ||||
|     const page = self.page; | ||||
|     if (page.getNodeState(@ptrCast(element))) |se| { | ||||
|         // if the script has a node state, then it was dynamically added and thus | ||||
|         // the onload/onerror were saved in the state (if there are any) | ||||
|         if (se.onload) |function| { | ||||
|             onload = .{ .function = function }; | ||||
|         } | ||||
|         if (se.onerror) |function| { | ||||
|             onerror = .{ .function = function }; | ||||
|         } | ||||
|     } else { | ||||
|         // if the script has no node state, then it could still be dynamically | ||||
|         // added (could have been dynamically added, but no attributes were set | ||||
|         // which required a node state to be created) or it could be a inline | ||||
|         // <script>. | ||||
|         if (try parser.elementGetAttribute(element, "onload")) |string| { | ||||
|             onload = .{ .string = string }; | ||||
|         } | ||||
|         if (try parser.elementGetAttribute(element, "onerror")) |string| { | ||||
|             onerror = .{ .string = string }; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     var source: Script.Source = undefined; | ||||
|     var remote_url: ?[:0]const u8 = null; | ||||
|     if (try parser.elementGetAttribute(element, "src")) |src| { | ||||
| @@ -184,8 +159,6 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void { | ||||
|  | ||||
|     var script = Script{ | ||||
|         .kind = kind, | ||||
|         .onload = onload, | ||||
|         .onerror = onerror, | ||||
|         .element = element, | ||||
|         .source = source, | ||||
|         .url = remote_url orelse page.url.raw, | ||||
| @@ -558,8 +531,6 @@ const Script = struct { | ||||
|     is_async: bool, | ||||
|     is_defer: bool, | ||||
|     source: Source, | ||||
|     onload: ?Callback, | ||||
|     onerror: ?Callback, | ||||
|     element: *parser.Element, | ||||
|  | ||||
|     const Kind = enum { | ||||
| @@ -644,7 +615,7 @@ const Script = struct { | ||||
|     } | ||||
|  | ||||
|     fn executeCallback(self: *const Script, comptime typ: []const u8, page: *Page) void { | ||||
|         const callback = @field(self, typ) orelse return; | ||||
|         const callback = self.getCallback(typ, page) orelse return; | ||||
|  | ||||
|         switch (callback) { | ||||
|             .string => |str| { | ||||
| @@ -687,6 +658,23 @@ const Script = struct { | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn getCallback(self: *const Script, comptime typ: []const u8, page: *Page) ?Callback { | ||||
|         const element = self.element; | ||||
|         // first we check if there was an el.onload set directly on the | ||||
|         // element in JavaScript (if so, it'd be stored in the node state) | ||||
|         if (page.getNodeState(@ptrCast(element))) |se| { | ||||
|             if (@field(se, typ)) |function| { | ||||
|                 return .{ .function = function }; | ||||
|             } | ||||
|         } | ||||
|         // if we have no node state, or if the node state has no onload/onerror | ||||
|         // then check for the onload/onerror attribute | ||||
|         if (parser.elementGetAttribute(element, typ) catch null) |string| { | ||||
|             return .{ .string = string }; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const BufferPool = struct { | ||||
|   | ||||
| @@ -262,7 +262,6 @@ pub const EventHandler = struct { | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         const callback = (try listener.callback(target)) orelse return null; | ||||
|  | ||||
|         if (signal) |s| { | ||||
|   | ||||
| @@ -862,12 +862,23 @@ pub const HTMLScriptElement = struct { | ||||
|         ) orelse ""; | ||||
|     } | ||||
|  | ||||
|     pub fn set_src(self: *parser.Script, v: []const u8) !void { | ||||
|         return try parser.elementSetAttribute( | ||||
|     pub fn set_src(self: *parser.Script, v: []const u8, page: *Page) !void { | ||||
|         try parser.elementSetAttribute( | ||||
|             parser.scriptToElt(self), | ||||
|             "src", | ||||
|             v, | ||||
|         ); | ||||
|  | ||||
|         if (try Node.get_isConnected(@alignCast(@ptrCast(self)))) { | ||||
|             // There are sites which do set the src AFTER appending the script | ||||
|             // tag to the document: | ||||
|             //    const s = document.createElement('script'); | ||||
|             //    document.getElementsByTagName('body')[0].appendChild(s); | ||||
|             //    s.src = '...'; | ||||
|             // This should load the script. | ||||
|             // addFromElement protects against double execution. | ||||
|             try page.script_manager.addFromElement(@alignCast(@ptrCast(self))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn get_type(self: *parser.Script) !?[]const u8 { | ||||
| @@ -878,7 +889,7 @@ pub const HTMLScriptElement = struct { | ||||
|     } | ||||
|  | ||||
|     pub fn set_type(self: *parser.Script, v: []const u8) !void { | ||||
|         return try parser.elementSetAttribute( | ||||
|         try parser.elementSetAttribute( | ||||
|             parser.scriptToElt(self), | ||||
|             "type", | ||||
|             v, | ||||
| @@ -893,7 +904,7 @@ pub const HTMLScriptElement = struct { | ||||
|     } | ||||
|  | ||||
|     pub fn set_text(self: *parser.Script, v: []const u8) !void { | ||||
|         return try parser.elementSetAttribute( | ||||
|         try parser.elementSetAttribute( | ||||
|             parser.scriptToElt(self), | ||||
|             "text", | ||||
|             v, | ||||
| @@ -908,7 +919,7 @@ pub const HTMLScriptElement = struct { | ||||
|     } | ||||
|  | ||||
|     pub fn set_integrity(self: *parser.Script, v: []const u8) !void { | ||||
|         return try parser.elementSetAttribute( | ||||
|         try parser.elementSetAttribute( | ||||
|             parser.scriptToElt(self), | ||||
|             "integrity", | ||||
|             v, | ||||
|   | ||||
| @@ -1110,13 +1110,22 @@ fn timestamp() u32 { | ||||
| // so that's handled. And because we're only executing the inline <script> tags | ||||
| // after the document is loaded, it's ok to execute any async and defer scripts | ||||
| // immediately. | ||||
| pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) callconv(.C) void { | ||||
| pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) callconv(.c) void { | ||||
|     const self: *Page = @alignCast(@ptrCast(ctx.?)); | ||||
|  | ||||
|     if (self.delayed_navigation) { | ||||
|         // if we're planning on navigating to another page, don't run this script | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // It's posisble for a script to be dynamically added without a src. | ||||
|     //   const s = document.createElement('script'); | ||||
|     //   document.getElementsByTagName('body')[0].appendChild(s); | ||||
|     // The src can be set after. We handle that in HTMLScriptElement.set_src, | ||||
|     // but it's important we don't pass such elements to the script_manager | ||||
|     // here, else the script_manager will flag it as already-processed. | ||||
|     _ = parser.elementGetAttribute(element.?, "src") catch return orelse return; | ||||
|  | ||||
|     self.script_manager.addFromElement(element.?) catch |err| { | ||||
|         log.warn(.browser, "dynamic script", .{ .err = err }); | ||||
|     }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Karl Seguin
					Karl Seguin