@@ -122,17 +122,66 @@ impl Page {
122122 async fn hide_plugins ( & self ) -> Result < ( ) , CdpError > {
123123 self . execute ( AddScriptToEvaluateOnNewDocumentParams {
124124 source : "
125- Object.defineProperty(
126- navigator,
127- 'plugins',
128- {
129- get: () => [
130- { filename: 'internal-pdf-viewer' },
131- { filename: 'adsfkjlkjhalkh' },
132- { filename: 'internal-nacl-plugin '}
133- ],
125+ // Create a proper PluginArray-like object (NOT an Array!)
126+ // Key insight: PluginArray is array-like but Array.isArray() returns false
127+ const makePlugin = (name, filename, description) => {
128+ const plugin = Object.create(Plugin.prototype);
129+
130+ Object.defineProperties(plugin, {
131+ name: { value: name, enumerable: true },
132+ filename: { value: filename, enumerable: true },
133+ description: { value: description, enumerable: true },
134+ length: { value: 1, enumerable: true },
135+ 0: { value: { type: 'application/pdf', suffixes: 'pdf', description }, enumerable: true }
136+ });
137+ return plugin;
138+ };
139+
140+ // Create the fake PluginArray using the real PluginArray prototype
141+ const fakePlugins = Object.create(PluginArray.prototype);
142+ const plugins = [
143+ makePlugin('PDF Viewer', 'internal-pdf-viewer', 'Portable Document Format'),
144+ makePlugin('Chrome PDF Viewer', 'internal-pdf-viewer', 'Portable Document Format'),
145+ makePlugin('Chromium PDF Viewer', 'internal-pdf-viewer', 'Portable Document Format'),
146+ makePlugin('Microsoft Edge PDF Viewer', 'internal-pdf-viewer', 'Portable Document Format'),
147+ makePlugin('WebKit built-in PDF', 'internal-pdf-viewer', 'Portable Document Format')
148+ ];
149+ // Add indexed access and length
150+ plugins.forEach((p, i) => {
151+ Object.defineProperty(fakePlugins, i, { value: p, enumerable: true });
152+ });
153+ Object.defineProperty(fakePlugins, 'length', { value: plugins.length, enumerable: true });
154+ // Add methods
155+ Object.defineProperty(fakePlugins, 'item', {
156+ value: function(index) { return this[index] || null; },
157+ enumerable: false
158+ });
159+ Object.defineProperty(fakePlugins, 'namedItem', {
160+ value: function(name) {
161+ for (let i = 0; i < this.length; i++) {
162+ if (this[i].name === name) return this[i];
134163 }
135- );
164+ return null;
165+ },
166+ enumerable: false
167+ });
168+
169+ Object.defineProperty(fakePlugins, 'refresh', {
170+ value: function() {},
171+ enumerable: false
172+ });
173+ // Make it iterable
174+ Object.defineProperty(fakePlugins, Symbol.iterator, {
175+ value: function* () {
176+ for (let i = 0; i < this.length; i++) yield this[i];
177+ },
178+ enumerable: false
179+ });
180+
181+ Object.defineProperty(Object.getPrototypeOf(navigator), 'plugins', {
182+ get: () => fakePlugins,
183+ configurable: true
184+ });
136185 "
137186 . to_string ( ) ,
138187 world_name : None ,
@@ -163,14 +212,14 @@ impl Page {
163212 Ok ( ( ) )
164213 }
165214
166- /// Removes the `navigator.webdriver` property on frame creation
215+ /// Sets the `navigator.webdriver` property to `false` on frame creation
167216 async fn hide_webdriver ( & self ) -> Result < ( ) , CdpError > {
168217 self . execute ( AddScriptToEvaluateOnNewDocumentParams {
169218 source : "
170219 Object.defineProperty(
171- navigator,
220+ Object.getPrototypeOf( navigator), //Fixes 'Object.getOwnPropertyNames(navigator) should return empty array'
172221 'webdriver',
173- { get: () => undefined }
222+ { get: () => false } //Fixes 'property should not be undefined'
174223 );
175224 "
176225 . to_string ( ) ,
@@ -1219,8 +1268,8 @@ impl Page {
12191268 /// # async fn example(page: Page) -> Result<(), Box<dyn std::error::Error>> {
12201269 /// // Hide webdriver property for stealth scraping
12211270 /// page.evaluate_on_new_document(r#"
1222- /// Object.defineProperty(navigator, 'webdriver', {
1223- /// get: () => undefined
1271+ /// Object.defineProperty(Object.getPrototypeOf( navigator) , 'webdriver', {
1272+ /// get: () => false
12241273 /// });
12251274 /// "#).await?;
12261275 /// # Ok(())
0 commit comments