2006-03-20 Eric Seidel <eseidel@apple.com>
[WebKit-https.git] / WebCore / khtml / ecma / kjs_navigator.cpp
1 // -*- c-basic-offset: 2 -*-
2 /*
3  *  This file is part of the KDE libraries
4  *  Copyright (C) 2000 Harri Porten (porten@kde.org)
5  *  Copyright (c) 2000 Daniel Molkentin (molkentin@kde.org)
6  *  Copyright (c) 2000 Stefan Schimanski (schimmi@kde.org)
7  *  Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25 #include "kjs_navigator.h"
26
27 #include "AtomicString.h"
28 #include "CookieJar.h"
29 #include "Frame.h"
30 #include "Language.h"
31 #include "Node.h"
32 #include "PlugInInfoStore.h"
33 #include "kjs_binding.h"
34
35 using namespace WebCore;
36
37 namespace KJS {
38
39     class PluginBase : public JSObject {
40     public:
41         PluginBase(ExecState *exec);
42         virtual ~PluginBase();
43         
44         void refresh(bool reload);
45
46     protected:
47         static void cachePluginDataIfNecessary();
48         static Vector<PluginInfo*> *plugins;
49         static Vector<MimeClassInfo*> *mimes;
50
51     private:
52         static int m_plugInCacheRefCount;
53     };
54
55
56     class Plugins : public PluginBase {
57     public:
58         Plugins(ExecState *exec) : PluginBase(exec) {};
59         virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
60         JSValue *getValueProperty(ExecState *, int token) const;
61         virtual const ClassInfo* classInfo() const { return &info; }
62         static const ClassInfo info;
63         enum { Length, Refresh };
64     private:
65         static JSValue *indexGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
66         static JSValue *nameGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
67     };
68
69     class MimeTypes : public PluginBase {
70     public:
71         MimeTypes(ExecState *exec) : PluginBase(exec) { };
72         virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
73         JSValue *getValueProperty(ExecState *, int token) const;
74         virtual const ClassInfo* classInfo() const { return &info; }
75         static const ClassInfo info;
76         enum { Length };
77     private:
78         static JSValue *indexGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
79         static JSValue *nameGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
80     };
81
82     class Plugin : public PluginBase {
83     public:
84         Plugin(ExecState *exec, PluginInfo *info) : PluginBase(exec), m_info(info) { }
85         virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
86         JSValue *getValueProperty(ExecState *, int token) const;
87         virtual const ClassInfo* classInfo() const { return &info; }
88         static const ClassInfo info;
89         enum { Name, Filename, Description, Length };
90     private:
91         static JSValue *indexGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
92         static JSValue *nameGetter(ExecState *, JSObject *, const Identifier&, const PropertySlot&);
93
94         PluginInfo *m_info;
95     };
96
97     class MimeType : public PluginBase {
98     public:
99         MimeType( ExecState *exec, MimeClassInfo *info ) : PluginBase(exec), m_info(info) { }
100         virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
101         JSValue *getValueProperty(ExecState *, int token) const;
102         virtual const ClassInfo* classInfo() const { return &info; }
103         static const ClassInfo info;
104         enum { Type, Suffixes, Description, EnabledPlugin };
105     private:
106         MimeClassInfo *m_info;
107     };
108
109 } // namespace
110
111 #include "kjs_navigator.lut.h"
112
113 namespace KJS {
114
115 const ClassInfo Plugins::info = { "PluginArray", 0, &PluginsTable, 0 };
116 const ClassInfo MimeTypes::info = { "MimeTypeArray", 0, &MimeTypesTable, 0 };
117 const ClassInfo Plugin::info = { "Plugin", 0, &PluginTable, 0 };
118 const ClassInfo MimeType::info = { "MimeType", 0, &MimeTypeTable, 0 };
119
120 Vector<PluginInfo*> *KJS::PluginBase::plugins = 0;
121 Vector<MimeClassInfo*> *KJS::PluginBase::mimes = 0;
122 int KJS::PluginBase::m_plugInCacheRefCount = 0;
123
124 const ClassInfo Navigator::info = { "Navigator", 0, &NavigatorTable, 0 };
125 /*
126 @begin NavigatorTable 13
127   appCodeName   Navigator::AppCodeName  DontDelete|ReadOnly
128   appName       Navigator::AppName      DontDelete|ReadOnly
129   appVersion    Navigator::AppVersion   DontDelete|ReadOnly
130   language      Navigator::Language     DontDelete|ReadOnly
131   userAgent     Navigator::UserAgent    DontDelete|ReadOnly
132   platform      Navigator::Platform     DontDelete|ReadOnly
133   plugins       Navigator::_Plugins     DontDelete|ReadOnly
134   mimeTypes     Navigator::_MimeTypes   DontDelete|ReadOnly
135   product       Navigator::Product      DontDelete|ReadOnly
136   productSub    Navigator::ProductSub   DontDelete|ReadOnly
137   vendor        Navigator::Vendor       DontDelete|ReadOnly
138   cookieEnabled Navigator::CookieEnabled DontDelete|ReadOnly
139   javaEnabled   Navigator::JavaEnabled  DontDelete|Function 0
140 @end
141 */
142 KJS_IMPLEMENT_PROTOFUNC(NavigatorFunc)
143
144 Navigator::Navigator(ExecState *exec, Frame *p)
145   : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()), m_frame(p) { }
146
147 bool Navigator::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
148 {
149   return getStaticPropertySlot<NavigatorFunc, Navigator, JSObject>(exec, &NavigatorTable, this, propertyName, slot);
150 }
151
152 JSValue *Navigator::getValueProperty(ExecState *exec, int token) const
153 {
154   String userAgent = m_frame->userAgent();
155   switch (token) {
156   case AppCodeName:
157     return jsString("Mozilla");
158   case AppName:
159     // If we find "Mozilla" but not "(compatible, ...)" we are a real Netscape
160     if (userAgent.find("Mozilla") >= 0 && userAgent.find("compatible") == -1)
161       return jsString("Netscape");
162     if (userAgent.find("Microsoft") >= 0 || userAgent.find("MSIE") >= 0)
163       return jsString("Microsoft Internet Explorer");
164     return jsUndefined();
165   case AppVersion:
166     // We assume the string is something like Mozilla/version (properties)
167     return jsString(userAgent.substring(userAgent.find('/') + 1));
168   case Product:
169     // When acting normal, we pretend to be "Gecko".
170     if (userAgent.find("Mozilla/5.0") >= 0 && userAgent.find("compatible") == -1)
171         return jsString("Gecko");
172     // When spoofing as IE, we use jsUndefined().
173     return jsUndefined();
174   case ProductSub:
175     return jsString("20030107");
176   case Vendor:
177     return jsString("Apple Computer, Inc.");
178   case Language:
179     return jsString(defaultLanguage());
180   case UserAgent:
181     return jsString(userAgent);
182   case Platform:
183     if (userAgent.find("Win", 0, false) >= 0)
184       return jsString("Win32");
185     if (userAgent.find("Macintosh", 0, false) >= 0 || userAgent.find("Mac_PowerPC", 0, false) >= 0)
186       return jsString("MacPPC");
187     // FIXME: What about Macintosh Intel?
188     return jsString("X11");
189   case _Plugins:
190     return new Plugins(exec);
191   case _MimeTypes:
192     return new MimeTypes(exec);
193   case CookieEnabled:
194     return jsBoolean(cookiesEnabled());
195   }
196   return 0;
197 }
198
199 /*******************************************************************/
200
201 void PluginBase::cachePluginDataIfNecessary()
202 {
203     if (!plugins) {
204         plugins = new Vector<PluginInfo*>;
205         mimes = new Vector<MimeClassInfo*>;
206         
207         // read configuration
208         PlugInInfoStore c;
209         unsigned pluginCount = c.pluginCount();
210         for (unsigned n = 0; n < pluginCount; n++) {
211             PluginInfo* plugin = c.createPluginInfoForPluginAtIndex(n);
212             if (!plugin) 
213                 continue;
214             
215             plugins->append(plugin);
216             if (!plugin->mimes)
217                 continue;
218             
219             Vector<MimeClassInfo*>::iterator end = plugin->mimes.end();
220             for (Vector<MimeClassInfo*>::iterator itr = plugin->mimes.begin(); itr != end; itr++)
221                 mimes->append(*itr);
222         }
223     }
224 }
225
226 PluginBase::PluginBase(ExecState *exec)
227   : JSObject(exec->lexicalInterpreter()->builtinObjectPrototype() )
228 {
229     cachePluginDataIfNecessary();
230     m_plugInCacheRefCount++;
231 }
232
233 PluginBase::~PluginBase()
234 {
235     m_plugInCacheRefCount--;
236     if (!m_plugInCacheRefCount) {
237         if (plugins) {
238             deleteAllValues(*plugins);
239             delete plugins;
240             plugins = 0;
241         }
242         if (mimes) {
243             deleteAllValues(*mimes);
244             delete mimes;
245             mimes = 0;
246         }
247     }
248 }
249
250 void PluginBase::refresh(bool reload)
251 {
252     if (plugins) {
253         deleteAllValues(*plugins);
254         delete plugins;
255         plugins = 0;
256     }
257     if (mimes) {
258         deleteAllValues(*mimes);
259         delete mimes;
260         mimes = 0;
261     }
262     
263     refreshPlugins(reload);
264     cachePluginDataIfNecessary();
265 }
266
267
268 /*******************************************************************/
269
270 /*
271 @begin PluginsTable 2
272   length        Plugins::Length         DontDelete|ReadOnly
273   refresh       Plugins::Refresh        DontDelete|Function 0
274 @end
275 */
276 KJS_IMPLEMENT_PROTOFUNC(PluginsFunc)
277
278 JSValue *Plugins::getValueProperty(ExecState *exec, int token) const
279 {
280   assert(token == Length);
281   return jsNumber(plugins->size());
282 }
283
284 JSValue *Plugins::indexGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
285 {
286     return new Plugin(exec, plugins->at(slot.index()));
287 }
288
289 JSValue *Plugins::nameGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
290 {
291     AtomicString atomicPropertyName = propertyName;
292     Vector<PluginInfo*>::iterator end = plugins->end();
293     for (Vector<PluginInfo*>::iterator itr = plugins->begin(); itr != end; itr++) {
294         PluginInfo *pl = *itr;
295         if (pl->name == atomicPropertyName)
296             return new Plugin(exec, pl);
297     }
298     return jsUndefined();
299 }
300
301 bool Plugins::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
302 {
303     const HashEntry* entry = Lookup::findEntry(&PluginsTable, propertyName);
304     if (entry) {
305       if (entry->attr & Function)
306         slot.setStaticEntry(this, entry, staticFunctionGetter<PluginsFunc>);
307       else
308         slot.setStaticEntry(this, entry, staticValueGetter<Plugins>);
309       return true;
310     } else {
311         // plugins[#]
312         bool ok;
313         unsigned int i = propertyName.toUInt32(&ok);
314         if (ok && i < plugins->size()) {
315             slot.setCustomIndex(this, i, indexGetter);
316             return true;
317         }
318
319         // plugin[name]
320         AtomicString atomicPropertyName = propertyName;
321         Vector<PluginInfo*>::iterator end = plugins->end();
322         for (Vector<PluginInfo*>::iterator itr = plugins->begin(); itr != end; itr++) {
323             if ((*itr)->name == atomicPropertyName) {
324                 slot.setCustom(this, nameGetter);
325                 return true;
326             }
327         }
328     }
329
330     return PluginBase::getOwnPropertySlot(exec, propertyName, slot);
331 }
332
333 /*******************************************************************/
334
335 /*
336 @begin MimeTypesTable 1
337   length        MimeTypes::Length       DontDelete|ReadOnly
338 @end
339 */
340
341 JSValue *MimeTypes::getValueProperty(ExecState *exec, int token) const
342 {
343   assert(token == Length);
344   return jsNumber(plugins->size());
345 }
346
347 JSValue *MimeTypes::indexGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
348 {
349     return new MimeType(exec, mimes->at(slot.index()));
350 }
351
352 JSValue *MimeTypes::nameGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
353 {
354     AtomicString atomicPropertyName = propertyName;
355     Vector<MimeClassInfo*>::iterator end = mimes->end();
356     for (Vector<MimeClassInfo*>::iterator itr = mimes->begin(); itr != end; itr++) {
357         MimeClassInfo *m = (*itr);
358         if (m->type == atomicPropertyName)
359             return new MimeType(exec, m);
360     }
361     return jsUndefined();
362 }
363
364 bool MimeTypes::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
365 {
366     const HashEntry* entry = Lookup::findEntry(&MimeTypesTable, propertyName);
367     if (entry) {
368       slot.setStaticEntry(this, entry, staticValueGetter<Plugins>);
369       return true;
370     } else {
371         // mimeTypes[#]
372         bool ok;
373         unsigned int i = propertyName.toUInt32(&ok);
374         if (ok && i < mimes->size()) {
375             slot.setCustomIndex(this, i, indexGetter);
376             return true;
377         }
378
379         // mimeTypes[name]
380         AtomicString atomicPropertyName = propertyName;
381         Vector<MimeClassInfo*>::iterator end = mimes->end();
382         for (Vector<MimeClassInfo*>::iterator itr = mimes->begin(); itr != end; itr++) {
383             if ((*itr)->type == atomicPropertyName) {
384                 slot.setCustom(this, nameGetter);
385                 return true;
386             }
387         }
388     }
389
390     return PluginBase::getOwnPropertySlot(exec, propertyName, slot);
391 }
392
393
394 /************************************************************************/
395
396 /*
397 @begin PluginTable 4
398   name          Plugin::Name            DontDelete|ReadOnly
399   filename      Plugin::Filename        DontDelete|ReadOnly
400   description   Plugin::Description     DontDelete|ReadOnly
401   length        Plugin::Length          DontDelete|ReadOnly
402 @end
403 */
404
405 JSValue *Plugin::getValueProperty(ExecState *exec, int token) const
406 {
407     switch (token) {
408     case Name:
409         return jsString(m_info->name);
410     case Filename:
411         return jsString(m_info->file);
412     case Description:
413         return jsString(m_info->desc);
414     case Length: 
415         return jsNumber(m_info->mimes.size());
416     default:
417         assert(0);
418         return jsUndefined();
419     }
420 }
421
422 JSValue *Plugin::indexGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
423 {
424     Plugin *thisObj = static_cast<Plugin *>(slot.slotBase());
425     return new MimeType(exec, thisObj->m_info->mimes.at(slot.index()));
426 }
427
428 JSValue *Plugin::nameGetter(ExecState *exec, JSObject *originalObject, const Identifier& propertyName, const PropertySlot& slot)
429 {
430     Plugin *thisObj = static_cast<Plugin *>(slot.slotBase());
431     AtomicString atomicPropertyName = propertyName;
432     Vector<MimeClassInfo*>::iterator end = thisObj->m_info->mimes.end();
433     for (Vector<MimeClassInfo*>::iterator itr = thisObj->m_info->mimes.begin(); itr != end; itr++) {
434         MimeClassInfo *m = (*itr);
435         if (m->type == atomicPropertyName)
436             return new MimeType(exec, m);
437     }
438     return jsUndefined();
439 }
440
441
442 bool Plugin::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
443 {
444     const HashEntry* entry = Lookup::findEntry(&PluginTable, propertyName);
445     if (entry) {
446         slot.setStaticEntry(this, entry, staticValueGetter<Plugin>);
447         return true;
448     } else {
449         // plugin[#]
450         bool ok;
451         unsigned int i = propertyName.toUInt32(&ok);
452         if (ok && i < m_info->mimes.size()) {
453             slot.setCustomIndex(this, i, indexGetter);
454             return true;
455         }
456
457         // plugin["name"]
458         AtomicString atomicPropertyName = propertyName;
459         Vector<MimeClassInfo*>::iterator end = m_info->mimes.end();
460         for (Vector<MimeClassInfo*>::iterator itr = m_info->mimes.begin(); itr != end; itr++) {
461             if ((*itr)->type == atomicPropertyName) {
462                 slot.setCustom(this, nameGetter);
463                 return true;
464             }
465         }
466     }
467
468     return PluginBase::getOwnPropertySlot(exec, propertyName, slot);
469 }
470
471 /*****************************************************************************/
472
473 /*
474 @begin MimeTypeTable 4
475   type          MimeType::Type          DontDelete|ReadOnly
476   suffixes      MimeType::Suffixes      DontDelete|ReadOnly
477   description   MimeType::Description   DontDelete|ReadOnly
478   enabledPlugin MimeType::EnabledPlugin DontDelete|ReadOnly
479 @end
480 */
481
482 JSValue *MimeType::getValueProperty(ExecState *exec, int token) const
483 {
484     switch (token) {
485     case Type:
486         return jsString(m_info->type);
487     case Suffixes:
488         return jsString(m_info->suffixes);
489     case Description:
490         return jsString(m_info->desc);
491     case EnabledPlugin: {
492         ScriptInterpreter *interpreter = static_cast<ScriptInterpreter *>(exec->dynamicInterpreter());
493         Frame *frame = interpreter->frame();
494         if (frame && frame->pluginsEnabled())
495             return new Plugin(exec, m_info->plugin);
496         else
497             return jsUndefined();
498     }
499     default:
500         return jsUndefined();
501     }
502 }
503
504 bool MimeType::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
505 {
506     return getStaticValueSlot<MimeType, PluginBase>(exec, &MimeTypeTable, this, propertyName, slot);
507 }
508
509 JSValue *PluginsFunc::callAsFunction(ExecState *exec, JSObject *, const List &args)
510 {
511     PluginBase(exec).refresh(args[0]->toBoolean(exec));
512     return jsUndefined();
513 }
514
515 JSValue *NavigatorFunc::callAsFunction(ExecState *exec, JSObject *thisObj, const List &)
516 {
517   if (!thisObj->inherits(&KJS::Navigator::info))
518     return throwError(exec, TypeError);
519   Navigator *nav = static_cast<Navigator *>(thisObj);
520   // javaEnabled()
521   return jsBoolean(nav->frame()->javaEnabled());
522 }
523
524 } // namespace